Introduce a default_server_name for aesthetics and rework .well-known
Fixes https://github.com/vector-im/riot-web/issues/7724 The `default_server_name` from the config gets displayed in the "Login with my [server] matrix ID" dropdown when the default server is being used. At this point, we also discourage the use of the `default_hs_url` and `default_is_url` options because we do an implicit .well-known lookup to configure the client based on the `default_server_name`. If the URLs are still present in the config, we'll honour them and won't do a .well-known lookup when the URLs are mixed with the new server_name option. Users will be warned if the `default_server_name` does not match the `default_hs_url` if both are supplied. Users are additionally prevented from logging in, registering, and resetting their password if the implicit .well-known check fails - this is to prevent people from doing actions against the wrong homeserver. This relies on https://github.com/matrix-org/matrix-js-sdk/pull/799 as we now do auto discovery in two places. Instead of bringing the .well-known out to its own utility class in the react-sdk, we might as well drag it out to the js-sdk.
This commit is contained in:
parent
c0ef2f7df3
commit
633be5061c
7 changed files with 166 additions and 118 deletions
|
@ -180,6 +180,13 @@ limitations under the License.
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_Login_subtext {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_Login_type_container {
|
.mx_Login_type_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
|
|
|
@ -48,6 +48,8 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
|
||||||
import { startAnyRegistrationFlow } from "../../Registration.js";
|
import { startAnyRegistrationFlow } from "../../Registration.js";
|
||||||
import { messageForSyncError } from '../../utils/ErrorUtils';
|
import { messageForSyncError } from '../../utils/ErrorUtils';
|
||||||
|
|
||||||
|
const AutoDiscovery = Matrix.AutoDiscovery;
|
||||||
|
|
||||||
// Disable warnings for now: we use deprecated bluebird functions
|
// Disable warnings for now: we use deprecated bluebird functions
|
||||||
// and need to migrate, but they spam the console with warnings.
|
// and need to migrate, but they spam the console with warnings.
|
||||||
Promise.config({warnings: false});
|
Promise.config({warnings: false});
|
||||||
|
@ -181,6 +183,12 @@ export default React.createClass({
|
||||||
register_is_url: null,
|
register_is_url: null,
|
||||||
register_id_sid: null,
|
register_id_sid: null,
|
||||||
|
|
||||||
|
// Parameters used for setting up the login/registration 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
|
// When showing Modal dialogs we need to set aria-hidden on the root app element
|
||||||
// and disable it when there are no dialogs
|
// and disable it when there are no dialogs
|
||||||
hideToSRUsers: false,
|
hideToSRUsers: false,
|
||||||
|
@ -199,6 +207,10 @@ export default React.createClass({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDefaultServerName: function() {
|
||||||
|
return this.state.defaultServerName;
|
||||||
|
},
|
||||||
|
|
||||||
getCurrentHsUrl: function() {
|
getCurrentHsUrl: function() {
|
||||||
if (this.state.register_hs_url) {
|
if (this.state.register_hs_url) {
|
||||||
return this.state.register_hs_url;
|
return this.state.register_hs_url;
|
||||||
|
@ -211,8 +223,10 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultHsUrl() {
|
getDefaultHsUrl(defaultToMatrixDotOrg) {
|
||||||
return this.props.config.default_hs_url || "https://matrix.org";
|
defaultToMatrixDotOrg = typeof(defaultToMatrixDotOrg) !== 'boolean' ? true : defaultToMatrixDotOrg;
|
||||||
|
if (!this.state.defaultHsUrl && defaultToMatrixDotOrg) return "https://matrix.org";
|
||||||
|
return this.state.defaultHsUrl;
|
||||||
},
|
},
|
||||||
|
|
||||||
getFallbackHsUrl: function() {
|
getFallbackHsUrl: function() {
|
||||||
|
@ -232,7 +246,7 @@ export default React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultIsUrl() {
|
getDefaultIsUrl() {
|
||||||
return this.props.config.default_is_url || "https://vector.im";
|
return this.state.defaultIsUrl || "https://vector.im";
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
|
@ -282,6 +296,11 @@ export default React.createClass({
|
||||||
console.info(`Team token set to ${this._teamToken}`);
|
console.info(`Team token set to ${this._teamToken}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up the default URLs (async)
|
||||||
|
if (this.getDefaultServerName() && !this.getDefaultHsUrl(false)) {
|
||||||
|
this._tryDiscoverDefaultHomeserver(this.getDefaultServerName());
|
||||||
|
}
|
||||||
|
|
||||||
// Set a default HS with query param `hs_url`
|
// Set a default HS with query param `hs_url`
|
||||||
const paramHs = this.props.startingFragmentQueryParams.hs_url;
|
const paramHs = this.props.startingFragmentQueryParams.hs_url;
|
||||||
if (paramHs) {
|
if (paramHs) {
|
||||||
|
@ -1732,6 +1751,21 @@ export default React.createClass({
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_tryDiscoverDefaultHomeserver: async function(serverName) {
|
||||||
|
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});
|
||||||
|
} 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});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_makeRegistrationUrl: function(params) {
|
_makeRegistrationUrl: function(params) {
|
||||||
if (this.props.startingFragmentQueryParams.referrer) {
|
if (this.props.startingFragmentQueryParams.referrer) {
|
||||||
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
||||||
|
@ -1820,6 +1854,8 @@ export default React.createClass({
|
||||||
idSid={this.state.register_id_sid}
|
idSid={this.state.register_id_sid}
|
||||||
email={this.props.startingFragmentQueryParams.email}
|
email={this.props.startingFragmentQueryParams.email}
|
||||||
referrer={this.props.startingFragmentQueryParams.referrer}
|
referrer={this.props.startingFragmentQueryParams.referrer}
|
||||||
|
defaultServerName={this.getDefaultServerName()}
|
||||||
|
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
|
||||||
defaultHsUrl={this.getDefaultHsUrl()}
|
defaultHsUrl={this.getDefaultHsUrl()}
|
||||||
defaultIsUrl={this.getDefaultIsUrl()}
|
defaultIsUrl={this.getDefaultIsUrl()}
|
||||||
brand={this.props.config.brand}
|
brand={this.props.config.brand}
|
||||||
|
@ -1842,6 +1878,8 @@ export default React.createClass({
|
||||||
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
const ForgotPassword = sdk.getComponent('structures.login.ForgotPassword');
|
||||||
return (
|
return (
|
||||||
<ForgotPassword
|
<ForgotPassword
|
||||||
|
defaultServerName={this.getDefaultServerName()}
|
||||||
|
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
|
||||||
defaultHsUrl={this.getDefaultHsUrl()}
|
defaultHsUrl={this.getDefaultHsUrl()}
|
||||||
defaultIsUrl={this.getDefaultIsUrl()}
|
defaultIsUrl={this.getDefaultIsUrl()}
|
||||||
customHsUrl={this.getCurrentHsUrl()}
|
customHsUrl={this.getCurrentHsUrl()}
|
||||||
|
@ -1858,6 +1896,8 @@ export default React.createClass({
|
||||||
<Login
|
<Login
|
||||||
onLoggedIn={Lifecycle.setLoggedIn}
|
onLoggedIn={Lifecycle.setLoggedIn}
|
||||||
onRegisterClick={this.onRegisterClick}
|
onRegisterClick={this.onRegisterClick}
|
||||||
|
defaultServerName={this.getDefaultServerName()}
|
||||||
|
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
|
||||||
defaultHsUrl={this.getDefaultHsUrl()}
|
defaultHsUrl={this.getDefaultHsUrl()}
|
||||||
defaultIsUrl={this.getDefaultIsUrl()}
|
defaultIsUrl={this.getDefaultIsUrl()}
|
||||||
customHsUrl={this.getCurrentHsUrl()}
|
customHsUrl={this.getCurrentHsUrl()}
|
||||||
|
|
|
@ -36,6 +36,14 @@ module.exports = React.createClass({
|
||||||
onLoginClick: PropTypes.func,
|
onLoginClick: PropTypes.func,
|
||||||
onRegisterClick: PropTypes.func,
|
onRegisterClick: PropTypes.func,
|
||||||
onComplete: PropTypes.func.isRequired,
|
onComplete: PropTypes.func.isRequired,
|
||||||
|
|
||||||
|
// The default server name to use when the user hasn't specified
|
||||||
|
// one. This is used when displaying the defaultHsUrl in the UI.
|
||||||
|
defaultServerName: PropTypes.string,
|
||||||
|
|
||||||
|
// An error passed along from higher up explaining that something
|
||||||
|
// went wrong when finding the defaultHsUrl.
|
||||||
|
defaultServerDiscoveryError: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -45,6 +53,7 @@ module.exports = React.createClass({
|
||||||
progress: null,
|
progress: null,
|
||||||
password: null,
|
password: null,
|
||||||
password2: null,
|
password2: null,
|
||||||
|
errorText: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -81,6 +90,13 @@ module.exports = React.createClass({
|
||||||
onSubmitForm: function(ev) {
|
onSubmitForm: function(ev) {
|
||||||
ev.preventDefault();
|
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) {
|
if (!this.state.email) {
|
||||||
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
|
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
|
||||||
} else if (!this.state.password || !this.state.password2) {
|
} else if (!this.state.password || !this.state.password2) {
|
||||||
|
@ -200,6 +216,12 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let errorText = null;
|
||||||
|
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
||||||
|
if (err) {
|
||||||
|
errorText = <div className="mx_Login_error">{ err }</div>;
|
||||||
|
}
|
||||||
|
|
||||||
const LanguageSelector = sdk.getComponent('structures.login.LanguageSelector');
|
const LanguageSelector = sdk.getComponent('structures.login.LanguageSelector');
|
||||||
|
|
||||||
resetPasswordJsx = (
|
resetPasswordJsx = (
|
||||||
|
@ -230,6 +252,7 @@ module.exports = React.createClass({
|
||||||
<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>
|
</form>
|
||||||
{ serverConfigSection }
|
{ serverConfigSection }
|
||||||
|
{ errorText }
|
||||||
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
|
<a className="mx_Login_create" onClick={this.props.onLoginClick} href="#">
|
||||||
{ _t('Return to login screen') }
|
{ _t('Return to login screen') }
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -26,11 +26,17 @@ import Login from '../../../Login';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||||
import request from 'browser-request';
|
import { AutoDiscovery } from "matrix-js-sdk";
|
||||||
|
|
||||||
// For validating phone numbers without country codes
|
// For validating phone numbers without country codes
|
||||||
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
|
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
|
||||||
|
|
||||||
|
// These are used in several places, and come from the js-sdk's autodiscovery
|
||||||
|
// stuff. We define them here so that they'll be picked up by i18n.
|
||||||
|
_td("Invalid homeserver discovery response");
|
||||||
|
_td("Invalid identity server discovery response");
|
||||||
|
_td("General failure");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wire component which glues together login UI components and Login logic
|
* A wire component which glues together login UI components and Login logic
|
||||||
*/
|
*/
|
||||||
|
@ -51,6 +57,14 @@ module.exports = React.createClass({
|
||||||
// different home server without confusing users.
|
// different home server without confusing users.
|
||||||
fallbackHsUrl: PropTypes.string,
|
fallbackHsUrl: PropTypes.string,
|
||||||
|
|
||||||
|
// The default server name to use when the user hasn't specified
|
||||||
|
// one. This is used when displaying the defaultHsUrl in the UI.
|
||||||
|
defaultServerName: PropTypes.string,
|
||||||
|
|
||||||
|
// An error passed along from higher up explaining that something
|
||||||
|
// went wrong when finding the defaultHsUrl.
|
||||||
|
defaultServerDiscoveryError: PropTypes.string,
|
||||||
|
|
||||||
defaultDeviceDisplayName: PropTypes.string,
|
defaultDeviceDisplayName: PropTypes.string,
|
||||||
|
|
||||||
// login shouldn't know or care how registration is done.
|
// login shouldn't know or care how registration is done.
|
||||||
|
@ -113,7 +127,7 @@ module.exports = React.createClass({
|
||||||
onPasswordLogin: function(username, phoneCountry, phoneNumber, password) {
|
onPasswordLogin: function(username, phoneCountry, phoneNumber, password) {
|
||||||
// Prevent people from submitting their password when homeserver
|
// Prevent people from submitting their password when homeserver
|
||||||
// discovery went wrong
|
// discovery went wrong
|
||||||
if (this.state.discoveryError) return;
|
if (this.state.discoveryError || this.props.defaultServerDiscoveryError) return;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
|
@ -290,114 +304,43 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const wellknown = await this._getWellKnownObject(`https://${serverName}/.well-known/matrix/client`);
|
const discovery = await AutoDiscovery.findClientConfig(serverName);
|
||||||
if (!wellknown["m.homeserver"]) {
|
const state = discovery["m.homeserver"].state;
|
||||||
console.error("No m.homeserver key in well-known response");
|
if (state !== AutoDiscovery.SUCCESS && state !== AutoDiscovery.PROMPT) {
|
||||||
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
|
this.setState({
|
||||||
return;
|
discoveredHsUrl: "",
|
||||||
|
discoveredIsUrl: "",
|
||||||
|
discoveryError: discovery["m.homeserver"].error,
|
||||||
|
});
|
||||||
|
} else if (state === AutoDiscovery.PROMPT) {
|
||||||
|
this.setState({
|
||||||
|
discoveredHsUrl: "",
|
||||||
|
discoveredIsUrl: "",
|
||||||
|
discoveryError: "",
|
||||||
|
});
|
||||||
|
} else if (state === AutoDiscovery.SUCCESS) {
|
||||||
|
this.setState({
|
||||||
|
discoveredHsUrl: discovery["m.homeserver"].base_url,
|
||||||
|
discoveredIsUrl:
|
||||||
|
discovery["m.identity_server"].state === AutoDiscovery.SUCCESS
|
||||||
|
? discovery["m.identity_server"].base_url
|
||||||
|
: "",
|
||||||
|
discoveryError: "",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn("Unknown state for m.homeserver in discovery response: ", discovery);
|
||||||
|
this.setState({
|
||||||
|
discoveredHsUrl: "",
|
||||||
|
discoveredIsUrl: "",
|
||||||
|
discoveryError: _t("Unknown failure discovering homeserver"),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const hsUrl = this._sanitizeWellKnownUrl(wellknown["m.homeserver"]["base_url"]);
|
|
||||||
if (!hsUrl) {
|
|
||||||
console.error("Invalid base_url for m.homeserver");
|
|
||||||
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Verifying homeserver URL: " + hsUrl);
|
|
||||||
const hsVersions = await this._getWellKnownObject(`${hsUrl}/_matrix/client/versions`);
|
|
||||||
if (!hsVersions["versions"]) {
|
|
||||||
console.error("Invalid /versions response");
|
|
||||||
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isUrl = "";
|
|
||||||
if (wellknown["m.identity_server"]) {
|
|
||||||
isUrl = this._sanitizeWellKnownUrl(wellknown["m.identity_server"]["base_url"]);
|
|
||||||
if (!isUrl) {
|
|
||||||
console.error("Invalid base_url for m.identity_server");
|
|
||||||
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Verifying identity server URL: " + isUrl);
|
|
||||||
const isResponse = await this._getWellKnownObject(`${isUrl}/_matrix/identity/api/v1`);
|
|
||||||
if (!isResponse) {
|
|
||||||
console.error("Invalid /api/v1 response");
|
|
||||||
this.setState({discoveryError: _t("Invalid homeserver discovery response")});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({discoveredHsUrl: hsUrl, discoveredIsUrl: isUrl, discoveryError: ""});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
if (e.wkAction) {
|
|
||||||
if (e.wkAction === "FAIL_ERROR" || e.wkAction === "FAIL_PROMPT") {
|
|
||||||
// We treat FAIL_ERROR and FAIL_PROMPT the same to avoid having the user
|
|
||||||
// submit their details to the wrong homeserver. In practice, the custom
|
|
||||||
// server options will show up to try and guide the user into entering
|
|
||||||
// the required information.
|
|
||||||
this.setState({discoveryError: _t("Cannot find homeserver")});
|
|
||||||
return;
|
|
||||||
} else if (e.wkAction === "IGNORE") {
|
|
||||||
// Nothing to discover
|
|
||||||
this.setState({discoveryError: "", discoveredHsUrl: "", discoveredIsUrl: ""});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_sanitizeWellKnownUrl: function(url) {
|
|
||||||
if (!url) return false;
|
|
||||||
|
|
||||||
const parser = document.createElement('a');
|
|
||||||
parser.href = url;
|
|
||||||
|
|
||||||
if (parser.protocol !== "http:" && parser.protocol !== "https:") return false;
|
|
||||||
if (!parser.hostname) return false;
|
|
||||||
|
|
||||||
const port = parser.port ? `:${parser.port}` : "";
|
|
||||||
const path = parser.pathname ? parser.pathname : "";
|
|
||||||
let saferUrl = `${parser.protocol}//${parser.hostname}${port}${path}`;
|
|
||||||
if (saferUrl.endsWith("/")) saferUrl = saferUrl.substring(0, saferUrl.length - 1);
|
|
||||||
return saferUrl;
|
|
||||||
},
|
|
||||||
|
|
||||||
_getWellKnownObject: function(url) {
|
|
||||||
return new Promise(function(resolve, reject) {
|
|
||||||
request(
|
|
||||||
{ method: "GET", url: url },
|
|
||||||
(err, response, body) => {
|
|
||||||
if (err || response.status < 200 || response.status >= 300) {
|
|
||||||
let action = "FAIL_ERROR";
|
|
||||||
if (response.status === 404) {
|
|
||||||
// We could just resolve with an empty object, but that
|
|
||||||
// causes a different series of branches when the m.homeserver
|
|
||||||
// bit of the JSON is missing.
|
|
||||||
action = "IGNORE";
|
|
||||||
}
|
|
||||||
reject({err: err, response: response, wkAction: action});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
resolve(JSON.parse(body));
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
if (e.name === "SyntaxError") {
|
|
||||||
reject({wkAction: "FAIL_PROMPT", wkError: "Invalid JSON"});
|
|
||||||
} else throw e;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_initLoginLogic: function(hsUrl, isUrl) {
|
_initLoginLogic: function(hsUrl, isUrl) {
|
||||||
const self = this;
|
const self = this;
|
||||||
hsUrl = hsUrl || this.state.enteredHomeserverUrl;
|
hsUrl = hsUrl || this.state.enteredHomeserverUrl;
|
||||||
|
@ -527,6 +470,9 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
_renderPasswordStep: function() {
|
_renderPasswordStep: function() {
|
||||||
const PasswordLogin = sdk.getComponent('login.PasswordLogin');
|
const PasswordLogin = sdk.getComponent('login.PasswordLogin');
|
||||||
|
const hsName = this.state.enteredHomeserverUrl === this.props.defaultHsUrl
|
||||||
|
? this.props.defaultServerName
|
||||||
|
: null;
|
||||||
return (
|
return (
|
||||||
<PasswordLogin
|
<PasswordLogin
|
||||||
onSubmit={this.onPasswordLogin}
|
onSubmit={this.onPasswordLogin}
|
||||||
|
@ -541,6 +487,7 @@ module.exports = React.createClass({
|
||||||
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
||||||
loginIncorrect={this.state.loginIncorrect}
|
loginIncorrect={this.state.loginIncorrect}
|
||||||
hsUrl={this.state.enteredHomeserverUrl}
|
hsUrl={this.state.enteredHomeserverUrl}
|
||||||
|
hsName={hsName}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -559,7 +506,7 @@ module.exports = React.createClass({
|
||||||
const ServerConfig = sdk.getComponent("login.ServerConfig");
|
const ServerConfig = sdk.getComponent("login.ServerConfig");
|
||||||
const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
|
const loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null;
|
||||||
|
|
||||||
const errorText = this.state.discoveryError || this.state.errorText;
|
const errorText = this.props.defaultServerDiscoveryError || this.state.discoveryError || this.state.errorText;
|
||||||
|
|
||||||
let loginAsGuestJsx;
|
let loginAsGuestJsx;
|
||||||
if (this.props.enableGuest) {
|
if (this.props.enableGuest) {
|
||||||
|
@ -576,7 +523,7 @@ module.exports = React.createClass({
|
||||||
serverConfig = <ServerConfig ref="serverConfig"
|
serverConfig = <ServerConfig ref="serverConfig"
|
||||||
withToggleButton={true}
|
withToggleButton={true}
|
||||||
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
||||||
customIsUrl={this.state.discoveredIsUrl ||this.props.customIsUrl}
|
customIsUrl={this.state.discoveredIsUrl || this.props.customIsUrl}
|
||||||
defaultHsUrl={this.props.defaultHsUrl}
|
defaultHsUrl={this.props.defaultHsUrl}
|
||||||
defaultIsUrl={this.props.defaultIsUrl}
|
defaultIsUrl={this.props.defaultIsUrl}
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
|
|
|
@ -57,6 +57,14 @@ module.exports = React.createClass({
|
||||||
}),
|
}),
|
||||||
teamSelected: PropTypes.object,
|
teamSelected: PropTypes.object,
|
||||||
|
|
||||||
|
// The default server name to use when the user hasn't specified
|
||||||
|
// one. This is used when displaying the defaultHsUrl in the UI.
|
||||||
|
defaultServerName: PropTypes.string,
|
||||||
|
|
||||||
|
// An error passed along from higher up explaining that something
|
||||||
|
// went wrong when finding the defaultHsUrl.
|
||||||
|
defaultServerDiscoveryError: PropTypes.string,
|
||||||
|
|
||||||
defaultDeviceDisplayName: PropTypes.string,
|
defaultDeviceDisplayName: PropTypes.string,
|
||||||
|
|
||||||
// registration shouldn't know or care how login is done.
|
// registration shouldn't know or care how login is done.
|
||||||
|
@ -170,6 +178,12 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onFormSubmit: function(formVals) {
|
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({
|
this.setState({
|
||||||
errorText: "",
|
errorText: "",
|
||||||
busy: true,
|
busy: true,
|
||||||
|
@ -441,12 +455,13 @@ module.exports = React.createClass({
|
||||||
let header;
|
let header;
|
||||||
let errorText;
|
let errorText;
|
||||||
// FIXME: remove hardcoded Status team tweaks at some point
|
// FIXME: remove hardcoded Status team tweaks at some point
|
||||||
if (theme === 'status' && this.state.errorText) {
|
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
||||||
header = <div className="mx_Login_error">{ this.state.errorText }</div>;
|
if (theme === 'status' && err) {
|
||||||
|
header = <div className="mx_Login_error">{ err }</div>;
|
||||||
} else {
|
} else {
|
||||||
header = <h2>{ _t('Create an account') }</h2>;
|
header = <h2>{ _t('Create an account') }</h2>;
|
||||||
if (this.state.errorText) {
|
if (err) {
|
||||||
errorText = <div className="mx_Login_error">{ this.state.errorText }</div>;
|
errorText = <div className="mx_Login_error">{ err }</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ class PasswordLogin extends React.Component {
|
||||||
initialPassword: "",
|
initialPassword: "",
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
hsDomain: "",
|
hsDomain: "",
|
||||||
|
hsName: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -250,13 +251,24 @@ class PasswordLogin extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let matrixIdText = '';
|
let matrixIdText = _t('Matrix ID');
|
||||||
|
let matrixIdSubtext = null;
|
||||||
|
if (this.props.hsName) {
|
||||||
|
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: this.props.hsName});
|
||||||
|
}
|
||||||
if (this.props.hsUrl) {
|
if (this.props.hsUrl) {
|
||||||
try {
|
try {
|
||||||
const parsedHsUrl = new URL(this.props.hsUrl);
|
const parsedHsUrl = new URL(this.props.hsUrl);
|
||||||
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname});
|
if (!this.props.hsName) {
|
||||||
|
matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname});
|
||||||
|
} else if (parsedHsUrl.hostname !== this.props.hsName) {
|
||||||
|
matrixIdSubtext = _t('%(serverName)s is located at %(homeserverUrl)s', {
|
||||||
|
serverName: this.props.hsName,
|
||||||
|
homeserverUrl: this.props.hsUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// pass
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +304,7 @@ class PasswordLogin extends React.Component {
|
||||||
<div>
|
<div>
|
||||||
<form onSubmit={this.onSubmitForm}>
|
<form onSubmit={this.onSubmitForm}>
|
||||||
{ loginType }
|
{ loginType }
|
||||||
|
<span className="mx_Login_subtext">{ matrixIdSubtext }</span>
|
||||||
{ loginField }
|
{ loginField }
|
||||||
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
<input className={pwFieldClass} ref={(e) => {this._passwordField = e;}} type="password"
|
||||||
name="password"
|
name="password"
|
||||||
|
@ -325,6 +338,7 @@ PasswordLogin.propTypes = {
|
||||||
onPhoneNumberChanged: PropTypes.func,
|
onPhoneNumberChanged: PropTypes.func,
|
||||||
onPasswordChanged: PropTypes.func,
|
onPasswordChanged: PropTypes.func,
|
||||||
loginIncorrect: PropTypes.bool,
|
loginIncorrect: PropTypes.bool,
|
||||||
|
hsName: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = PasswordLogin;
|
module.exports = PasswordLogin;
|
||||||
|
|
|
@ -686,6 +686,7 @@
|
||||||
"Mobile phone number": "Mobile phone number",
|
"Mobile phone number": "Mobile phone number",
|
||||||
"Forgot your password?": "Forgot your password?",
|
"Forgot your password?": "Forgot your password?",
|
||||||
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
|
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
|
||||||
|
"%(serverName)s is located at %(homeserverUrl)s": "%(serverName)s is located at %(homeserverUrl)s",
|
||||||
"Sign in with": "Sign in with",
|
"Sign in with": "Sign in with",
|
||||||
"Email address": "Email address",
|
"Email address": "Email address",
|
||||||
"Sign in": "Sign in",
|
"Sign in": "Sign in",
|
||||||
|
@ -831,7 +832,6 @@
|
||||||
"And %(count)s more...|other": "And %(count)s more...",
|
"And %(count)s more...|other": "And %(count)s more...",
|
||||||
"ex. @bob:example.com": "ex. @bob:example.com",
|
"ex. @bob:example.com": "ex. @bob:example.com",
|
||||||
"Add User": "Add User",
|
"Add User": "Add User",
|
||||||
"Matrix ID": "Matrix ID",
|
|
||||||
"Matrix Room ID": "Matrix Room ID",
|
"Matrix Room ID": "Matrix Room ID",
|
||||||
"email address": "email address",
|
"email address": "email address",
|
||||||
"You have entered an invalid address.": "You have entered an invalid address.",
|
"You have entered an invalid address.": "You have entered an invalid address.",
|
||||||
|
@ -1283,6 +1283,9 @@
|
||||||
"Confirm your new password": "Confirm your new password",
|
"Confirm your new password": "Confirm your new password",
|
||||||
"Send Reset Email": "Send Reset Email",
|
"Send Reset Email": "Send Reset Email",
|
||||||
"Create an account": "Create an account",
|
"Create an account": "Create an account",
|
||||||
|
"Invalid homeserver discovery response": "Invalid homeserver discovery response",
|
||||||
|
"Invalid identity server discovery response": "Invalid identity server discovery response",
|
||||||
|
"General failure": "General failure",
|
||||||
"This Home Server does not support login using email address.": "This Home Server does not support login using email address.",
|
"This Home Server does not support login using email address.": "This Home Server does not support login using email address.",
|
||||||
"Please <a>contact your service administrator</a> to continue using this service.": "Please <a>contact your service administrator</a> to continue using this service.",
|
"Please <a>contact your service administrator</a> to continue using this service.": "Please <a>contact your service administrator</a> to continue using this service.",
|
||||||
"Incorrect username and/or password.": "Incorrect username and/or password.",
|
"Incorrect username and/or password.": "Incorrect username and/or password.",
|
||||||
|
@ -1290,8 +1293,7 @@
|
||||||
"Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.",
|
"Guest access is disabled on this Home Server.": "Guest access is disabled on this Home Server.",
|
||||||
"Failed to perform homeserver discovery": "Failed to perform homeserver discovery",
|
"Failed to perform homeserver discovery": "Failed to perform homeserver discovery",
|
||||||
"The phone number entered looks invalid": "The phone number entered looks invalid",
|
"The phone number entered looks invalid": "The phone number entered looks invalid",
|
||||||
"Invalid homeserver discovery response": "Invalid homeserver discovery response",
|
"Unknown failure discovering homeserver": "Unknown failure discovering homeserver",
|
||||||
"Cannot find homeserver": "Cannot find homeserver",
|
|
||||||
"This homeserver doesn't offer any login flows which are supported by this client.": "This homeserver doesn't offer any login flows which are supported by this client.",
|
"This homeserver doesn't offer any login flows which are supported by this client.": "This homeserver doesn't offer any login flows which are supported by this client.",
|
||||||
"Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.",
|
"Error: Problem communicating with the given homeserver.": "Error: Problem communicating with the given homeserver.",
|
||||||
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.",
|
"Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or <a>enable unsafe scripts</a>.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue