mirror of
https://github.com/element-hq/element-web
synced 2024-11-24 18:25:49 +03:00
Verification nag toasts
Implement the three differenty cases for this session, and also fix ones for other sessions which had the wrong copy. Fixes https://github.com/vector-im/riot-web/issues/11220
This commit is contained in:
parent
988ae14d35
commit
8c5fd5c77e
4 changed files with 160 additions and 12 deletions
|
@ -24,6 +24,9 @@ function toastKey(device) {
|
||||||
return 'newsession_' + device.deviceId;
|
return 'newsession_' + device.deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
||||||
|
const THIS_DEVICE_TOAST_KEY = 'setupencryption';
|
||||||
|
|
||||||
export default class DeviceListener {
|
export default class DeviceListener {
|
||||||
static sharedInstance() {
|
static sharedInstance() {
|
||||||
if (!global.mx_DeviceListener) global.mx_DeviceListener = new DeviceListener();
|
if (!global.mx_DeviceListener) global.mx_DeviceListener = new DeviceListener();
|
||||||
|
@ -33,42 +36,114 @@ export default class DeviceListener {
|
||||||
constructor() {
|
constructor() {
|
||||||
// device IDs for which the user has dismissed the verify toast ('Later')
|
// device IDs for which the user has dismissed the verify toast ('Later')
|
||||||
this._dismissed = new Set();
|
this._dismissed = new Set();
|
||||||
|
// has the user dismissed any of the various nag toasts to setup encryption on this device?
|
||||||
|
this._dismissedThisDeviceToast = false;
|
||||||
|
|
||||||
|
// cache of the key backup info
|
||||||
|
this._keyBackupInfo = null;
|
||||||
|
this._keyBackupFetchedAt = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
MatrixClientPeg.get().on('crypto.devicesUpdated', this._onDevicesUpdated);
|
MatrixClientPeg.get().on('crypto.devicesUpdated', this._onDevicesUpdated);
|
||||||
MatrixClientPeg.get().on('deviceVerificationChanged', this._onDeviceVerificationChanged);
|
MatrixClientPeg.get().on('deviceVerificationChanged', this._onDeviceVerificationChanged);
|
||||||
this.recheck();
|
MatrixClientPeg.get().on('userTrustStatusChanged', this._onUserTrustStatusChanged);
|
||||||
|
this._recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener('crypto.devicesUpdated', this._onDevicesUpdated);
|
MatrixClientPeg.get().removeListener('crypto.devicesUpdated', this._onDevicesUpdated);
|
||||||
MatrixClientPeg.get().removeListener('deviceVerificationChanged', this._onDeviceVerificationChanged);
|
MatrixClientPeg.get().removeListener('deviceVerificationChanged', this._onDeviceVerificationChanged);
|
||||||
|
MatrixClientPeg.get().removeListener('userTrustStatusChanged', this._onUserTrustStatusChanged);
|
||||||
}
|
}
|
||||||
this._dismissed.clear();
|
this._dismissed.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
dismissVerification(deviceId) {
|
dismissVerification(deviceId) {
|
||||||
this._dismissed.add(deviceId);
|
this._dismissed.add(deviceId);
|
||||||
this.recheck();
|
this._recheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
dismissEncryptionSetup() {
|
||||||
|
this._dismissedThisDeviceToast = true;
|
||||||
|
this._recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDevicesUpdated = (users) => {
|
_onDevicesUpdated = (users) => {
|
||||||
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
||||||
this.recheck();
|
this._recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDeviceVerificationChanged = (users) => {
|
_onDeviceVerificationChanged = (users) => {
|
||||||
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
||||||
this.recheck();
|
this._recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
async recheck() {
|
_onUserTrustStatusChanged = (userId, trustLevel) => {
|
||||||
|
if (userId !== MatrixClientPeg.get().getUserId()) return;
|
||||||
|
this._recheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The server doesn't tell us when key backup is set up, so we poll
|
||||||
|
// & cache the result
|
||||||
|
async _getKeyBackupInfo() {
|
||||||
|
const now = (new Date()).getTime();
|
||||||
|
if (!this._keyBackupInfo || this._keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) {
|
||||||
|
this._keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||||
|
this._keyBackupFetchedAt = now;
|
||||||
|
}
|
||||||
|
return this._keyBackupInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _recheck() {
|
||||||
if (!SettingsStore.isFeatureEnabled("feature_cross_signing")) return;
|
if (!SettingsStore.isFeatureEnabled("feature_cross_signing")) return;
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
if (!cli.isCryptoEnabled()) return false;
|
if (!cli.isCryptoEnabled()) return;
|
||||||
|
if (!cli.getCrossSigningId()) {
|
||||||
|
if (this._dismissedThisDeviceToast) {
|
||||||
|
ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cross signing isn't enabled - nag to enable it
|
||||||
|
// There 3 different toasts for:
|
||||||
|
if (cli.getStoredCrossSigningForUser(cli.getUserId())) {
|
||||||
|
// Cross-signing on account but this device doesn't trust the master key (verify this session)
|
||||||
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
|
key: THIS_DEVICE_TOAST_KEY,
|
||||||
|
title: _t("Verify this Session"),
|
||||||
|
icon: "verification_warning",
|
||||||
|
props: {kind: 'verify_this_session'},
|
||||||
|
component: sdk.getComponent("toasts.SetupEncryptionToast"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const backupInfo = await this._getKeyBackupInfo();
|
||||||
|
if (backupInfo) {
|
||||||
|
// No cross-signing on account but key backup available (upgrade encryption)
|
||||||
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
|
key: THIS_DEVICE_TOAST_KEY,
|
||||||
|
title: _t("Encryption upgrade available"),
|
||||||
|
icon: "verification_warning",
|
||||||
|
props: {kind: 'upgrade_encryption'},
|
||||||
|
component: sdk.getComponent("toasts.SetupEncryptionToast"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No cross-signing or key backup on account (set up encryption)
|
||||||
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
|
key: THIS_DEVICE_TOAST_KEY,
|
||||||
|
title: _t("Set up encryption"),
|
||||||
|
icon: "verification_warning",
|
||||||
|
props: {kind: 'set_up_encryption'},
|
||||||
|
component: sdk.getComponent("toasts.SetupEncryptionToast"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
const devices = await cli.getStoredDevicesForUser(cli.getUserId());
|
const devices = await cli.getStoredDevicesForUser(cli.getUserId());
|
||||||
for (const device of devices) {
|
for (const device of devices) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default class VerifySessionToast extends React.PureComponent {
|
||||||
DeviceListener.sharedInstance().dismissVerification(this.props.deviceId);
|
DeviceListener.sharedInstance().dismissVerification(this.props.deviceId);
|
||||||
};
|
};
|
||||||
|
|
||||||
_onVerifyClick = async () => {
|
_onReviewClick = async () => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
|
const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog');
|
||||||
|
|
||||||
|
@ -47,10 +47,10 @@ export default class VerifySessionToast extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const FormButton = sdk.getComponent("elements.FormButton");
|
const FormButton = sdk.getComponent("elements.FormButton");
|
||||||
return (<div>
|
return (<div>
|
||||||
<div className="mx_Toast_description">{_t("Other users may not trust it")}</div>
|
<div className="mx_Toast_description">{_t("Review & verify your new session")}</div>
|
||||||
<div className="mx_Toast_buttons" aria-live="off">
|
<div className="mx_Toast_buttons" aria-live="off">
|
||||||
<FormButton label={_t("Later")} kind="danger" onClick={this._onLaterClick} />
|
<FormButton label={_t("Later")} kind="danger" onClick={this._onLaterClick} />
|
||||||
<FormButton label={_t("Verify")} onClick={this._onVerifyClick} />
|
<FormButton label={_t("Review")} onClick={this._onReviewClick} />
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
68
src/components/views/toasts/SetupEncryptionToast.js
Normal file
68
src/components/views/toasts/SetupEncryptionToast.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
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 * as sdk from "../../../index";
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import DeviceListener from '../../../DeviceListener';
|
||||||
|
import { accessSecretStorage } from '../../../CrossSigningManager';
|
||||||
|
|
||||||
|
export default class SetupEncryptionToast extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
toastKey: PropTypes.string.isRequired,
|
||||||
|
kind: PropTypes.oneOf(['set_up_encryption', 'verify_this_session', 'upgrade_encryption']).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
_onLaterClick = () => {
|
||||||
|
DeviceListener.sharedInstance().dismissEncryptionSetup();
|
||||||
|
};
|
||||||
|
|
||||||
|
_onSetupClick = async () => {
|
||||||
|
accessSecretStorage();
|
||||||
|
};
|
||||||
|
|
||||||
|
getDescription() {
|
||||||
|
switch (this.props.kind) {
|
||||||
|
case 'set_up_encryption':
|
||||||
|
case 'upgrade_encryption':
|
||||||
|
return _t('Verify your other devices easier');
|
||||||
|
case 'verify_this_session':
|
||||||
|
return _t('Other users may not trust it');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSetupCaption() {
|
||||||
|
switch (this.props.kind) {
|
||||||
|
case 'set_up_encryption':
|
||||||
|
case 'upgrade_encryption':
|
||||||
|
return _t('Upgrade');
|
||||||
|
case 'verify_this_session':
|
||||||
|
return _t('Verify');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const FormButton = sdk.getComponent("elements.FormButton");
|
||||||
|
return (<div>
|
||||||
|
<div className="mx_Toast_description">{this.getDescription()}</div>
|
||||||
|
<div className="mx_Toast_buttons" aria-live="off">
|
||||||
|
<FormButton label={_t("Later")} kind="danger" onClick={this._onLaterClick} />
|
||||||
|
<FormButton label={this.getSetupCaption()} onClick={this._onSetupClick} />
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
}
|
|
@ -88,6 +88,9 @@
|
||||||
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
|
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(time)s",
|
||||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
|
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s",
|
||||||
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s",
|
||||||
|
"Verify this Session": "Verify this Session",
|
||||||
|
"Encryption upgrade available": "Encryption upgrade available",
|
||||||
|
"Set up encryption": "Set up encryption",
|
||||||
"New Session": "New Session",
|
"New Session": "New Session",
|
||||||
"Who would you like to add to this community?": "Who would you like to add to this community?",
|
"Who would you like to add to this community?": "Who would you like to add to this community?",
|
||||||
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID",
|
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID",
|
||||||
|
@ -509,8 +512,12 @@
|
||||||
"Headphones": "Headphones",
|
"Headphones": "Headphones",
|
||||||
"Folder": "Folder",
|
"Folder": "Folder",
|
||||||
"Pin": "Pin",
|
"Pin": "Pin",
|
||||||
"Other users may not trust it": "Other users may not trust it",
|
"Review & verify your new session": "Review & verify your new session",
|
||||||
"Later": "Later",
|
"Later": "Later",
|
||||||
|
"Review": "Review",
|
||||||
|
"Verify your other devices easier": "Verify your other devices easier",
|
||||||
|
"Other users may not trust it": "Other users may not trust it",
|
||||||
|
"Upgrade": "Upgrade",
|
||||||
"Verify": "Verify",
|
"Verify": "Verify",
|
||||||
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
"Decline (%(counter)s)": "Decline (%(counter)s)",
|
||||||
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
|
||||||
|
@ -1514,7 +1521,6 @@
|
||||||
"Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.",
|
"Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.",
|
||||||
"This usually only affects how the room is processed on the server. If you're having problems with your Riot, please <a>report a bug</a>.": "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please <a>report a bug</a>.",
|
"This usually only affects how the room is processed on the server. If you're having problems with your Riot, please <a>report a bug</a>.": "This usually only affects how the room is processed on the server. If you're having problems with your Riot, please <a>report a bug</a>.",
|
||||||
"You'll upgrade this room from <oldVersion /> to <newVersion />.": "You'll upgrade this room from <oldVersion /> to <newVersion />.",
|
"You'll upgrade this room from <oldVersion /> to <newVersion />.": "You'll upgrade this room from <oldVersion /> to <newVersion />.",
|
||||||
"Upgrade": "Upgrade",
|
|
||||||
"Sign out and remove encryption keys?": "Sign out and remove encryption keys?",
|
"Sign out and remove encryption keys?": "Sign out and remove encryption keys?",
|
||||||
"Clear Storage and Sign Out": "Clear Storage and Sign Out",
|
"Clear Storage and Sign Out": "Clear Storage and Sign Out",
|
||||||
"Send Logs": "Send Logs",
|
"Send Logs": "Send Logs",
|
||||||
|
@ -2008,7 +2014,6 @@
|
||||||
"Set up secret storage": "Set up secret storage",
|
"Set up secret storage": "Set up secret storage",
|
||||||
"Restore your Key Backup": "Restore your Key Backup",
|
"Restore your Key Backup": "Restore your Key Backup",
|
||||||
"Upgrade your encryption": "Upgrade your encryption",
|
"Upgrade your encryption": "Upgrade your encryption",
|
||||||
"Set up encryption": "Set up encryption",
|
|
||||||
"Recovery key": "Recovery key",
|
"Recovery key": "Recovery key",
|
||||||
"Keep it safe": "Keep it safe",
|
"Keep it safe": "Keep it safe",
|
||||||
"Storing secrets...": "Storing secrets...",
|
"Storing secrets...": "Storing secrets...",
|
||||||
|
|
Loading…
Reference in a new issue