Invoke Secure Backup flow inside the app when requested by HS

If the Secure Backup required mode is set the client `.well-known` file, then
this will ensure that everyone already inside the app is required to complete
setup matching that policy.

Fixes https://github.com/vector-im/element-web/issues/14954
This commit is contained in:
J. Ryan Stinnett 2020-08-19 16:13:29 +01:00
parent e8e691b746
commit e56a61ec68
4 changed files with 42 additions and 2 deletions

View file

@ -27,10 +27,12 @@ import {ModalManager} from "../Modal";
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import {ActiveRoomObserver} from "../ActiveRoomObserver"; import {ActiveRoomObserver} from "../ActiveRoomObserver";
import {Notifier} from "../Notifier"; import {Notifier} from "../Notifier";
import type {Renderer} from "react-dom";
declare global { declare global {
interface Window { interface Window {
Modernizr: ModernizrStatic; Modernizr: ModernizrStatic;
matrixChat: ReturnType<Renderer>;
mxMatrixClientPeg: IMatrixClientPeg; mxMatrixClientPeg: IMatrixClientPeg;
Olm: { Olm: {
init: () => Promise<void>; init: () => Promise<void>;

View file

@ -15,6 +15,7 @@ limitations under the License.
*/ */
import {MatrixClientPeg} from './MatrixClientPeg'; import {MatrixClientPeg} from './MatrixClientPeg';
import dis from "./dispatcher/dispatcher";
import { import {
hideToast as hideBulkUnverifiedSessionsToast, hideToast as hideBulkUnverifiedSessionsToast,
showToast as showBulkUnverifiedSessionsToast, showToast as showBulkUnverifiedSessionsToast,
@ -29,11 +30,15 @@ import {
showToast as showUnverifiedSessionsToast, showToast as showUnverifiedSessionsToast,
} from "./toasts/UnverifiedSessionToast"; } from "./toasts/UnverifiedSessionToast";
import { privateShouldBeEncrypted } from "./createRoom"; import { privateShouldBeEncrypted } from "./createRoom";
import { isSecretStorageBeingAccessed } from "./CrossSigningManager"; import { isSecretStorageBeingAccessed, accessSecretStorage } from "./CrossSigningManager";
import { ensureClientWellKnown, isSecureBackupRequired } from './utils/WellKnownUtils';
import { isLoggedIn } from './components/structures/MatrixChat';
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000; const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
export default class DeviceListener { export default class DeviceListener {
private dispatcherRef: string;
// device IDs for which the user has dismissed the verify toast ('Later') // device IDs for which the user has dismissed the verify toast ('Later')
private dismissed = new Set<string>(); private dismissed = new Set<string>();
// has the user dismissed any of the various nag toasts to setup encryption on this device? // has the user dismissed any of the various nag toasts to setup encryption on this device?
@ -61,6 +66,7 @@ export default class DeviceListener {
MatrixClientPeg.get().on('crossSigning.keysChanged', this._onCrossSingingKeysChanged); MatrixClientPeg.get().on('crossSigning.keysChanged', this._onCrossSingingKeysChanged);
MatrixClientPeg.get().on('accountData', this._onAccountData); MatrixClientPeg.get().on('accountData', this._onAccountData);
MatrixClientPeg.get().on('sync', this._onSync); MatrixClientPeg.get().on('sync', this._onSync);
this.dispatcherRef = dis.register(this._onAction);
this._recheck(); this._recheck();
} }
@ -74,6 +80,10 @@ export default class DeviceListener {
MatrixClientPeg.get().removeListener('accountData', this._onAccountData); MatrixClientPeg.get().removeListener('accountData', this._onAccountData);
MatrixClientPeg.get().removeListener('sync', this._onSync); MatrixClientPeg.get().removeListener('sync', this._onSync);
} }
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef);
this.dispatcherRef = null;
}
this.dismissed.clear(); this.dismissed.clear();
this.dismissedThisDeviceToast = false; this.dismissedThisDeviceToast = false;
this.keyBackupInfo = null; this.keyBackupInfo = null;
@ -159,6 +169,11 @@ export default class DeviceListener {
if (state === 'PREPARED' && prevState === null) this._recheck(); if (state === 'PREPARED' && prevState === null) this._recheck();
}; };
_onAction = ({ action }) => {
if (action !== "on_logged_in") return;
this._recheck();
};
// The server doesn't tell us when key backup is set up, so we poll // The server doesn't tell us when key backup is set up, so we poll
// & cache the result // & cache the result
async _getKeyBackupInfo() { async _getKeyBackupInfo() {
@ -211,10 +226,18 @@ export default class DeviceListener {
showSetupEncryptionToast(SetupKind.UPGRADE_ENCRYPTION); showSetupEncryptionToast(SetupKind.UPGRADE_ENCRYPTION);
} else { } else {
// No cross-signing or key backup on account (set up encryption) // No cross-signing or key backup on account (set up encryption)
await ensureClientWellKnown();
if (isSecureBackupRequired() && isLoggedIn()) {
// If we're meant to set up, and Secure Backup is required,
// trigger the flow directly without a toast once logged in.
hideSetupEncryptionToast();
accessSecretStorage();
} else {
showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION); showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
} }
} }
} }
}
// This needs to be done after awaiting on downloadKeys() above, so // This needs to be done after awaiting on downloadKeys() above, so
// we make sure we get the devices after the fetch is done. // we make sure we get the devices after the fetch is done.

View file

@ -2085,3 +2085,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
</ErrorBoundary>; </ErrorBoundary>;
} }
} }
export function isLoggedIn(): boolean {
// JRS: Maybe we should move the step that writes this to the window out of
// `element-web` and into this file?
const app = window.matrixChat;
return app && (app as MatrixChat).state.view === Views.LOGGED_IN;
}

View file

@ -23,6 +23,14 @@ export interface IE2EEWellKnown {
default?: boolean; default?: boolean;
} }
export async function ensureClientWellKnown() {
const cli = MatrixClientPeg.get();
if (cli.haveAttemptedFetchingClientWellKnown()) return;
return new Promise(resolve => {
cli.once("WellKnown.attempted", resolve);
});
}
export function getE2EEWellKnown(): IE2EEWellKnown { export function getE2EEWellKnown(): IE2EEWellKnown {
const clientWellKnown = MatrixClientPeg.get().getClientWellKnown(); const clientWellKnown = MatrixClientPeg.get().getClientWellKnown();
if (clientWellKnown && clientWellKnown[E2EE_WK_KEY]) { if (clientWellKnown && clientWellKnown[E2EE_WK_KEY]) {