Fail more softly on homeserver liveliness errors

This performs liveliness checks on the auth pages to try and show a friendlier error. Earlier checks in the app startup are expected to not block the app from loading on such failures.

See https://github.com/vector-im/riot-web/issues/9828
This commit is contained in:
Travis Ralston 2019-06-04 23:41:59 -06:00
parent b412103f21
commit e2fdeec71a
8 changed files with 257 additions and 33 deletions

View file

@ -15,10 +15,15 @@ limitations under the License.
*/
import {AutoDiscovery} from "matrix-js-sdk";
import {_td, newTranslatableError} from "../languageHandler";
import {_t, _td, newTranslatableError} from "../languageHandler";
import {makeType} from "./TypeUtils";
import SdkConfig from "../SdkConfig";
const LIVLINESS_DISCOVERY_ERRORS = [
AutoDiscovery.ERROR_INVALID_HOMESERVER,
AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER,
];
export class ValidatedServerConfig {
hsUrl: string;
hsName: string;
@ -31,7 +36,52 @@ export class ValidatedServerConfig {
}
export default class AutoDiscoveryUtils {
static async validateServerConfigWithStaticUrls(homeserverUrl: string, identityUrl: string): ValidatedServerConfig {
/**
* Checks if a given error or error message is considered an error
* relating to the liveliness of the server. Must be an error returned
* from this AutoDiscoveryUtils class.
* @param {string|Error} error The error to check
* @returns {boolean} True if the error is a liveliness error.
*/
static isLivelinessError(error: string|Error): boolean {
if (!error) return false;
return !!LIVLINESS_DISCOVERY_ERRORS.find(e => e === error || e === error.message);
}
/**
* Gets the common state for auth components (login, registration, forgot
* password) for a given validation error.
* @param {Error} err The error encountered.
* @returns {{serverDeadError: (string|*), serverIsAlive: boolean}} The state
* for the component, given the error.
*/
static authComponentStateForError(err: Error): {serverIsAlive: boolean, serverDeadError: string} {
if (AutoDiscoveryUtils.isLivelinessError(err)) {
// TODO: TravisR - Copy from Nad
return {
serverIsAlive: false,
serverDeadError: _t("Server failed liveliness check"),
};
} else {
// TODO: TravisR - Copy from Nad
return {
serverIsAlive: false,
serverDeadError: _t("Server failed syntax check"),
};
}
}
/**
* Validates a server configuration, using a pair of URLs as input.
* @param {string} homeserverUrl The homeserver URL.
* @param {string} identityUrl The identity server URL.
* @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will
* not be raised.
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
*/
static async validateServerConfigWithStaticUrls(
homeserverUrl: string, identityUrl: string, syntaxOnly = false): ValidatedServerConfig {
if (!homeserverUrl) {
throw newTranslatableError(_td("No homeserver URL provided"));
}
@ -50,15 +100,33 @@ export default class AutoDiscoveryUtils {
const url = new URL(homeserverUrl);
const serverName = url.hostname;
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result, syntaxOnly);
}
static async validateServerName(serverName: string): ValidatedServerConfig {
/**
* Validates a server configuration, using a homeserver domain name as input.
* @param {string} serverName The homeserver domain name (eg: "matrix.org") to validate.
* @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will
* not be raised.
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
*/
static async validateServerName(serverName: string, syntaxOnly=false): ValidatedServerConfig {
const result = await AutoDiscovery.findClientConfig(serverName);
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
}
static buildValidatedConfigFromDiscovery(serverName: string, discoveryResult): ValidatedServerConfig {
/**
* Validates a server configuration, using a pre-calculated AutoDiscovery result as
* input.
* @param {string} serverName The domain name the AutoDiscovery result is for.
* @param {*} discoveryResult The AutoDiscovery result.
* @param {boolean} syntaxOnly If true, errors relating to liveliness of the servers will
* not be raised.
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
*/
static buildValidatedConfigFromDiscovery(
serverName: string, discoveryResult, syntaxOnly=false): ValidatedServerConfig {
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
@ -68,19 +136,27 @@ export default class AutoDiscoveryUtils {
const hsResult = discoveryResult['m.homeserver'];
if (hsResult.state !== AutoDiscovery.SUCCESS) {
if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error) !== -1) {
throw newTranslatableError(hsResult.error);
}
throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
console.error("Error processing homeserver config:", hsResult);
if (!syntaxOnly || !AutoDiscoveryUtils.isLivelinessError(hsResult.error)) {
if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error) !== -1) {
throw newTranslatableError(hsResult.error);
}
throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
} // else the error is not related to syntax - continue anyways.
}
const isResult = discoveryResult['m.identity_server'];
let preferredIdentityUrl = "https://vector.im";
let preferredIdentityUrl = "https://vector.im"; // We already know this is an IS, so don't validate it.
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
preferredIdentityUrl = isResult["base_url"];
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
console.error("Error determining preferred identity server URL:", isResult);
throw newTranslatableError(_td("Unexpected error resolving homeserver configuration"));
if (!syntaxOnly || !AutoDiscoveryUtils.isLivelinessError(isResult.error)) {
if (AutoDiscovery.ALL_ERRORS.indexOf(isResult.error) !== -1) {
throw newTranslatableError(isResult.error);
}
throw newTranslatableError(_td("Unexpected error resolving identity server configuration"));
} // else the error is not related to syntax - continue anyways.
}
const preferredHomeserverUrl = hsResult["base_url"];