Merge branch 'travis/soft-logout-rehydrate' into t1

pull/21833/head
Travis Ralston 2019-07-05 15:03:24 -06:00
commit ca6563dce4
8 changed files with 200 additions and 13 deletions

View File

@ -79,3 +79,22 @@ limitations under the License.
color: $button-danger-disabled-fg-color; color: $button-danger-disabled-fg-color;
background-color: $button-danger-disabled-bg-color; background-color: $button-danger-disabled-bg-color;
} }
.mx_AccessibleButton_kind_link {
color: $button-link-fg-color;
background-color: $button-link-bg-color;
}
.mx_AccessibleButton_kind_link.mx_AccessibleButton_disabled {
opacity: 0.4;
}
.mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_link_sm {
padding: 5px 12px;
color: $button-link-fg-color;
background-color: $button-link-bg-color;
}
.mx_AccessibleButton_kind_link_sm.mx_AccessibleButton_disabled {
opacity: 0.4;
}

View File

@ -143,6 +143,8 @@ $button-danger-fg-color: #ffffff;
$button-danger-bg-color: $notice-primary-color; $button-danger-bg-color: $notice-primary-color;
$button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-fg-color: #ffffff;
$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color
$button-link-fg-color: $accent-color;
$button-link-bg-color: transparent;
$room-warning-bg-color: $header-panel-bg-color; $room-warning-bg-color: $header-panel-bg-color;

View File

@ -244,6 +244,8 @@ $button-danger-fg-color: #ffffff;
$button-danger-bg-color: $notice-primary-color; $button-danger-bg-color: $notice-primary-color;
$button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-fg-color: #ffffff;
$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color
$button-link-fg-color: $accent-color;
$button-link-bg-color: transparent;
// Toggle switch // Toggle switch
$togglesw-off-color: #c1c9d6; $togglesw-off-color: #c1c9d6;

View File

