Merge pull request #3847 from matrix-org/jryans/cross-signing-setup
Add post-login complete security flow
This commit is contained in:
commit
89230395eb
9 changed files with 310 additions and 32 deletions
|
@ -28,6 +28,7 @@
|
||||||
@import "./structures/_TopLeftMenuButton.scss";
|
@import "./structures/_TopLeftMenuButton.scss";
|
||||||
@import "./structures/_UploadBar.scss";
|
@import "./structures/_UploadBar.scss";
|
||||||
@import "./structures/_ViewSource.scss";
|
@import "./structures/_ViewSource.scss";
|
||||||
|
@import "./structures/auth/_CompleteSecurity.scss";
|
||||||
@import "./structures/auth/_Login.scss";
|
@import "./structures/auth/_Login.scss";
|
||||||
@import "./views/auth/_AuthBody.scss";
|
@import "./views/auth/_AuthBody.scss";
|
||||||
@import "./views/auth/_AuthButtons.scss";
|
@import "./views/auth/_AuthButtons.scss";
|
||||||
|
|
51
res/css/structures/auth/_CompleteSecurity.scss
Normal file
51
res/css/structures/auth/_CompleteSecurity.scss
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_headerIcon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 0 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_heroIcon {
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_body {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_actionRow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.mx_AccessibleButton {
|
||||||
|
margin-inline-start: 18px;
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
color: $warning-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd.
|
Copyright 2017 Vector Creations Ltd.
|
||||||
Copyright 2017, 2018, 2019 New Vector Ltd
|
Copyright 2017, 2018, 2019 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -223,9 +223,10 @@ class _MatrixClientPeg {
|
||||||
};
|
};
|
||||||
|
|
||||||
opts.cryptoCallbacks = {};
|
opts.cryptoCallbacks = {};
|
||||||
if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
|
// These are always installed regardless of the labs flag so that
|
||||||
Object.assign(opts.cryptoCallbacks, crossSigningCallbacks);
|
// cross-signing features can toggle on without reloading and also be
|
||||||
}
|
// accessed immediately after login.
|
||||||
|
Object.assign(opts.cryptoCallbacks, crossSigningCallbacks);
|
||||||
|
|
||||||
this.matrixClient = createMatrixClient(opts);
|
this.matrixClient = createMatrixClient(opts);
|
||||||
|
|
||||||
|
|
|
@ -313,7 +313,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"Secret Storage will be set up using your existing key backup details. " +
|
"Secret Storage will be set up using your existing key backup details. " +
|
||||||
"Your secret storage passphrase and recovery key will be the same as " +
|
"Your secret storage passphrase and recovery key will be the same as " +
|
||||||
" they were for your key backup",
|
"they were for your key backup.",
|
||||||
)}</p>
|
)}</p>
|
||||||
<DialogButtons primaryButton={_t('Next')}
|
<DialogButtons primaryButton={_t('Next')}
|
||||||
onPrimaryButtonClick={this._onMigrateNextClick}
|
onPrimaryButtonClick={this._onMigrateNextClick}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2017-2019 New Vector Ltd
|
Copyright 2017-2019 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -21,6 +21,7 @@ import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
import createReactClass from 'create-react-class';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as Matrix from "matrix-js-sdk";
|
import * as Matrix from "matrix-js-sdk";
|
||||||
|
import { isCryptoAvailable } from 'matrix-js-sdk/src/crypto';
|
||||||
|
|
||||||
// focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss
|
// focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss
|
||||||
import 'focus-visible';
|
import 'focus-visible';
|
||||||
|
@ -79,18 +80,14 @@ export const VIEWS = {
|
||||||
// we are showing the registration view
|
// we are showing the registration view
|
||||||
REGISTER: 3,
|
REGISTER: 3,
|
||||||
|
|
||||||
// completeing the registration flow
|
// completing the registration flow
|
||||||
POST_REGISTRATION: 4,
|
POST_REGISTRATION: 4,
|
||||||
|
|
||||||
// showing the 'forgot password' view
|
// showing the 'forgot password' view
|
||||||
FORGOT_PASSWORD: 5,
|
FORGOT_PASSWORD: 5,
|
||||||
|
|
||||||
// we have valid matrix credentials (either via an explicit login, via the
|
// showing flow to trust this new device with cross-signing
|
||||||
// initial re-animation/guest registration, or via a registration), and are
|
COMPLETE_SECURITY: 6,
|
||||||
// now setting up a matrixclient to talk to it. This isn't an instant
|
|
||||||
// process because we need to clear out indexeddb. While it is going on we
|
|
||||||
// show a big spinner.
|
|
||||||
LOGGING_IN: 6,
|
|
||||||
|
|
||||||
// we are logged in with an active matrix client.
|
// we are logged in with an active matrix client.
|
||||||
LOGGED_IN: 7,
|
LOGGED_IN: 7,
|
||||||
|
@ -656,16 +653,12 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'on_logging_in':
|
|
||||||
// We are now logging in, so set the state to reflect that
|
|
||||||
// NB. This does not touch 'ready' since if our dispatches
|
|
||||||
// are delayed, the sync could already have completed
|
|
||||||
this.setStateForNewView({
|
|
||||||
view: VIEWS.LOGGING_IN,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'on_logged_in':
|
case 'on_logged_in':
|
||||||
if (!Lifecycle.isSoftLogout()) {
|
if (
|
||||||
|
!Lifecycle.isSoftLogout() &&
|
||||||
|
this.state.view !== VIEWS.LOGIN &&
|
||||||
|
this.state.view !== VIEWS.COMPLETE_SECURITY
|
||||||
|
) {
|
||||||
this._onLoggedIn();
|
this._onLoggedIn();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1169,7 +1162,7 @@ export default createReactClass({
|
||||||
if (this.props.config.welcomeUserId && getCurrentLanguage().startsWith("en")) {
|
if (this.props.config.welcomeUserId && getCurrentLanguage().startsWith("en")) {
|
||||||
const welcomeUserRoom = await this._startWelcomeUserChat();
|
const welcomeUserRoom = await this._startWelcomeUserChat();
|
||||||
if (welcomeUserRoom === null) {
|
if (welcomeUserRoom === null) {
|
||||||
// We didn't rediret to the welcome user room, so show
|
// We didn't redirect to the welcome user room, so show
|
||||||
// the homepage.
|
// the homepage.
|
||||||
dis.dispatch({action: 'view_home_page'});
|
dis.dispatch({action: 'view_home_page'});
|
||||||
}
|
}
|
||||||
|
@ -1573,6 +1566,10 @@ export default createReactClass({
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_my_groups',
|
action: 'view_my_groups',
|
||||||
});
|
});
|
||||||
|
} else if (screen === 'complete_security') {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'start_complete_security',
|
||||||
|
});
|
||||||
} else if (screen == 'post_registration') {
|
} else if (screen == 'post_registration') {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'start_post_registration',
|
action: 'start_post_registration',
|
||||||
|
@ -1822,21 +1819,69 @@ export default createReactClass({
|
||||||
this._loggedInView = ref;
|
this._loggedInView = ref;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async onUserCompletedLoginFlow(credentials) {
|
||||||
|
// Wait for the client to be logged in (but not started)
|
||||||
|
// which is enough to ask the server about account data.
|
||||||
|
const loggedIn = new Promise(resolve => {
|
||||||
|
const actionHandlerRef = dis.register(payload => {
|
||||||
|
if (payload.action !== "on_logged_in") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dis.unregister(actionHandlerRef);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create and start the client in the background
|
||||||
|
Lifecycle.setLoggedIn(credentials);
|
||||||
|
await loggedIn;
|
||||||
|
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
// We're checking `isCryptoAvailable` here instead of `isCryptoEnabled`
|
||||||
|
// because the client hasn't been started yet.
|
||||||
|
if (!isCryptoAvailable()) {
|
||||||
|
this._onLoggedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for the master cross-signing key in SSSS as a quick proxy for
|
||||||
|
// whether cross-signing has been set up on the account.
|
||||||
|
let masterKeyInStorage = false;
|
||||||
|
try {
|
||||||
|
masterKeyInStorage = !!await cli.getAccountDataFromServer("m.cross_signing.master");
|
||||||
|
} catch (e) {
|
||||||
|
if (e.errcode !== "M_NOT_FOUND") throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (masterKeyInStorage) {
|
||||||
|
this.setStateForNewView({ view: VIEWS.COMPLETE_SECURITY });
|
||||||
|
} else {
|
||||||
|
this._onLoggedIn();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onCompleteSecurityFinished() {
|
||||||
|
this._onLoggedIn();
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
||||||
|
|
||||||
let view;
|
let view;
|
||||||
|
|
||||||
if (
|
if (this.state.view === VIEWS.LOADING) {
|
||||||
this.state.view === VIEWS.LOADING ||
|
|
||||||
this.state.view === VIEWS.LOGGING_IN
|
|
||||||
) {
|
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
view = (
|
view = (
|
||||||
<div className="mx_MatrixChat_splash">
|
<div className="mx_MatrixChat_splash">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (this.state.view === VIEWS.COMPLETE_SECURITY) {
|
||||||
|
const CompleteSecurity = sdk.getComponent('structures.auth.CompleteSecurity');
|
||||||
|
view = (
|
||||||
|
<CompleteSecurity
|
||||||
|
onFinished={this.onCompleteSecurityFinished}
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else if (this.state.view === VIEWS.POST_REGISTRATION) {
|
} else if (this.state.view === VIEWS.POST_REGISTRATION) {
|
||||||
// needs to be before normal PageTypes as you are logged in technically
|
// needs to be before normal PageTypes as you are logged in technically
|
||||||
const PostRegistration = sdk.getComponent('structures.auth.PostRegistration');
|
const PostRegistration = sdk.getComponent('structures.auth.PostRegistration');
|
||||||
|
@ -1921,7 +1966,7 @@ export default createReactClass({
|
||||||
const Login = sdk.getComponent('structures.auth.Login');
|
const Login = sdk.getComponent('structures.auth.Login');
|
||||||
view = (
|
view = (
|
||||||
<Login
|
<Login
|
||||||
onLoggedIn={Lifecycle.setLoggedIn}
|
onLoggedIn={this.onUserCompletedLoginFlow}
|
||||||
onRegisterClick={this.onRegisterClick}
|
onRegisterClick={this.onRegisterClick}
|
||||||
fallbackHsUrl={this.getFallbackHsUrl()}
|
fallbackHsUrl={this.getFallbackHsUrl()}
|
||||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||||
|
|
173
src/components/structures/auth/CompleteSecurity.js
Normal file
173
src/components/structures/auth/CompleteSecurity.js
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import * as sdk from '../../../index';
|
||||||
|
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||||
|
import { accessSecretStorage } from '../../../CrossSigningManager';
|
||||||
|
|
||||||
|
const PHASE_INTRO = 0;
|
||||||
|
const PHASE_DONE = 1;
|
||||||
|
const PHASE_CONFIRM_SKIP = 2;
|
||||||
|
|
||||||
|
export default class CompleteSecurity extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
phase: PHASE_INTRO,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onStartClick = async () => {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
await accessSecretStorage(async () => {
|
||||||
|
await cli.checkOwnCrossSigningTrust();
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_DONE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSkipClick = () => {
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_CONFIRM_SKIP,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSkipConfirmClick = () => {
|
||||||
|
this.props.onFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSkipBackClick = () => {
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_INTRO,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDoneClick = () => {
|
||||||
|
this.props.onFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const AuthPage = sdk.getComponent("auth.AuthPage");
|
||||||
|
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||||
|
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||||
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
|
|
||||||
|
const {
|
||||||
|
phase,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
let icon;
|
||||||
|
let title;
|
||||||
|
let body;
|
||||||
|
if (phase === PHASE_INTRO) {
|
||||||
|
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning"></span>;
|
||||||
|
title = _t("Complete security");
|
||||||
|
body = (
|
||||||
|
<div>
|
||||||
|
<p>{_t(
|
||||||
|
"Verify this session to grant it access to encrypted messages.",
|
||||||
|
)}</p>
|
||||||
|
<div className="mx_CompleteSecurity_actionRow">
|
||||||
|
<AccessibleButton
|
||||||
|
kind="danger"
|
||||||
|
onClick={this.onSkipClick}
|
||||||
|
>
|
||||||
|
{_t("Skip")}
|
||||||
|
</AccessibleButton>
|
||||||
|
<AccessibleButton
|
||||||
|
kind="primary"
|
||||||
|
onClick={this.onStartClick}
|
||||||
|
>
|
||||||
|
{_t("Start")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (phase === PHASE_DONE) {
|
||||||
|
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_verified"></span>;
|
||||||
|
title = _t("Session verified");
|
||||||
|
body = (
|
||||||
|
<div>
|
||||||
|
<div className="mx_CompleteSecurity_heroIcon mx_E2EIcon_verified"></div>
|
||||||
|
<p>{_t(
|
||||||
|
"Your new session is now verified. It has access to your " +
|
||||||
|
"encrypted messages, and other users will see it as trusted.",
|
||||||
|
)}</p>
|
||||||
|
<div className="mx_CompleteSecurity_actionRow">
|
||||||
|
<AccessibleButton
|
||||||
|
kind="primary"
|
||||||
|
onClick={this.onDoneClick}
|
||||||
|
>
|
||||||
|
{_t("Done")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (phase === PHASE_CONFIRM_SKIP) {
|
||||||
|
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning"></span>;
|
||||||
|
title = _t("Are you sure?");
|
||||||
|
body = (
|
||||||
|
<div>
|
||||||
|
<p>{_t(
|
||||||
|
"Without completing security on this device, it won’t have " +
|
||||||
|
"access to encrypted messages.",
|
||||||
|
)}</p>
|
||||||
|
<div className="mx_CompleteSecurity_actionRow">
|
||||||
|
<AccessibleButton
|
||||||
|
className="warning"
|
||||||
|
kind="secondary"
|
||||||
|
onClick={this.onSkipConfirmClick}
|
||||||
|
>
|
||||||
|
{_t("Skip")}
|
||||||
|
</AccessibleButton>
|
||||||
|
<AccessibleButton
|
||||||
|
kind="danger"
|
||||||
|
onClick={this.onSkipBackClick}
|
||||||
|
>
|
||||||
|
{_t("Go Back")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown phase ${phase}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthPage>
|
||||||
|
<AuthHeader />
|
||||||
|
<AuthBody>
|
||||||
|
<h2 className="mx_CompleteSecurity_header">
|
||||||
|
{icon}
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
<div className="mx_CompleteSecurity_body">
|
||||||
|
{body}
|
||||||
|
</div>
|
||||||
|
</AuthBody>
|
||||||
|
</AuthPage>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,7 +66,7 @@ export default class SoftLogout extends React.Component {
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
// We've ended up here when we don't need to - navigate to login
|
// We've ended up here when we don't need to - navigate to login
|
||||||
if (!Lifecycle.isSoftLogout()) {
|
if (!Lifecycle.isSoftLogout()) {
|
||||||
dis.dispatch({action: "on_logged_in"});
|
dis.dispatch({action: "start_login"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1848,6 +1848,14 @@
|
||||||
"Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s",
|
"Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s",
|
||||||
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
|
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
|
||||||
"Could not load user profile": "Could not load user profile",
|
"Could not load user profile": "Could not load user profile",
|
||||||
|
"Complete security": "Complete security",
|
||||||
|
"Verify this session to grant it access to encrypted messages.": "Verify this session to grant it access to encrypted messages.",
|
||||||
|
"Start": "Start",
|
||||||
|
"Session verified": "Session verified",
|
||||||
|
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
|
||||||
|
"Done": "Done",
|
||||||
|
"Without completing security on this device, it won’t have access to encrypted messages.": "Without completing security on this device, it won’t have access to encrypted messages.",
|
||||||
|
"Go Back": "Go Back",
|
||||||
"Failed to send email": "Failed to send email",
|
"Failed to send email": "Failed to send email",
|
||||||
"The email address linked to your account must be entered.": "The email address linked to your account must be entered.",
|
"The email address linked to your account must be entered.": "The email address linked to your account must be entered.",
|
||||||
"A new password must be entered.": "A new password must be entered.",
|
"A new password must be entered.": "A new password must be entered.",
|
||||||
|
@ -1952,7 +1960,7 @@
|
||||||
"Import": "Import",
|
"Import": "Import",
|
||||||
"Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.": "Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.",
|
"Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.": "Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.",
|
||||||
"Restore": "Restore",
|
"Restore": "Restore",
|
||||||
"Secret Storage will be set up using your existing key backup details. Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Secret Storage will be set up using your existing key backup details. Your secret storage passphrase and recovery key will be the same as they were for your key backup",
|
"Secret Storage will be set up using your existing key backup details. Your secret storage passphrase and recovery key will be the same as they were for your key backup.": "Secret Storage will be set up using your existing key backup details. Your secret storage passphrase and recovery key will be the same as they were for your key backup.",
|
||||||
"Great! This passphrase looks strong enough.": "Great! This passphrase looks strong enough.",
|
"Great! This passphrase looks strong enough.": "Great! This passphrase looks strong enough.",
|
||||||
"<b>Warning</b>: You should only set up secret storage from a trusted computer.": "<b>Warning</b>: You should only set up secret storage from a trusted computer.",
|
"<b>Warning</b>: You should only set up secret storage from a trusted computer.": "<b>Warning</b>: You should only set up secret storage from a trusted computer.",
|
||||||
"We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.",
|
"We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Travis Ralston
|
Copyright 2017 Travis Ralston
|
||||||
Copyright 2018, 2019 New Vector Ltd.
|
Copyright 2018, 2019 New Vector Ltd.
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -153,7 +153,6 @@ export const SETTINGS = {
|
||||||
displayName: _td("Enable cross-signing to verify per-user instead of per-device (in development)"),
|
displayName: _td("Enable cross-signing to verify per-user instead of per-device (in development)"),
|
||||||
supportedLevels: LEVELS_FEATURE,
|
supportedLevels: LEVELS_FEATURE,
|
||||||
default: false,
|
default: false,
|
||||||
controller: new ReloadOnChangeController(),
|
|
||||||
},
|
},
|
||||||
"feature_event_indexing": {
|
"feature_event_indexing": {
|
||||||
isFeature: true,
|
isFeature: true,
|
||||||
|
|
Loading…
Reference in a new issue