Merge pull request #3067 from matrix-org/travis/fail-fast-but-not-too-fast

Fail more softly on homeserver liveliness errors
This commit is contained in:
Travis Ralston 2019-06-07 07:43:45 -06:00 committed by GitHub
commit 795a273e26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 272 additions and 41 deletions

View file

@ -16,22 +16,18 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import {_t} from "../../languageHandler";
export default class GenericErrorPage extends React.PureComponent {
static propTypes = {
title: PropTypes.object.isRequired, // jsx for title
message: PropTypes.object.isRequired, // jsx to display
};
render() {
return <div className='mx_GenericErrorPage'>
<div className='mx_GenericErrorPage_box'>
<h1>{_t("Error loading Riot")}</h1>
<h1>{this.props.title}</h1>
<p>{this.props.message}</p>
<p>{_t(
"If this is unexpected, please contact your system administrator " +
"or technical support representative.",
)}</p>
</div>
</div>;
}

View file

@ -22,7 +22,7 @@ import sdk from '../../../index';
import Modal from "../../../Modal";
import SdkConfig from "../../../SdkConfig";
import PasswordReset from "../../../PasswordReset";
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
// Phases
// Show controls to configure server details
@ -53,9 +53,40 @@ module.exports = React.createClass({
password: "",
password2: "",
errorText: null,
// We perform liveliness checks later, but for now suppress the errors.
// We also track the server dead errors independently of the regular errors so
// that we can render it differently, and override any other error the user may
// be seeing.
serverIsAlive: true,
serverDeadError: "",
};
},
componentWillMount: function() {
this._checkServerLiveliness(this.props.serverConfig);
},
componentWillReceiveProps: function(newProps) {
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
// Do a liveliness check on the new URLs
this._checkServerLiveliness(newProps.serverConfig);
},
_checkServerLiveliness: async function(serverConfig) {
try {
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
serverConfig.hsUrl,
serverConfig.isUrl,
);
this.setState({serverIsAlive: true});
} catch (e) {
this.setState(AutoDiscoveryUtils.authComponentStateForError(e));
}
},
submitPasswordReset: function(email, password) {
this.setState({
phase: PHASE_SENDING_EMAIL,
@ -86,9 +117,11 @@ module.exports = React.createClass({
});
},
onSubmitForm: function(ev) {
onSubmitForm: async function(ev) {
ev.preventDefault();
await this._checkServerLiveliness(this.props.serverConfig);
if (!this.state.email) {
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
} else if (!this.state.password || !this.state.password2) {
@ -173,11 +206,20 @@ module.exports = React.createClass({
const Field = sdk.getComponent('elements.Field');
let errorText = null;
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
const err = this.state.errorText;
if (err) {
errorText = <div className="mx_Login_error">{ err }</div>;
}
let serverDeadSection;
if (!this.state.serverIsAlive) {
serverDeadSection = (
<div className="mx_Login_error mx_Login_serverError">
{this.state.serverDeadError}
</div>
);
}
let yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
serverName: this.props.serverConfig.hsName,
});
@ -207,11 +249,12 @@ module.exports = React.createClass({
}
return <div>
{errorText}
{serverDeadSection}
<h3>
{yourMatrixAccountText}
{editLink}
</h3>
{errorText}
<form onSubmit={this.onSubmitForm}>
<div className="mx_AuthBody_fieldRow">
<Field
@ -246,7 +289,11 @@ module.exports = React.createClass({
'A verification email will be sent to your inbox to confirm ' +
'setting your new password.',
)}</span>
<input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
<input
className="mx_Login_submit"
type="submit"
value={_t('Send Reset Email')}
/>
</form>
<a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
{_t('Sign in instead')}

View file

@ -94,6 +94,13 @@ module.exports = React.createClass({
phase: PHASE_LOGIN,
// The current login flow, such as password, SSO, etc.
currentFlow: "m.login.password",
// We perform liveliness checks later, but for now suppress the errors.
// We also track the server dead errors independently of the regular errors so
// that we can render it differently, and override any other error the user may
// be seeing.
serverIsAlive: true,
serverDeadError: "",
};
},
@ -138,7 +145,7 @@ module.exports = React.createClass({
onPasswordLogin: function(username, phoneCountry, phoneNumber, password) {
// Prevent people from submitting their password when something isn't right.
if (this.isBusy() || !this.state.canTryLogin) return;
if (this.isBusy()) return;
this.setState({
busy: true,
@ -149,6 +156,7 @@ module.exports = React.createClass({
this._loginLogic.loginViaPassword(
username, phoneCountry, phoneNumber, password,
).then((data) => {
this.setState({serverIsAlive: true}); // it must be, we logged in.
this.props.onLoggedIn(data);
}, (error) => {
if (this._unmounted) {
@ -247,7 +255,19 @@ module.exports = React.createClass({
if (e.translatedMessage) {
message = e.translatedMessage;
}
this.setState({errorText: message, busy: false, canTryLogin: false});
let errorText = message;
let discoveryState = {};
if (AutoDiscoveryUtils.isLivelinessError(e)) {
errorText = this.state.errorText;
discoveryState = AutoDiscoveryUtils.authComponentStateForError(e);
}
this.setState({
busy: false,
errorText,
...discoveryState,
});
}
}
},
@ -297,13 +317,18 @@ module.exports = React.createClass({
});
},
_initLoginLogic: function(hsUrl, isUrl) {
const self = this;
_initLoginLogic: async function(hsUrl, isUrl) {
hsUrl = hsUrl || this.props.serverConfig.hsUrl;
isUrl = isUrl || this.props.serverConfig.isUrl;
// TODO: TravisR - Only use this if the homeserver is the default homeserver
const fallbackHsUrl = this.props.fallbackHsUrl;
let isDefaultServer = false;
if (this.props.serverConfig.isDefault
&& hsUrl === this.props.serverConfig.hsUrl
&& isUrl === this.props.serverConfig.isUrl) {
isDefaultServer = true;
}
const fallbackHsUrl = isDefaultServer ? this.props.fallbackHsUrl : null;
const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
@ -315,6 +340,18 @@ module.exports = React.createClass({
loginIncorrect: false,
});
// Do a quick liveliness check on the URLs
try {
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, isUrl);
this.setState({serverIsAlive: true, errorText: ""});
} catch (e) {
this.setState({
busy: false,
...AutoDiscoveryUtils.authComponentStateForError(e),
});
return; // Server is dead - do not continue.
}
loginLogic.getFlows().then((flows) => {
// look for a flow where we understand all of the steps.
for (let i = 0; i < flows.length; i++ ) {
@ -339,14 +376,14 @@ module.exports = React.createClass({
"supported by this client.",
),
});
}, function(err) {
self.setState({
errorText: self._errorTextFromError(err),
}, (err) => {
this.setState({
errorText: this._errorTextFromError(err),
loginIncorrect: false,
canTryLogin: false,
});
}).finally(function() {
self.setState({
}).finally(() => {
this.setState({
busy: false,
});
}).done();
@ -522,6 +559,15 @@ module.exports = React.createClass({
);
}
let serverDeadSection;
if (!this.state.serverIsAlive) {
serverDeadSection = (
<div className="mx_Login_error mx_Login_serverError">
{this.state.serverDeadError}
</div>
);
}
return (
<AuthPage>
<AuthHeader />
@ -531,6 +577,7 @@ module.exports = React.createClass({
{loader}
</h2>
{ errorTextSection }
{ serverDeadSection }
{ this.renderServerComponent() }
{ this.renderLoginComponentForStep() }
<a className="mx_AuthBody_changeFlow" onClick={this.onRegisterClick} href="#">

View file

@ -26,7 +26,7 @@ import { _t, _td } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig';
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
import * as ServerType from '../../views/auth/ServerTypeSelector';
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
// Phases
// Show controls to configure server details
@ -79,6 +79,13 @@ module.exports = React.createClass({
// Phase of the overall registration dialog.
phase: PHASE_REGISTRATION,
flows: null,
// We perform liveliness checks later, but for now suppress the errors.
// We also track the server dead errors independently of the regular errors so
// that we can render it differently, and override any other error the user may
// be seeing.
serverIsAlive: true,
serverDeadError: "",
};
},
@ -152,6 +159,19 @@ module.exports = React.createClass({
errorText: null,
});
if (!serverConfig) serverConfig = this.props.serverConfig;
// Do a liveliness check on the URLs
try {
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
serverConfig.hsUrl,
serverConfig.isUrl,
);
this.setState({serverIsAlive: true});
} catch (e) {
this.setState(AutoDiscoveryUtils.authComponentStateForError(e));
return; // Server is dead - do not continue.
}
const {hsUrl, isUrl} = serverConfig;
this._matrixClient = Matrix.createClient({
baseUrl: hsUrl,
@ -447,6 +467,7 @@ module.exports = React.createClass({
onEditServerDetailsClick={onEditServerDetailsClick}
flows={this.state.flows}
serverConfig={this.props.serverConfig}
canSubmit={this.state.serverIsAlive}
/>;
}
},
@ -462,6 +483,15 @@ module.exports = React.createClass({
errorText = <div className="mx_Login_error">{ err }</div>;
}
let serverDeadSection;
if (!this.state.serverIsAlive) {
serverDeadSection = (
<div className="mx_Login_error mx_Login_serverError">
{this.state.serverDeadError}
</div>
);
}
const signIn = <a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
{ _t('Sign in instead') }
</a>;
@ -480,6 +510,7 @@ module.exports = React.createClass({
<AuthBody>
<h2>{ _t('Create your account') }</h2>
{ errorText }
{ serverDeadSection }
{ this.renderServerComponent() }
{ this.renderRegisterComponent() }
{ goBack }