@ -340,6 +340,25 @@ export function setLoggedIn(credentials) {
return _doSetLoggedIn(credentials, true); return _doSetLoggedIn(credentials, true);
} }
/**
* Hydrates an existing session by using the credentials provided. This will
* not clear any local storage, unlike setLoggedIn().
*
* Stops the existing Matrix client (without clearing its data) and starts a
* new one in its place. This additionally starts all other react-sdk services
* which use the new Matrix client.
*
* @param {MatrixClientCreds} credentials The credentials to use
*
* @returns {Promise} promise which resolves to the new MatrixClient once it has been started
*/
export function hydrateSession(credentials) {
stopMatrixClient();
localStorage.removeItem("mx_soft_logout");
_isLoggingOut = false;
return _doSetLoggedIn(credentials, false);
}
/** /**
* fires on_logging_in, optionally clears localstorage, persists new credentials * fires on_logging_in, optionally clears localstorage, persists new credentials
* to localstorage, starts the new client. * to localstorage, starts the new client.
@ -541,6 +560,7 @@ async function startMatrixClient(startSyncing=true) {
await MatrixClientPeg.start(); await MatrixClientPeg.start();
} else { } else {
console.warn("Caller requested only auxiliary services be started"); console.warn("Caller requested only auxiliary services be started");
await MatrixClientPeg.assign();
} }
// dispatch that we finished starting up to wire up any other bits // dispatch that we finished starting up to wire up any other bits

View File

@ -120,7 +120,7 @@ class MatrixClientPeg {
this._createClient(creds); this._createClient(creds);
} }
async start() { async assign() {
for (const dbType of ['indexeddb', 'memory']) { for (const dbType of ['indexeddb', 'memory']) {
try { try {
const promise = this.matrixClient.store.startup(); const promise = this.matrixClient.store.startup();
@ -131,7 +131,7 @@ class MatrixClientPeg {
if (dbType === 'indexeddb') { if (dbType === 'indexeddb') {
console.error('Error starting matrixclient store - falling back to memory store', err); console.error('Error starting matrixclient store - falling back to memory store', err);
this.matrixClient.store = new Matrix.MemoryStore({ this.matrixClient.store = new Matrix.MemoryStore({
localStorage: global.localStorage, localStorage: global.localStorage,
}); });
} else { } else {
console.error('Failed to start memory store!', err); console.error('Failed to start memory store!', err);
@ -172,6 +172,12 @@ class MatrixClientPeg {
MatrixActionCreators.start(this.matrixClient); MatrixActionCreators.start(this.matrixClient);
MatrixClientBackedSettingsHandler.matrixClient = this.matrixClient; MatrixClientBackedSettingsHandler.matrixClient = this.matrixClient;
return opts;
}
async start() {
const opts = await this.assign();
console.log(`MatrixClientPeg: really starting MatrixClient`); console.log(`MatrixClientPeg: really starting MatrixClient`);
await this.get().startClient(opts); await this.get().startClient(opts);
console.log(`MatrixClientPeg: MatrixClient started`); console.log(`MatrixClientPeg: MatrixClient started`);

View File

@ -450,6 +450,10 @@ export default React.createClass({
startAnyRegistrationFlow(payload); startAnyRegistrationFlow(payload);
break; break;
case 'start_registration': case 'start_registration':
if (Lifecycle.isSoftLogout()) {
this._onSoftLogout();
break;
}
// This starts the full registration flow // This starts the full registration flow
if (payload.screenAfterLogin) { if (payload.screenAfterLogin) {
this._screenAfterLogin = payload.screenAfterLogin; this._screenAfterLogin = payload.screenAfterLogin;
@ -457,6 +461,10 @@ export default React.createClass({
this._startRegistration(payload.params || {}); this._startRegistration(payload.params || {});
break; break;
case 'start_login': case 'start_login':
if (Lifecycle.isSoftLogout()) {
this._onSoftLogout();
break;
}
if (payload.screenAfterLogin) { if (payload.screenAfterLogin) {
this._screenAfterLogin = payload.screenAfterLogin; this._screenAfterLogin = payload.screenAfterLogin;
} }

View File

@ -23,6 +23,21 @@ import Modal from '../../../Modal';
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import SdkConfig from "../../../SdkConfig"; import SdkConfig from "../../../SdkConfig";
import MatrixClientPeg from "../../../MatrixClientPeg"; import MatrixClientPeg from "../../../MatrixClientPeg";
import {sendLoginRequest} from "../../../Login";
const LOGIN_VIEW = {
LOADING: 1,
PASSWORD: 2,
CAS: 3, // SSO, but old
SSO: 4,
UNSUPPORTED: 5,
};
const FLOWS_TO_VIEWS = {
"m.login.password": LOGIN_VIEW.PASSWORD,
"m.login.cas": LOGIN_VIEW.CAS,
"m.login.sso": LOGIN_VIEW.SSO,
};
export default class SoftLogout extends React.Component { export default class SoftLogout extends React.Component {
static propTypes = { static propTypes = {
@ -48,7 +63,14 @@ export default class SoftLogout extends React.Component {
domainName, domainName,
userId, userId,
displayName, displayName,
loginView: LOGIN_VIEW.LOADING,
busy: false,
password: "",
errorText: "",
}; };
this._initLogin();
} }
onClearAll = () => { onClearAll = () => {
@ -63,10 +85,122 @@ export default class SoftLogout extends React.Component {
}); });
}; };
onLogin = () => { async _initLogin() {
dis.dispatch({action: 'start_login'}); // Note: we don't use the existing Login class because it is heavily flow-based. We don't
// care about login flows here, unless it is the single flow we support.
const client = MatrixClientPeg.get();
const loginViews = (await client.loginFlows()).flows.map(f => FLOWS_TO_VIEWS[f.type]);
const chosenView = loginViews.filter(f => !!f)[0] || LOGIN_VIEW.UNSUPPORTED;
this.setState({loginView: chosenView});
}
onPasswordChange = (ev) => {
this.setState({password: ev.target.value});
}; };
onForgotPassword = () => {
dis.dispatch({action: 'start_password_recovery'});
};
onPasswordLogin = async (ev) => {
ev.preventDefault();
ev.stopPropagation();
this.setState({busy: true});
const hsUrl = MatrixClientPeg.get().getHomeserverUrl();
const isUrl = MatrixClientPeg.get().getIdentityServerUrl();
const loginType = "m.login.password";
const loginParams = {
identifier: {
type: "m.id.user",
user: MatrixClientPeg.get().getUserId(),
},
password: this.state.password,
device_id: MatrixClientPeg.get().getDeviceId(),
};
let credentials = null;
try {
credentials = await sendLoginRequest(hsUrl, isUrl, loginType, loginParams);
} catch (e) {
let errorText = _t("Failed to re-authenticate due to a homeserver problem");
if (e.errcode === "M_FORBIDDEN" && (e.httpStatus === 401 || e.httpStatus === 403)) {
errorText = _t("Incorrect password");
}
this.setState({
busy: false,
errorText: errorText,
});
return;
}
Lifecycle.hydrateSession(credentials).catch((e) => {
console.error(e);
this.setState({busy: false, errorText: _t("Failed to re-authenticate")});
});
};
_renderSignInSection() {
if (this.state.loginView === LOGIN_VIEW.LOADING) {
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
}
if (this.state.loginView === LOGIN_VIEW.PASSWORD) {
const Field = sdk.getComponent("elements.Field");
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let error = null;
if (this.state.errorText) {
error = <span className='mx_Login_error'>{this.state.errorText}</span>;
}
return (
<form onSubmit={this.onPasswordLogin}>
<p>{_t("Enter your password to sign in and regain access to your account.")}</p>
{error}
<Field
id="softlogout_password"
type="password"
label={_t("Password")}
onChange={this.onPasswordChange}
value={this.state.password}
disabled={this.state.busy}
/>
<AccessibleButton
onClick={this.onPasswordLogin}
kind="primary"
type="submit"
disabled={this.state.busy}
>
{_t("Sign In")}
</AccessibleButton>
<AccessibleButton onClick={this.onForgotPassword} kind="link">
{_t("Forgotten your password?")}
</AccessibleButton>
</form>
);
}
if (this.state.loginView === LOGIN_VIEW.SSO || this.state.loginView === LOGIN_VIEW.CAS) {
// TODO: TravisR - https://github.com/vector-im/riot-web/issues/10238
return <p>PLACEHOLDER</p>;
}
// Default: assume unsupported
return (
<p>
{_t(
"Cannot re-authenticate with your account. Please contact your " +
"homeserver admin for more information.",
)}
</p>
);
}
render() { render() {
const AuthPage = sdk.getComponent("auth.AuthPage"); const AuthPage = sdk.getComponent("auth.AuthPage");
const AuthHeader = sdk.getComponent("auth.AuthHeader"); const AuthHeader = sdk.getComponent("auth.AuthHeader");
@ -107,14 +241,7 @@ export default class SoftLogout extends React.Component {
<h3>{_t("Sign in")}</h3> <h3>{_t("Sign in")}</h3>
<div> <div>
{_t( {this._renderSignInSection()}
"Sign in again to regain access to your account, or a different one.",
)}
</div>
<div>
<AccessibleButton onClick={this.onLogin} kind="primary">
{_t("Sign in")}
</AccessibleButton>
</div> </div>
</AuthBody> </AuthBody>
</AuthPage> </AuthPage>

View File

@ -1586,12 +1586,15 @@
"You can now close this window or <a>log in</a> to your new account.": "You can now close this window or <a>log in</a> to your new account.", "You can now close this window or <a>log in</a> to your new account.": "You can now close this window or <a>log in</a> to your new account.",
"Registration Successful": "Registration Successful", "Registration Successful": "Registration Successful",
"Create your account": "Create your account", "Create your account": "Create your account",
"Failed to re-authenticate": "Failed to re-authenticate",
"Enter your password to sign in and regain access to your account.": "Enter your password to sign in and regain access to your account.",
"Forgotten your password?": "Forgotten your password?",
"Cannot re-authenticate with your account. Please contact your homeserver admin for more information.": "Cannot re-authenticate with your account. Please contact your homeserver admin for more information.",
"You're signed out": "You're signed out", "You're signed out": "You're signed out",
"Your homeserver (%(domainName)s) admin has signed you out of your account %(displayName)s (%(userId)s).": "Your homeserver (%(domainName)s) admin has signed you out of your account %(displayName)s (%(userId)s).", "Your homeserver (%(domainName)s) admin has signed you out of your account %(displayName)s (%(userId)s).": "Your homeserver (%(domainName)s) admin has signed you out of your account %(displayName)s (%(userId)s).",
"I don't want to sign in": "I don't want to sign in", "I don't want to sign in": "I don't want to sign in",
"If this is a shared device, or you don't want to access your account again from it, clear all data stored locally on this device.": "If this is a shared device, or you don't want to access your account again from it, clear all data stored locally on this device.", "If this is a shared device, or you don't want to access your account again from it, clear all data stored locally on this device.": "If this is a shared device, or you don't want to access your account again from it, clear all data stored locally on this device.",
"Clear all data": "Clear all data", "Clear all data": "Clear all data",
"Sign in again to regain access to your account, or a different one.": "Sign in again to regain access to your account, or a different one.",
"Commands": "Commands", "Commands": "Commands",
"Results from DuckDuckGo": "Results from DuckDuckGo", "Results from DuckDuckGo": "Results from DuckDuckGo",
"Emoji": "Emoji", "Emoji": "Emoji",