Merge pull request #2941 from matrix-org/travis/guests/block-ui

Use validated server config for login, registration, and password reset
This commit is contained in:
Travis Ralston 2019-05-10 11:21:21 -06:00 committed by GitHub
commit 6a941aca91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 509 additions and 560 deletions

View file

@ -50,8 +50,7 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
import { startAnyRegistrationFlow } from "../../Registration.js";
import { messageForSyncError } from '../../utils/ErrorUtils';
import ResizeNotifier from "../../utils/ResizeNotifier";
const AutoDiscovery = Matrix.AutoDiscovery;
import {ValidatedServerConfig} from "../../utils/AutoDiscoveryUtils";
// Disable warnings for now: we use deprecated bluebird functions
// and need to migrate, but they spam the console with warnings.
@ -109,6 +108,7 @@ export default React.createClass({
propTypes: {
config: PropTypes.object,
serverConfig: PropTypes.instanceOf(ValidatedServerConfig),
ConferenceHandler: PropTypes.any,
onNewScreen: PropTypes.func,
registrationUrl: PropTypes.string,
@ -181,16 +181,8 @@ export default React.createClass({
// Parameters used in the registration dance with the IS
register_client_secret: null,
register_session_id: null,
register_hs_url: null,
register_is_url: null,
register_id_sid: null,
// Parameters used for setting up the authentication views
defaultServerName: this.props.config.default_server_name,
defaultHsUrl: this.props.config.default_hs_url,
defaultIsUrl: this.props.config.default_is_url,
defaultServerDiscoveryError: null,
// When showing Modal dialogs we need to set aria-hidden on the root app element
// and disable it when there are no dialogs
hideToSRUsers: false,
@ -211,42 +203,16 @@ export default React.createClass({
};
},
getDefaultServerName: function() {
return this.state.defaultServerName;
},
getCurrentHsUrl: function() {
if (this.state.register_hs_url) {
return this.state.register_hs_url;
} else if (MatrixClientPeg.get()) {
return MatrixClientPeg.get().getHomeserverUrl();
} else {
return this.getDefaultHsUrl();
}
},
getDefaultHsUrl(defaultToMatrixDotOrg) {
defaultToMatrixDotOrg = typeof(defaultToMatrixDotOrg) !== 'boolean' ? true : defaultToMatrixDotOrg;
if (!this.state.defaultHsUrl && defaultToMatrixDotOrg) return "https://matrix.org";
return this.state.defaultHsUrl;
},
// TODO: TravisR - Remove this or put it somewhere else
getFallbackHsUrl: function() {
return this.props.config.fallback_hs_url;
},
getCurrentIsUrl: function() {
if (this.state.register_is_url) {
return this.state.register_is_url;
} else if (MatrixClientPeg.get()) {
return MatrixClientPeg.get().getIdentityServerUrl();
} else {
return this.getDefaultIsUrl();
}
},
getDefaultIsUrl() {
return this.state.defaultIsUrl || "https://vector.im";
getServerProperties() {
let props = this.state.serverConfig;
if (!props) props = this.props.serverConfig; // for unit tests
if (!props) props = SdkConfig.get()["validated_server_config"];
return {serverConfig: props};
},
componentWillMount: function() {
@ -260,40 +226,6 @@ export default React.createClass({
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
}
// Set up the default URLs (async)
if (this.getDefaultServerName() && !this.getDefaultHsUrl(false)) {
this.setState({loadingDefaultHomeserver: true});
this._tryDiscoverDefaultHomeserver(this.getDefaultServerName());
} else if (this.getDefaultServerName() && this.getDefaultHsUrl(false)) {
// Ideally we would somehow only communicate this to the server admins, but
// given this is at login time we can't really do much besides hope that people
// will check their settings.
this.setState({
defaultServerName: null, // To un-hide any secrets people might be keeping
defaultServerDiscoveryError: _t(
"Invalid configuration: Cannot supply a default homeserver URL and " +
"a default server name",
),
});
}
// Set a default HS with query param `hs_url`
const paramHs = this.props.startingFragmentQueryParams.hs_url;
if (paramHs) {
console.log('Setting register_hs_url ', paramHs);
this.setState({
register_hs_url: paramHs,
});
}
// Set a default IS with query param `is_url`
const paramIs = this.props.startingFragmentQueryParams.is_url;
if (paramIs) {
console.log('Setting register_is_url ', paramIs);
this.setState({
register_is_url: paramIs,
});
}
// a thing to call showScreen with once login completes. this is kept
// outside this.state because updating it should never trigger a
// rerender.
@ -374,8 +306,8 @@ export default React.createClass({
return Lifecycle.loadSession({
fragmentQueryParams: this.props.startingFragmentQueryParams,
enableGuest: this.props.enableGuest,
guestHsUrl: this.getCurrentHsUrl(),
guestIsUrl: this.getCurrentIsUrl(),
guestHsUrl: this.getServerProperties().serverConfig.hsUrl,
guestIsUrl: this.getServerProperties().serverConfig.isUrl,
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
});
}).then((loadedSession) => {
@ -1823,44 +1755,7 @@ export default React.createClass({
},
onServerConfigChange(config) {
const newState = {};
if (config.hsUrl) {
newState.register_hs_url = config.hsUrl;
}
if (config.isUrl) {
newState.register_is_url = config.isUrl;
}
this.setState(newState);
},
_tryDiscoverDefaultHomeserver: async function(serverName) {
try {
const discovery = await AutoDiscovery.findClientConfig(serverName);
const state = discovery["m.homeserver"].state;
if (state !== AutoDiscovery.SUCCESS) {
console.error("Failed to discover homeserver on startup:", discovery);
this.setState({
defaultServerDiscoveryError: discovery["m.homeserver"].error,
loadingDefaultHomeserver: false,
});
} else {
const hsUrl = discovery["m.homeserver"].base_url;
const isUrl = discovery["m.identity_server"].state === AutoDiscovery.SUCCESS
? discovery["m.identity_server"].base_url
: "https://vector.im";
this.setState({
defaultHsUrl: hsUrl,
defaultIsUrl: isUrl,
loadingDefaultHomeserver: false,
});
}
} catch (e) {
console.error(e);
this.setState({
defaultServerDiscoveryError: _t("Unknown error discovering homeserver"),
loadingDefaultHomeserver: false,
});
}
this.setState({serverConfig: config});
},
_makeRegistrationUrl: function(params) {
@ -1879,8 +1774,7 @@ export default React.createClass({
if (
this.state.view === VIEWS.LOADING ||
this.state.view === VIEWS.LOGGING_IN ||
this.state.loadingDefaultHomeserver
this.state.view === VIEWS.LOGGING_IN
) {
const Spinner = sdk.getComponent('elements.Spinner');
return (
@ -1958,18 +1852,13 @@ export default React.createClass({
sessionId={this.state.register_session_id}
idSid={this.state.register_id_sid}
email={this.props.startingFragmentQueryParams.email}
defaultServerName={this.getDefaultServerName()}
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()}
brand={this.props.config.brand}
customHsUrl={this.getCurrentHsUrl()}
customIsUrl={this.getCurrentIsUrl()}
makeRegistrationUrl={this._makeRegistrationUrl}
onLoggedIn={this.onRegistered}
onLoginClick={this.onLoginClick}
onServerConfigChange={this.onServerConfigChange}
/>
{...this.getServerProperties()}
/>
);
}
@ -1978,14 +1867,11 @@ export default React.createClass({
const ForgotPassword = sdk.getComponent('structures.auth.ForgotPassword');
return (
<ForgotPassword
defaultServerName={this.getDefaultServerName()}
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()}
customHsUrl={this.getCurrentHsUrl()}
customIsUrl={this.getCurrentIsUrl()}
onComplete={this.onLoginClick}
onLoginClick={this.onLoginClick} />
onLoginClick={this.onLoginClick}
onServerConfigChange={this.onServerConfigChange}
{...this.getServerProperties()}
/>
);
}
@ -1995,16 +1881,11 @@ export default React.createClass({
<Login
onLoggedIn={Lifecycle.setLoggedIn}
onRegisterClick={this.onRegisterClick}
defaultServerName={this.getDefaultServerName()}
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()}
customHsUrl={this.getCurrentHsUrl()}
customIsUrl={this.getCurrentIsUrl()}
fallbackHsUrl={this.getFallbackHsUrl()}
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
onForgotPasswordClick={this.onForgotPasswordClick}
onServerConfigChange={this.onServerConfigChange}
{...this.getServerProperties()}
/>
);
}

View file

@ -21,8 +21,8 @@ import { _t } from '../../../languageHandler';
import sdk from '../../../index';
import Modal from "../../../Modal";
import SdkConfig from "../../../SdkConfig";
import PasswordReset from "../../../PasswordReset";
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
// Phases
// Show controls to configure server details
@ -40,28 +40,14 @@ module.exports = React.createClass({
displayName: 'ForgotPassword',
propTypes: {
// The default server name to use when the user hasn't specified
// one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this
// via `.well-known` discovery. The server name is used instead of the
// HS URL when talking about "your account".
defaultServerName: PropTypes.string,
// An error passed along from higher up explaining that something
// went wrong when finding the defaultHsUrl.
defaultServerDiscoveryError: PropTypes.string,
defaultHsUrl: PropTypes.string,
defaultIsUrl: PropTypes.string,
customHsUrl: PropTypes.string,
customIsUrl: PropTypes.string,
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
onServerConfigChange: PropTypes.func.isRequired,
onLoginClick: PropTypes.func,
onComplete: PropTypes.func.isRequired,
},
getInitialState: function() {
return {
enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl,
enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl,
phase: PHASE_FORGOT,
email: "",
password: "",
@ -70,11 +56,11 @@ module.exports = React.createClass({
};
},
submitPasswordReset: function(hsUrl, identityUrl, email, password) {
submitPasswordReset: function(email, password) {
this.setState({
phase: PHASE_SENDING_EMAIL,
});
this.reset = new PasswordReset(hsUrl, identityUrl);
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
this.reset.resetPassword(email, password).done(() => {
this.setState({
phase: PHASE_EMAIL_SENT,
@ -103,13 +89,6 @@ module.exports = React.createClass({
onSubmitForm: function(ev) {
ev.preventDefault();
// Don't allow the user to register if there's a discovery error
// Without this, the user could end up registering on the wrong homeserver.
if (this.props.defaultServerDiscoveryError) {
this.setState({errorText: this.props.defaultServerDiscoveryError});
return;
}
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) {
@ -132,10 +111,7 @@ module.exports = React.createClass({
button: _t('Continue'),
onFinished: (confirmed) => {
if (confirmed) {
this.submitPasswordReset(
this.state.enteredHsUrl, this.state.enteredIsUrl,
this.state.email, this.state.password,
);
this.submitPasswordReset(this.state.email, this.state.password);
}
},
});
@ -148,19 +124,13 @@ module.exports = React.createClass({
});
},
onServerConfigChange: function(config) {
const newState = {};
if (config.hsUrl !== undefined) {
newState.enteredHsUrl = config.hsUrl;
}
if (config.isUrl !== undefined) {
newState.enteredIsUrl = config.isUrl;
}
this.setState(newState);
},
onServerDetailsNextPhaseClick(ev) {
async onServerDetailsNextPhaseClick(ev) {
ev.stopPropagation();
// TODO: TravisR - Capture the user's input somehow else
if (this._serverConfigRef) {
// Just to make sure the user's input gets captured
await this._serverConfigRef.validateServer();
}
this.setState({
phase: PHASE_FORGOT,
});
@ -196,13 +166,12 @@ module.exports = React.createClass({
return null;
}
// TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button?
return <div>
<ServerConfig
defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl}
customHsUrl={this.state.enteredHsUrl}
customIsUrl={this.state.enteredIsUrl}
onServerConfigChange={this.onServerConfigChange}
ref={r => this._serverConfigRef = r}
serverConfig={this.props.serverConfig}
onServerConfigChange={this.props.onServerConfigChange}
delayTimeMs={0} />
<AccessibleButton className="mx_Login_submit"
onClick={this.onServerDetailsNextPhaseClick}
@ -221,25 +190,14 @@ module.exports = React.createClass({
errorText = <div className="mx_Login_error">{ err }</div>;
}
let yourMatrixAccountText = _t('Your Matrix account');
if (this.state.enteredHsUrl === this.props.defaultHsUrl && this.props.defaultServerName) {
yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
serverName: this.props.defaultServerName,
let yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
serverName: this.props.serverConfig.hsName,
});
if (this.props.serverConfig.hsNameIsDifferent) {
// TODO: TravisR - Use tooltip to underline
yourMatrixAccountText = _t('Your Matrix account on <underlinedServerName />', {}, {
'underlinedServerName': () => <u>{this.props.serverConfig.hsName}</u>,
});
} else {
try {
const parsedHsUrl = new URL(this.state.enteredHsUrl);
yourMatrixAccountText = _t('Your Matrix account on %(serverName)s', {
serverName: parsedHsUrl.hostname,
});
} catch (e) {
errorText = <div className="mx_Login_error">{_t(
"The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please " +
"enter a valid URL including the protocol prefix.",
{
hsUrl: this.state.enteredHsUrl,
})}</div>;
}
}
// If custom URLs are allowed, wire up the server details edit link.

View file

@ -25,7 +25,7 @@ import sdk from '../../../index';
import Login from '../../../Login';
import SdkConfig from '../../../SdkConfig';
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
import { AutoDiscovery } from "matrix-js-sdk";
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
// For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
@ -59,19 +59,14 @@ module.exports = React.createClass({
propTypes: {
onLoggedIn: PropTypes.func.isRequired,
// The default server name to use when the user hasn't specified
// one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this
// via `.well-known` discovery. The server name is used instead of the
// HS URL when talking about where to "sign in to".
defaultServerName: PropTypes.string,
// An error passed along from higher up explaining that something
// went wrong when finding the defaultHsUrl.
defaultServerDiscoveryError: PropTypes.string,
// went wrong. May be replaced with a different error within the
// Login component.
errorText: PropTypes.string,
// If true, the component will consider itself busy.
busy: PropTypes.bool,
customHsUrl: PropTypes.string,
customIsUrl: PropTypes.string,
defaultHsUrl: PropTypes.string,
defaultIsUrl: PropTypes.string,
// Secondary HS which we try to log into if the user is using
// the default HS but login fails. Useful for migrating to a
// different homeserver without confusing users.
@ -79,12 +74,13 @@ module.exports = React.createClass({
defaultDeviceDisplayName: PropTypes.string,
// login shouldn't know or care how registration is done.
// login shouldn't know or care how registration, password recovery,
// etc is done.
onRegisterClick: PropTypes.func.isRequired,
// login shouldn't care how password recovery is done.
onForgotPasswordClick: PropTypes.func,
onServerConfigChange: PropTypes.func.isRequired,
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
},
getInitialState: function() {
@ -93,9 +89,6 @@ module.exports = React.createClass({
errorText: null,
loginIncorrect: false,
enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl,
enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl,
// used for preserving form values when changing homeserver
username: "",
phoneCountry: null,
@ -105,10 +98,6 @@ module.exports = React.createClass({
phase: PHASE_LOGIN,
// The current login flow, such as password, SSO, etc.
currentFlow: "m.login.password",
// .well-known discovery
discoveryError: "",
findingHomeserver: false,
};
},
@ -132,6 +121,14 @@ module.exports = React.createClass({
this._unmounted = true;
},
componentWillReceiveProps(newProps) {
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
// Ensure that we end up actually logging in to the right place
this._initLoginLogic(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl);
},
onPasswordLoginError: function(errorText) {
this.setState({
errorText,
@ -139,10 +136,17 @@ module.exports = React.createClass({
});
},
isBusy: function() {
return this.state.busy || this.props.busy;
},
hasError: function() {
return this.state.errorText || this.props.errorText;
},
onPasswordLogin: function(username, phoneCountry, phoneNumber, password) {
// Prevent people from submitting their password when homeserver
// discovery went wrong
if (this.state.discoveryError || this.props.defaultServerDiscoveryError) return;
// Prevent people from submitting their password when something isn't right.
if (this.isBusy() || this.hasError()) return;
this.setState({
busy: true,
@ -164,7 +168,7 @@ module.exports = React.createClass({
const usingEmail = username.indexOf("@") > 0;
if (error.httpStatus === 400 && usingEmail) {
errorText = _t('This homeserver does not support login using email address.');
} else if (error.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') {
} else if (error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') {
const errorTop = messageForResourceLimitError(
error.data.limit_type,
error.data.admin_contact, {
@ -194,11 +198,10 @@ module.exports = React.createClass({
<div>
<div>{ _t('Incorrect username and/or password.') }</div>
<div className="mx_Login_smallError">
{ _t('Please note you are logging into the %(hs)s server, not matrix.org.',
{
hs: this.props.defaultHsUrl.replace(/^https?:\/\//, ''),
})
}
{_t(
'Please note you are logging into the %(hs)s server, not matrix.org.',
{hs: this.props.serverConfig.hsName},
)}
</div>
</div>
);
@ -235,9 +238,9 @@ module.exports = React.createClass({
onUsernameBlur: function(username) {
this.setState({
username: username,
discoveryError: null,
errorText: null,
});
if (username[0] === "@") {
if (username[0] === "@" && false) { // TODO: TravisR - Restore this
const serverName = username.split(':').slice(1).join(':');
try {
// we have to append 'https://' to make the URL constructor happy
@ -246,7 +249,7 @@ module.exports = React.createClass({
this._tryWellKnownDiscovery(url.hostname);
} catch (e) {
console.error("Problem parsing URL or unhandled error doing .well-known discovery:", e);
this.setState({discoveryError: _t("Failed to perform homeserver discovery")});
this.setState({errorText: _t("Failed to perform homeserver discovery")});
}
}
},
@ -274,32 +277,19 @@ module.exports = React.createClass({
}
},
onServerConfigChange: function(config) {
const self = this;
const newState = {
errorText: null, // reset err messages
};
if (config.hsUrl !== undefined) {
newState.enteredHsUrl = config.hsUrl;
}
if (config.isUrl !== undefined) {
newState.enteredIsUrl = config.isUrl;
}
this.props.onServerConfigChange(config);
this.setState(newState, function() {
self._initLoginLogic(config.hsUrl || null, config.isUrl);
});
},
onRegisterClick: function(ev) {
ev.preventDefault();
ev.stopPropagation();
this.props.onRegisterClick();
},
onServerDetailsNextPhaseClick(ev) {
async onServerDetailsNextPhaseClick(ev) {
ev.stopPropagation();
// TODO: TravisR - Capture the user's input somehow else
if (this._serverConfigRef) {
// Just to make sure the user's input gets captured
await this._serverConfigRef.validateServer();
}
this.setState({
phase: PHASE_LOGIN,
});
@ -313,64 +303,13 @@ module.exports = React.createClass({
});
},
_tryWellKnownDiscovery: async function(serverName) {
if (!serverName.trim()) {
// Nothing to discover
this.setState({
discoveryError: "",
findingHomeserver: false,
});
return;
}
this.setState({findingHomeserver: true});
try {
const discovery = await AutoDiscovery.findClientConfig(serverName);
const state = discovery["m.homeserver"].state;
if (state !== AutoDiscovery.SUCCESS && state !== AutoDiscovery.PROMPT) {
this.setState({
discoveryError: discovery["m.homeserver"].error,
findingHomeserver: false,
});
} else if (state === AutoDiscovery.PROMPT) {
this.setState({
discoveryError: "",
findingHomeserver: false,
});
} else if (state === AutoDiscovery.SUCCESS) {
this.setState({
discoveryError: "",
findingHomeserver: false,
});
this.onServerConfigChange({
hsUrl: discovery["m.homeserver"].base_url,
isUrl: discovery["m.identity_server"].state === AutoDiscovery.SUCCESS
? discovery["m.identity_server"].base_url
: "",
});
} else {
console.warn("Unknown state for m.homeserver in discovery response: ", discovery);
this.setState({
discoveryError: _t("Unknown failure discovering homeserver"),
findingHomeserver: false,
});
}
} catch (e) {
console.error(e);
this.setState({
findingHomeserver: false,
discoveryError: _t("Unknown error discovering homeserver"),
});
}
},
_initLoginLogic: function(hsUrl, isUrl) {
const self = this;
hsUrl = hsUrl || this.state.enteredHsUrl;
isUrl = isUrl || this.state.enteredIsUrl;
hsUrl = hsUrl || this.props.serverConfig.hsUrl;
isUrl = isUrl || this.props.serverConfig.isUrl;
const fallbackHsUrl = hsUrl === this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
// TODO: TravisR - Only use this if the homeserver is the default homeserver
const fallbackHsUrl = this.props.fallbackHsUrl;
const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
@ -378,8 +317,6 @@ module.exports = React.createClass({
this._loginLogic = loginLogic;
this.setState({
enteredHsUrl: hsUrl,
enteredIsUrl: isUrl,
busy: true,
loginIncorrect: false,
});
@ -445,8 +382,8 @@ module.exports = React.createClass({
if (err.cors === 'rejected') {
if (window.location.protocol === 'https:' &&
(this.state.enteredHsUrl.startsWith("http:") ||
!this.state.enteredHsUrl.startsWith("http"))
(this.props.serverConfig.hsUrl.startsWith("http:") ||
!this.props.serverConfig.hsUrl.startsWith("http"))
) {
errorText = <span>
{ _t("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " +
@ -469,9 +406,9 @@ module.exports = React.createClass({
"is not blocking requests.", {},
{
'a': (sub) => {
return <a target="_blank" rel="noopener"
href={this.state.enteredHsUrl}
>{ sub }</a>;
return <a target="_blank" rel="noopener" href={this.props.serverConfig.hsUrl}>
{ sub }
</a>;
},
},
) }
@ -495,19 +432,17 @@ module.exports = React.createClass({
}
const serverDetails = <ServerConfig
customHsUrl={this.state.enteredHsUrl}
customIsUrl={this.state.enteredIsUrl}
defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl}
onServerConfigChange={this.onServerConfigChange}
ref={r => this._serverConfigRef = r}
serverConfig={this.props.serverConfig}
onServerConfigChange={this.props.onServerConfigChange}
delayTimeMs={250}
/>;
let nextButton = null;
if (PHASES_ENABLED) {
// TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button?
nextButton = <AccessibleButton className="mx_Login_submit"
onClick={this.onServerDetailsNextPhaseClick}
>
onClick={this.onServerDetailsNextPhaseClick}>
{_t("Next")}
</AccessibleButton>;
}
@ -547,13 +482,6 @@ module.exports = React.createClass({
onEditServerDetailsClick = this.onEditServerDetailsClick;
}
// If the current HS URL is the default HS URL, then we can label it
// with the default HS name (if it exists).
let hsName;
if (this.state.enteredHsUrl === this.props.defaultHsUrl) {
hsName = this.props.defaultServerName;
}
return (
<PasswordLogin
onSubmit={this.onPasswordLogin}
@ -569,10 +497,9 @@ module.exports = React.createClass({
onPhoneNumberBlur={this.onPhoneNumberBlur}
onForgotPasswordClick={this.props.onForgotPasswordClick}
loginIncorrect={this.state.loginIncorrect}
hsName={hsName}
hsUrl={this.state.enteredHsUrl}
disableSubmit={this.state.findingHomeserver}
/>
serverConfig={this.props.serverConfig}
disableSubmit={this.isBusy()}
/>
);
},
@ -595,9 +522,9 @@ module.exports = React.createClass({
const AuthPage = sdk.getComponent("auth.AuthPage");
const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody");
const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
const loader = this.isBusy() ? <div className="mx_Login_loader"><Loader /></div> : null;
const errorText = this.props.defaultServerDiscoveryError || this.state.discoveryError || this.state.errorText;
const errorText = this.state.errorText || this.props.errorText;
let errorTextSection;
if (errorText) {

View file

@ -17,16 +17,15 @@ limitations under the License.
*/
import Matrix from 'matrix-js-sdk';
import Promise from 'bluebird';
import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
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";
// Phases
// Show controls to configure server details
@ -46,18 +45,7 @@ module.exports = React.createClass({
sessionId: PropTypes.string,
makeRegistrationUrl: PropTypes.func.isRequired,
idSid: PropTypes.string,
// The default server name to use when the user hasn't specified
// one. If set, `defaultHsUrl` and `defaultHsUrl` were derived for this
// via `.well-known` discovery. The server name is used instead of the
// HS URL when talking about "your account".
defaultServerName: PropTypes.string,
// An error passed along from higher up explaining that something
// went wrong when finding the defaultHsUrl.
defaultServerDiscoveryError: PropTypes.string,
customHsUrl: PropTypes.string,
customIsUrl: PropTypes.string,
defaultHsUrl: PropTypes.string,
defaultIsUrl: PropTypes.string,
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
brand: PropTypes.string,
email: PropTypes.string,
// registration shouldn't know or care how login is done.
@ -66,7 +54,7 @@ module.exports = React.createClass({
},
getInitialState: function() {
const serverType = ServerType.getTypeFromHsUrl(this.props.customHsUrl);
const serverType = ServerType.getTypeFromServerConfig(this.props.serverConfig);
return {
busy: false,
@ -87,8 +75,6 @@ module.exports = React.createClass({
// straight back into UI auth
doingUIAuth: Boolean(this.props.sessionId),
serverType,
hsUrl: this.props.customHsUrl,
isUrl: this.props.customIsUrl,
// Phase of the overall registration dialog.
phase: PHASE_REGISTRATION,
flows: null,
@ -100,18 +86,22 @@ module.exports = React.createClass({
this._replaceClient();
},
onServerConfigChange: function(config) {
const newState = {};
if (config.hsUrl !== undefined) {
newState.hsUrl = config.hsUrl;
componentWillReceiveProps(newProps) {
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
this._replaceClient(newProps.serverConfig);
// Handle cases where the user enters "https://matrix.org" for their server
// from the advanced option - we should default to FREE at that point.
const serverType = ServerType.getTypeFromServerConfig(newProps.serverConfig);
if (serverType !== this.state.serverType) {
// Reset the phase to default phase for the server type.
this.setState({
serverType,
phase: this.getDefaultPhaseForServerType(serverType),
});
}
if (config.isUrl !== undefined) {
newState.isUrl = config.isUrl;
}
this.props.onServerConfigChange(config);
this.setState(newState, () => {
this._replaceClient();
});
},
getDefaultPhaseForServerType(type) {
@ -136,19 +126,17 @@ module.exports = React.createClass({
// the new type.
switch (type) {
case ServerType.FREE: {
const { hsUrl, isUrl } = ServerType.TYPES.FREE;
this.onServerConfigChange({
hsUrl,
isUrl,
});
const { serverConfig } = ServerType.TYPES.FREE;
this.props.onServerConfigChange(serverConfig);
break;
}
case ServerType.PREMIUM:
// We can accept whatever server config was the default here as this essentially
// acts as a slightly different "custom server"/ADVANCED option.
break;
case ServerType.ADVANCED:
this.onServerConfigChange({
hsUrl: this.props.defaultHsUrl,
isUrl: this.props.defaultIsUrl,
});
// Use the default config from the config
this.props.onServerConfigChange(SdkConfig.get()["validated_server_config"]);
break;
}
@ -158,13 +146,15 @@ module.exports = React.createClass({
});
},
_replaceClient: async function() {
_replaceClient: async function(serverConfig) {
this.setState({
errorText: null,
});
if (!serverConfig) serverConfig = this.props.serverConfig;
const {hsUrl, isUrl} = serverConfig;
this._matrixClient = Matrix.createClient({
baseUrl: this.state.hsUrl,
idBaseUrl: this.state.isUrl,
baseUrl: hsUrl,
idBaseUrl: isUrl,
});
try {
await this._makeRegisterRequest({});
@ -189,12 +179,6 @@ module.exports = React.createClass({
},
onFormSubmit: function(formVals) {
// Don't allow the user to register if there's a discovery error
// Without this, the user could end up registering on the wrong homeserver.
if (this.props.defaultServerDiscoveryError) {
this.setState({errorText: this.props.defaultServerDiscoveryError});
return;
}
this.setState({
errorText: "",
busy: true,
@ -207,7 +191,7 @@ module.exports = React.createClass({
if (!success) {
let msg = response.message || response.toString();
// can we give a better error message?
if (response.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') {
if (response.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') {
const errorTop = messageForResourceLimitError(
response.data.limit_type,
response.data.admin_contact, {
@ -302,8 +286,13 @@ module.exports = React.createClass({
});
},
onServerDetailsNextPhaseClick(ev) {
async onServerDetailsNextPhaseClick(ev) {
ev.stopPropagation();
// TODO: TravisR - Capture the user's input somehow else
if (this._serverConfigRef) {
// Just to make sure the user's input gets captured
await this._serverConfigRef.validateServer();
}
this.setState({
phase: PHASE_REGISTRATION,
});
@ -371,20 +360,17 @@ module.exports = React.createClass({
break;
case ServerType.PREMIUM:
serverDetails = <ModularServerConfig
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl}
onServerConfigChange={this.onServerConfigChange}
ref={r => this._serverConfigRef = r}
serverConfig={this.props.serverConfig}
onServerConfigChange={this.props.onServerConfigChange}
delayTimeMs={250}
/>;
break;
case ServerType.ADVANCED:
serverDetails = <ServerConfig
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
customIsUrl={this.state.discoveredIsUrl || this.props.customIsUrl}
defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl}
onServerConfigChange={this.onServerConfigChange}
ref={r => this._serverConfigRef = r}
serverConfig={this.props.serverConfig}
onServerConfigChange={this.props.onServerConfigChange}
delayTimeMs={250}
/>;
break;
@ -392,6 +378,7 @@ module.exports = React.createClass({
let nextButton = null;
if (PHASES_ENABLED) {
// TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button?
nextButton = <AccessibleButton className="mx_Login_submit"
onClick={this.onServerDetailsNextPhaseClick}
>
@ -446,13 +433,6 @@ module.exports = React.createClass({
onEditServerDetailsClick = this.onEditServerDetailsClick;
}
// If the current HS URL is the default HS URL, then we can label it
// with the default HS name (if it exists).
let hsName;
if (this.state.hsUrl === this.props.defaultHsUrl) {
hsName = this.props.defaultServerName;
}
return <RegistrationForm
defaultUsername={this.state.formVals.username}
defaultEmail={this.state.formVals.email}
@ -462,8 +442,7 @@ module.exports = React.createClass({
onRegisterClick={this.onFormSubmit}
onEditServerDetailsClick={onEditServerDetailsClick}
flows={this.state.flows}
hsName={hsName}
hsUrl={this.state.hsUrl}
serverConfig={this.props.serverConfig}
/>;
}
},
@ -474,7 +453,7 @@ module.exports = React.createClass({
const AuthPage = sdk.getComponent('auth.AuthPage');
let errorText;
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
const err = this.state.errorText;
if (err) {
errorText = <div className="mx_Login_error">{ err }</div>;
}