diff --git a/src/Registration.js b/src/Registration.js new file mode 100644 index 0000000000..b3da7acb1e --- /dev/null +++ b/src/Registration.js @@ -0,0 +1,96 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * Utility code for registering with a homeserver + * Note that this is currently *not* used by the actual + * registration code. + */ + +import dis from './dispatcher'; +import sdk from './index'; +import MatrixClientPeg from './MatrixClientPeg'; +import Modal from './Modal'; +import { _t } from './languageHandler'; + +/** + * Starts either the ILAG or full registration flow, depending + * on what the HS supports + */ +export async function startAnyRegistrationFlow(options) { + if (options === undefined) options = {}; + const flows = await _getRegistrationFlows(); + // look for an ILAG compatible flow. We define this as one + // which has only dummy or recaptcha flows. In practice it + // would support any stage InteractiveAuth supports, just not + // ones like email & msisdn which require the user to supply + // the relevant details in advance. We err on the side of + // caution though. + let hasIlagFlow = false; + for (const flow of flows) { + let flowSuitable = true; + for (const stage of flow.stages) { + if (!['m.login.dummy', 'm.login.recaptcha'].includes(stage)) { + flowSuitable = false; + break; + } + } + if (flowSuitable) { + hasIlagFlow = true; + break; + } + } + if (hasIlagFlow) { + dis.dispatch({ + action: 'view_set_mxid', + go_home_on_cancel: options.go_home_on_cancel, + }); + } else { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + Modal.createTrackedDialog('Registration required', '', QuestionDialog, { + title: _t("Registration Required"), + description: _t("You need to register to do this. Would you like to register now?"), + button: _t("Register"), + onFinished: (proceed) => { + if (proceed) { + dis.dispatch({action: 'start_registration'}); + } else if (options.go_home_on_cancel) { + dis.dispatch({action: 'view_home_page'}); + } + } + }); + } +} + +async function _getRegistrationFlows() { + try { + await MatrixClientPeg.get().register( + null, + null, + undefined, + {}, + {}, + ); + console.log("Register request succeeded when it should have returned 401!"); + } catch (e) { + if (e.httpStatus === 401) { + return e.data.flows; + } + throw e; + } + throw new Error("Register request succeeded when it should have returned 401!"); +} + diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 7a13067976..002e439454 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -45,6 +45,7 @@ import createRoom from "../../createRoom"; import KeyRequestHandler from '../../KeyRequestHandler'; import { _t, getCurrentLanguage } from '../../languageHandler'; import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; +import { startAnyRegistrationFlow } from "../../Registration.js"; /** constants for MatrixChat.state.view */ const VIEWS = { @@ -471,7 +472,7 @@ export default React.createClass({ action: 'do_after_sync_prepared', deferred_action: payload, }); - dis.dispatch({action: 'view_set_mxid'}); + dis.dispatch({action: 'require_registration'}); return; } @@ -479,7 +480,11 @@ export default React.createClass({ case 'logout': Lifecycle.logout(); break; + case 'require_registration': + startAnyRegistrationFlow(payload); + break; case 'start_registration': + // This starts the full registration flow this._startRegistration(payload.params || {}); break; case 'start_login': @@ -945,7 +950,7 @@ export default React.createClass({ }); } dis.dispatch({ - action: 'view_set_mxid', + action: 'require_registration', // If the set_mxid dialog is cancelled, view /home because if the browser // was pointing at /user/@someone:domain?action=chat, the URL needs to be // reset so that they can revisit /user/.. // (and trigger diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 995614225a..d06b986474 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -87,6 +87,8 @@ "Unable to enable Notifications": "Unable to enable Notifications", "This email address was not found": "This email address was not found", "Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.", + "Registration Required": "Registration Required", + "You need to register to do this. Would you like to register now?": "You need to register to do this. Would you like to register now?", "Default": "Default", "Restricted": "Restricted", "Moderator": "Moderator", @@ -146,7 +148,6 @@ "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's device %(deviceId)s. Device marked as verified.", "Displays action": "Displays action", "Forces the current outbound group session in an encrypted room to be discarded": "Forces the current outbound group session in an encrypted room to be discarded", - "Error Discarding Session": "Error Discarding Session", "Unrecognised command:": "Unrecognised command:", "Reason": "Reason", "%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.", @@ -398,8 +399,6 @@ "At this time it is not possible to reply with a file so this will be sent without being a reply.": "At this time it is not possible to reply with a file so this will be sent without being a reply.", "Upload Files": "Upload Files", "Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?", - "This room has been replaced and is no longer active.": "This room has been replaced and is no longer active.", - "The conversation continues here.": "The conversation continues here.", "Encrypted room": "Encrypted room", "Unencrypted room": "Unencrypted room", "Hangup": "Hangup", @@ -411,6 +410,8 @@ "Send a reply (unencrypted)…": "Send a reply (unencrypted)…", "Send an encrypted message…": "Send an encrypted message…", "Send a message (unencrypted)…": "Send a message (unencrypted)…", + "This room has been replaced and is no longer active.": "This room has been replaced and is no longer active.", + "The conversation continues here.": "The conversation continues here.", "You do not have permission to post to this room": "You do not have permission to post to this room", "Turn Markdown on": "Turn Markdown on", "Turn Markdown off": "Turn Markdown off", @@ -1201,6 +1202,7 @@ "Failed to fetch avatar URL": "Failed to fetch avatar URL", "Set a display name:": "Set a display name:", "Upload an avatar:": "Upload an avatar:", + "Unable to query for supported registration methods": "Unable to query for supported registration methods", "This server does not support authentication with a phone number.": "This server does not support authentication with a phone number.", "Missing password.": "Missing password.", "Passwords don't match.": "Passwords don't match.",