CreateSecretStorageDialog
: stop using deprecated APIs (#11635)
* Add some tests for `CreateSecretStorageDialog` * CreateSecretStorageDialog: stop using deprecated APIs
This commit is contained in:
parent
11f258e62e
commit
6fd46f3bc8
5 changed files with 830 additions and 28 deletions
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Copyright 2018, 2019 New Vector Ltd
|
||||
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2019, 2020, 2023 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.
|
||||
|
@ -18,12 +18,11 @@ limitations under the License.
|
|||
import React, { createRef } from "react";
|
||||
import FileSaver from "file-saver";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
import { TrustInfo } from "matrix-js-sdk/src/crypto/backup";
|
||||
import { CrossSigningKeys, IAuthDict, MatrixError, UIAFlow, UIAResponse } from "matrix-js-sdk/src/matrix";
|
||||
import { AuthDict, CrossSigningKeys, MatrixError, UIAFlow, UIAResponse } from "matrix-js-sdk/src/matrix";
|
||||
import { IRecoveryKey } from "matrix-js-sdk/src/crypto/api";
|
||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
||||
import classNames from "classnames";
|
||||
import { BackupTrustInfo, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
import { _t, _td } from "../../../../languageHandler";
|
||||
|
@ -81,8 +80,25 @@ interface IState {
|
|||
copied: boolean;
|
||||
downloaded: boolean;
|
||||
setPassphrase: boolean;
|
||||
backupInfo: IKeyBackupInfo | null;
|
||||
backupSigStatus: TrustInfo | null;
|
||||
|
||||
/** Information on the current key backup version, as returned by the server.
|
||||
*
|
||||
* `null` could mean any of:
|
||||
* * we haven't yet requested the data from the server.
|
||||
* * we were unable to reach the server.
|
||||
* * the server returned key backup version data we didn't understand or was malformed.
|
||||
* * there is actually no backup on the server.
|
||||
*/
|
||||
backupInfo: KeyBackupInfo | null;
|
||||
|
||||
/**
|
||||
* Information on whether the backup in `backupInfo` is correctly signed, and whether we have the right key to
|
||||
* decrypt it.
|
||||
*
|
||||
* `undefined` if `backupInfo` is null, or if crypto is not enabled in the client.
|
||||
*/
|
||||
backupTrustInfo: BackupTrustInfo | undefined;
|
||||
|
||||
// does the server offer a UI auth flow with just m.login.password
|
||||
// for /keys/device_signing/upload?
|
||||
canUploadKeysWithPasswordOnly: boolean | null;
|
||||
|
@ -144,7 +160,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
downloaded: false,
|
||||
setPassphrase: false,
|
||||
backupInfo: null,
|
||||
backupSigStatus: null,
|
||||
backupTrustInfo: undefined,
|
||||
// does the server offer a UI auth flow with just m.login.password
|
||||
// for /keys/device_signing/upload?
|
||||
accountPasswordCorrect: null,
|
||||
|
@ -177,13 +193,21 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
this.fetchBackupInfo();
|
||||
}
|
||||
|
||||
private async fetchBackupInfo(): Promise<{ backupInfo?: IKeyBackupInfo; backupSigStatus?: TrustInfo }> {
|
||||
/**
|
||||
* Attempt to get information on the current backup from the server, and update the state.
|
||||
*
|
||||
* Updates {@link IState.backupInfo} and {@link IState.backupTrustInfo}, and picks an appropriate phase for
|
||||
* {@link IState.phase}.
|
||||
*
|
||||
* @returns If the backup data was retrieved successfully, the trust info for the backup. Otherwise, undefined.
|
||||
*/
|
||||
private async fetchBackupInfo(): Promise<BackupTrustInfo | undefined> {
|
||||
try {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const backupInfo = await cli.getKeyBackupVersion();
|
||||
const backupSigStatus =
|
||||
const backupTrustInfo =
|
||||
// we may not have started crypto yet, in which case we definitely don't trust the backup
|
||||
backupInfo && cli.isCryptoEnabled() ? await cli.isKeyBackupTrusted(backupInfo) : null;
|
||||
backupInfo ? await cli.getCrypto()?.isKeyBackupTrusted(backupInfo) : undefined;
|
||||
|
||||
const { forceReset } = this.props;
|
||||
const phase = backupInfo && !forceReset ? Phase.Migrate : Phase.ChooseKeyPassphrase;
|
||||
|
@ -191,16 +215,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
this.setState({
|
||||
phase,
|
||||
backupInfo,
|
||||
backupSigStatus,
|
||||
backupTrustInfo,
|
||||
});
|
||||
|
||||
return {
|
||||
backupInfo: backupInfo ?? undefined,
|
||||
backupSigStatus: backupSigStatus ?? undefined,
|
||||
};
|
||||
return backupTrustInfo;
|
||||
} catch (e) {
|
||||
console.error("Error fetching backup data from server", e);
|
||||
this.setState({ phase: Phase.LoadError });
|
||||
return {};
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,7 +259,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
private onChooseKeyPassphraseFormSubmit = async (): Promise<void> => {
|
||||
if (this.state.passPhraseKeySelected === SecureBackupSetupMethod.Key) {
|
||||
this.recoveryKey = await MatrixClientPeg.safeGet().createRecoveryKeyFromPassphrase();
|
||||
this.recoveryKey = await MatrixClientPeg.safeGet().getCrypto()!.createRecoveryKeyFromPassphrase();
|
||||
this.setState({
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
|
@ -255,7 +277,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
private onMigrateFormSubmit = (e: React.FormEvent): void => {
|
||||
e.preventDefault();
|
||||
if (this.state.backupSigStatus?.usable) {
|
||||
if (this.state.backupTrustInfo?.trusted) {
|
||||
this.bootstrapSecretStorage();
|
||||
} else {
|
||||
this.restoreBackup();
|
||||
|
@ -284,7 +306,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
};
|
||||
|
||||
private doBootstrapUIAuth = async (
|
||||
makeRequest: (authData: IAuthDict) => Promise<UIAResponse<void>>,
|
||||
makeRequest: (authData: AuthDict) => Promise<UIAResponse<void>>,
|
||||
): Promise<void> => {
|
||||
if (this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
|
||||
await makeRequest({
|
||||
|
@ -337,13 +359,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
});
|
||||
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const crypto = cli.getCrypto()!;
|
||||
|
||||
const { forceReset } = this.props;
|
||||
|
||||
try {
|
||||
if (forceReset) {
|
||||
logger.log("Forcing secret storage reset");
|
||||
await cli.bootstrapSecretStorage({
|
||||
await crypto.bootstrapSecretStorage({
|
||||
createSecretStorageKey: async () => this.recoveryKey!,
|
||||
setupNewKeyBackup: true,
|
||||
setupNewSecretStorage: true,
|
||||
|
@ -356,10 +379,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
// * SSO authentication users which require interactive auth to upload
|
||||
// keys (and also happen to skip all post-authentication flows at the
|
||||
// moment via token login)
|
||||
await cli.bootstrapCrossSigning({
|
||||
await crypto.bootstrapCrossSigning({
|
||||
authUploadDeviceSigningKeys: this.doBootstrapUIAuth,
|
||||
});
|
||||
await cli.bootstrapSecretStorage({
|
||||
await crypto.bootstrapSecretStorage({
|
||||
createSecretStorageKey: async () => this.recoveryKey!,
|
||||
keyBackupInfo: this.state.backupInfo!,
|
||||
setupNewKeyBackup: !this.state.backupInfo,
|
||||
|
@ -420,8 +443,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
);
|
||||
|
||||
await finished;
|
||||
const { backupSigStatus } = await this.fetchBackupInfo();
|
||||
if (backupSigStatus?.usable && this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
|
||||
const backupTrustInfo = await this.fetchBackupInfo();
|
||||
if (backupTrustInfo?.trusted && this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
|
||||
this.bootstrapSecretStorage();
|
||||
}
|
||||
};
|
||||
|
@ -462,7 +485,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
|
||||
|
||||
this.recoveryKey = await MatrixClientPeg.safeGet().createRecoveryKeyFromPassphrase(this.state.passPhrase);
|
||||
this.recoveryKey = await MatrixClientPeg.safeGet()
|
||||
.getCrypto()!
|
||||
.createRecoveryKeyFromPassphrase(this.state.passPhrase);
|
||||
this.setState({
|
||||
copied: false,
|
||||
downloaded: false,
|
||||
|
@ -585,6 +610,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
<div>{_t("Enter your account password to confirm the upgrade:")}</div>
|
||||
<div>
|
||||
<Field
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
type="password"
|
||||
label={_t("common|password")}
|
||||
value={this.state.accountPassword}
|
||||
|
@ -595,7 +621,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (!this.state.backupSigStatus?.usable) {
|
||||
} else if (!this.state.backupTrustInfo?.trusted) {
|
||||
authPrompt = (
|
||||
<div>
|
||||
<div>{_t("Restore your key backup to upgrade your encryption")}</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue