From cd7cf86b9614d46c1f508219cd19971bf5239a3e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 19 Dec 2024 11:55:05 +0000 Subject: [PATCH] Remove AccountPasswordStore and related flows (#28750) * Remove AccountPasswordStore and related flows As they are no longer needed since MSC3967 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Improve coverage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update src/CreateCrossSigning.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update comment Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- src/CreateCrossSigning.ts | 60 ++++--------------- src/SecurityManager.ts | 2 - src/components/structures/MatrixChat.tsx | 12 +--- src/components/structures/auth/Login.tsx | 7 +-- .../structures/auth/Registration.tsx | 22 +++---- src/contexts/SDKContext.ts | 9 --- src/stores/AccountPasswordStore.ts | 35 ----------- src/stores/InitialCryptoSetupStore.ts | 32 ++-------- src/stores/SetupEncryptionStore.ts | 2 - test/CreateCrossSigning-test.ts | 50 ++++++++-------- .../stores/AccountPasswordStore-test.ts | 53 ---------------- .../stores/InitialCryptoSetupStore-test.ts | 36 +++++------ .../stores/SetupEncryptionStore-test.ts | 8 --- 13 files changed, 71 insertions(+), 257 deletions(-) delete mode 100644 src/stores/AccountPasswordStore.ts delete mode 100644 test/unit-tests/stores/AccountPasswordStore-test.ts diff --git a/src/CreateCrossSigning.ts b/src/CreateCrossSigning.ts index c38f1a3dd57..19b2977a9f9 100644 --- a/src/CreateCrossSigning.ts +++ b/src/CreateCrossSigning.ts @@ -7,60 +7,25 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import { logger } from "matrix-js-sdk/src/logger"; -import { AuthDict, CrossSigningKeys, MatrixClient, MatrixError, UIAFlow, UIAResponse } from "matrix-js-sdk/src/matrix"; +import { AuthDict, MatrixClient, MatrixError, UIAResponse } from "matrix-js-sdk/src/matrix"; import { SSOAuthEntry } from "./components/views/auth/InteractiveAuthEntryComponents"; import Modal from "./Modal"; import { _t } from "./languageHandler"; import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog"; -/** - * Determine if the homeserver allows uploading device keys with only password auth, or with no auth at - * all (ie. if the homeserver supports MSC3967). - * @param cli The Matrix Client to use - * @returns True if the homeserver allows uploading device keys with only password auth or with no auth - * at all, otherwise false - */ -async function canUploadKeysWithPasswordOnly(cli: MatrixClient): Promise { - try { - await cli.uploadDeviceSigningKeys(undefined, {} as CrossSigningKeys); - // If we get here, it's because the server is allowing us to upload keys without - // auth the first time due to MSC3967. Therefore, yes, we can upload keys - // (with or without password, technically, but that's fine). - return true; - } catch (error) { - if (!(error instanceof MatrixError) || !error.data || !error.data.flows) { - logger.log("uploadDeviceSigningKeys advertised no flows!"); - return false; - } - const canUploadKeysWithPasswordOnly = error.data.flows.some((f: UIAFlow) => { - return f.stages.length === 1 && f.stages[0] === "m.login.password"; - }); - return canUploadKeysWithPasswordOnly; - } -} - /** * Ensures that cross signing keys are created and uploaded for the user. * The homeserver may require user-interactive auth to upload the keys, in - * which case the user will be prompted to authenticate. If the homeserver - * allows uploading keys with just an account password and one is provided, - * the keys will be uploaded without user interaction. + * which case the user will be prompted to authenticate. * * This function does not set up backups of the created cross-signing keys * (or message keys): the cross-signing keys are stored locally and will be * lost requiring a crypto reset, if the user logs out or loses their session. * * @param cli The Matrix Client to use - * @param isTokenLogin True if the user logged in via a token login, otherwise false - * @param accountPassword The password that the user logged in with */ -export async function createCrossSigning( - cli: MatrixClient, - isTokenLogin: boolean, - accountPassword?: string, -): Promise { +export async function createCrossSigning(cli: MatrixClient): Promise { const cryptoApi = cli.getCrypto(); if (!cryptoApi) { throw new Error("No crypto API found!"); @@ -69,19 +34,14 @@ export async function createCrossSigning( const doBootstrapUIAuth = async ( makeRequest: (authData: AuthDict) => Promise>, ): Promise => { - if (accountPassword && (await canUploadKeysWithPasswordOnly(cli))) { - await makeRequest({ - type: "m.login.password", - identifier: { - type: "m.id.user", - user: cli.getUserId(), - }, - password: accountPassword, - }); - } else if (isTokenLogin) { - // We are hoping the grace period is active + try { await makeRequest({}); - } else { + } catch (error) { + if (!(error instanceof MatrixError) || !error.data || !error.data.flows) { + // Not a UIA response + throw error; + } + const dialogAesthetics = { [SSOAuthEntry.PHASE_PREAUTH]: { title: _t("auth|uia|sso_title"), diff --git a/src/SecurityManager.ts b/src/SecurityManager.ts index e8122b2dbf2..10672917be4 100644 --- a/src/SecurityManager.ts +++ b/src/SecurityManager.ts @@ -191,8 +191,6 @@ export interface AccessSecretStorageOpts { forceReset?: boolean; /** Create new cross-signing keys. Only applicable if `forceReset` is `true`. */ resetCrossSigning?: boolean; - /** The cached account password, if available. */ - accountPassword?: string; } /** diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index ee120c430ae..1a6abbbadd3 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -431,8 +431,6 @@ export default class MatrixChat extends React.PureComponent { // if cross-signing is not yet set up, do so now if possible. InitialCryptoSetupStore.sharedInstance().startInitialCryptoSetup( cli, - Boolean(this.tokenLogin), - this.stores, this.onCompleteSecurityE2eSetupFinished, ); this.setStateForNewView({ view: Views.E2E_SETUP }); @@ -504,8 +502,6 @@ export default class MatrixChat extends React.PureComponent { UIStore.destroy(); this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize); window.removeEventListener("resize", this.onWindowResized); - - this.stores.accountPasswordStore.clearPassword(); } private onWindowResized = (): void => { @@ -1935,8 +1931,8 @@ export default class MatrixChat extends React.PureComponent { this.showScreen("forgot_password"); }; - private onRegisterFlowComplete = (credentials: IMatrixClientCreds, password: string): Promise => { - return this.onUserCompletedLoginFlow(credentials, password); + private onRegisterFlowComplete = (credentials: IMatrixClientCreds): Promise => { + return this.onUserCompletedLoginFlow(credentials); }; // returns a promise which resolves to the new MatrixClient @@ -2003,9 +1999,7 @@ export default class MatrixChat extends React.PureComponent { * Note: SSO users (and any others using token login) currently do not pass through * this, as they instead jump straight into the app after `attemptTokenLogin`. */ - private onUserCompletedLoginFlow = async (credentials: IMatrixClientCreds, password: string): Promise => { - this.stores.accountPasswordStore.setPassword(password); - + private onUserCompletedLoginFlow = async (credentials: IMatrixClientCreds): Promise => { // Create and start the client await Lifecycle.setLoggedIn(credentials); await this.postLoginSetup(); diff --git a/src/components/structures/auth/Login.tsx b/src/components/structures/auth/Login.tsx index 0a14450e63a..d9c853bc3a5 100644 --- a/src/components/structures/auth/Login.tsx +++ b/src/components/structures/auth/Login.tsx @@ -48,10 +48,7 @@ interface IProps { // Called when the user has logged in. Params: // - The object returned by the login API - // - The user's password, if applicable, (may be cached in memory for a - // short time so the user is not required to re-enter their password - // for operations like uploading cross-signing keys). - onLoggedIn(data: IMatrixClientCreds, password: string): void; + onLoggedIn(data: IMatrixClientCreds): void; // login shouldn't know or care how registration, password recovery, etc is done. onRegisterClick(): void; @@ -199,7 +196,7 @@ export default class LoginComponent extends React.PureComponent this.loginLogic.loginViaPassword(username, phoneCountry, phoneNumber, password).then( (data) => { this.setState({ serverIsAlive: true }); // it must be, we logged in. - this.props.onLoggedIn(data, password); + this.props.onLoggedIn(data); }, (error) => { if (this.unmounted) return; diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 0ae5c933460..91fe5c5faa0 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -72,10 +72,7 @@ interface IProps { mobileRegister?: boolean; // Called when the user has logged in. Params: // - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken - // - The user's password, if available and applicable (may be cached in memory - // for a short time so the user is not required to re-enter their password - // for operations like uploading cross-signing keys). - onLoggedIn(params: IMatrixClientCreds, password: string): Promise; + onLoggedIn(params: IMatrixClientCreds): Promise; // registration shouldn't know or care how login is done. onLoginClick(): void; onServerConfigChange(config: ValidatedServerConfig): void; @@ -431,16 +428,13 @@ export default class Registration extends React.Component { newState.busy = false; newState.completedNoSignin = true; } else { - await this.props.onLoggedIn( - { - userId, - deviceId: (response as RegisterResponse).device_id!, - homeserverUrl: this.state.matrixClient.getHomeserverUrl(), - identityServerUrl: this.state.matrixClient.getIdentityServerUrl(), - accessToken, - }, - this.state.formVals.password!, - ); + await this.props.onLoggedIn({ + userId, + deviceId: (response as RegisterResponse).device_id!, + homeserverUrl: this.state.matrixClient.getHomeserverUrl(), + identityServerUrl: this.state.matrixClient.getIdentityServerUrl(), + accessToken, + }); this.setupPushers(); } diff --git a/src/contexts/SDKContext.ts b/src/contexts/SDKContext.ts index fe736615545..d77cd1e804e 100644 --- a/src/contexts/SDKContext.ts +++ b/src/contexts/SDKContext.ts @@ -13,7 +13,6 @@ import defaultDispatcher from "../dispatcher/dispatcher"; import LegacyCallHandler from "../LegacyCallHandler"; import { PosthogAnalytics } from "../PosthogAnalytics"; import { SlidingSyncManager } from "../SlidingSyncManager"; -import { AccountPasswordStore } from "../stores/AccountPasswordStore"; import { MemberListStore } from "../stores/MemberListStore"; import { RoomNotificationStateStore } from "../stores/notifications/RoomNotificationStateStore"; import RightPanelStore from "../stores/right-panel/RightPanelStore"; @@ -63,7 +62,6 @@ export class SdkContextClass { protected _SpaceStore?: SpaceStoreClass; protected _LegacyCallHandler?: LegacyCallHandler; protected _TypingStore?: TypingStore; - protected _AccountPasswordStore?: AccountPasswordStore; protected _UserProfilesStore?: UserProfilesStore; protected _OidcClientStore?: OidcClientStore; @@ -149,13 +147,6 @@ export class SdkContextClass { return this._TypingStore; } - public get accountPasswordStore(): AccountPasswordStore { - if (!this._AccountPasswordStore) { - this._AccountPasswordStore = new AccountPasswordStore(); - } - return this._AccountPasswordStore; - } - public get userProfilesStore(): UserProfilesStore { if (!this.client) { throw new Error("Unable to create UserProfilesStore without a client"); diff --git a/src/stores/AccountPasswordStore.ts b/src/stores/AccountPasswordStore.ts deleted file mode 100644 index 85bb7359e19..00000000000 --- a/src/stores/AccountPasswordStore.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2023 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only -Please see LICENSE files in the repository root for full details. -*/ - -const PASSWORD_TIMEOUT = 5 * 60 * 1000; // five minutes - -/** - * Store for the account password. - * This password can be used for a short time after login - * to avoid requestin the password all the time for instance during e2ee setup. - */ -export class AccountPasswordStore { - private password?: string; - private passwordTimeoutId?: ReturnType; - - public setPassword(password: string): void { - this.password = password; - clearTimeout(this.passwordTimeoutId); - this.passwordTimeoutId = setTimeout(this.clearPassword, PASSWORD_TIMEOUT); - } - - public getPassword(): string | undefined { - return this.password; - } - - public clearPassword = (): void => { - clearTimeout(this.passwordTimeoutId); - this.passwordTimeoutId = undefined; - this.password = undefined; - }; -} diff --git a/src/stores/InitialCryptoSetupStore.ts b/src/stores/InitialCryptoSetupStore.ts index 5554a15d260..46ae784db4b 100644 --- a/src/stores/InitialCryptoSetupStore.ts +++ b/src/stores/InitialCryptoSetupStore.ts @@ -11,7 +11,6 @@ import { logger } from "matrix-js-sdk/src/logger"; import { useEffect, useState } from "react"; import { createCrossSigning } from "../CreateCrossSigning"; -import { SdkContextClass } from "../contexts/SDKContext"; type Status = "in_progress" | "complete" | "error" | undefined; @@ -45,8 +44,6 @@ export class InitialCryptoSetupStore extends EventEmitter { private status: Status = undefined; private client?: MatrixClient; - private isTokenLogin?: boolean; - private stores?: SdkContextClass; private onFinished?: (success: boolean) => void; public static sharedInstance(): InitialCryptoSetupStore { @@ -62,18 +59,9 @@ export class InitialCryptoSetupStore extends EventEmitter { * Start the initial crypto setup process. * * @param {MatrixClient} client The client to use for the setup - * @param {boolean} isTokenLogin True if the user logged in via a token login, otherwise false - * @param {SdkContextClass} stores The stores to use for the setup */ - public startInitialCryptoSetup( - client: MatrixClient, - isTokenLogin: boolean, - stores: SdkContextClass, - onFinished: (success: boolean) => void, - ): void { + public startInitialCryptoSetup(client: MatrixClient, onFinished: (success: boolean) => void): void { this.client = client; - this.isTokenLogin = isTokenLogin; - this.stores = stores; this.onFinished = onFinished; // We just start this process: it's progress is tracked by the events rather @@ -89,7 +77,7 @@ export class InitialCryptoSetupStore extends EventEmitter { * @returns {boolean} True if a retry was initiated, otherwise false */ public retry(): boolean { - if (this.client === undefined || this.isTokenLogin === undefined || this.stores == undefined) return false; + if (this.client === undefined) return false; this.doSetup().catch(() => logger.error("Initial crypto setup failed")); @@ -98,12 +86,10 @@ export class InitialCryptoSetupStore extends EventEmitter { private reset(): void { this.client = undefined; - this.isTokenLogin = undefined; - this.stores = undefined; } private async doSetup(): Promise { - if (this.client === undefined || this.isTokenLogin === undefined || this.stores == undefined) { + if (this.client === undefined) { throw new Error("No setup is in progress"); } @@ -115,7 +101,7 @@ export class InitialCryptoSetupStore extends EventEmitter { try { // Create the user's cross-signing keys - await createCrossSigning(this.client, this.isTokenLogin, this.stores.accountPasswordStore.getPassword()); + await createCrossSigning(this.client); // Check for any existing backup and enable key backup if there isn't one const currentKeyBackup = await cryptoApi.checkKeyBackupAndEnable(); @@ -129,16 +115,6 @@ export class InitialCryptoSetupStore extends EventEmitter { this.emit("update"); this.onFinished?.(true); } catch (e) { - if (this.isTokenLogin) { - // ignore any failures, we are relying on grace period here - this.reset(); - - this.status = "complete"; - this.emit("update"); - this.onFinished?.(true); - - return; - } logger.error("Error bootstrapping cross-signing", e); this.status = "error"; this.emit("update"); diff --git a/src/stores/SetupEncryptionStore.ts b/src/stores/SetupEncryptionStore.ts index a13ba26f722..bfa28c3cd22 100644 --- a/src/stores/SetupEncryptionStore.ts +++ b/src/stores/SetupEncryptionStore.ts @@ -19,7 +19,6 @@ import { Device, SecretStorage } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from "../MatrixClientPeg"; import { AccessCancelledError, accessSecretStorage } from "../SecurityManager"; -import { SdkContextClass } from "../contexts/SDKContext"; import { asyncSome } from "../utils/arrays"; import { initialiseDehydration } from "../utils/device/dehydration"; @@ -239,7 +238,6 @@ export class SetupEncryptionStore extends EventEmitter { { forceReset: true, resetCrossSigning: true, - accountPassword: SdkContextClass.instance.accountPasswordStore.getPassword(), }, ); } catch (e) { diff --git a/test/CreateCrossSigning-test.ts b/test/CreateCrossSigning-test.ts index e1762bb5040..85341b8bce2 100644 --- a/test/CreateCrossSigning-test.ts +++ b/test/CreateCrossSigning-test.ts @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import { MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix"; +import { HTTPError, MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix"; import { mocked } from "jest-mock"; import { createCrossSigning } from "../src/CreateCrossSigning"; @@ -21,14 +21,14 @@ describe("CreateCrossSigning", () => { }); it("should call bootstrapCrossSigning with an authUploadDeviceSigningKeys function", async () => { - await createCrossSigning(client, false, "password"); + await createCrossSigning(client); expect(client.getCrypto()?.bootstrapCrossSigning).toHaveBeenCalledWith({ authUploadDeviceSigningKeys: expect.any(Function), }); }); - it("should upload with password auth if possible", async () => { + it("should upload", async () => { client.uploadDeviceSigningKeys = jest.fn().mockRejectedValueOnce( new MatrixError({ flows: [ @@ -39,24 +39,7 @@ describe("CreateCrossSigning", () => { }), ); - await createCrossSigning(client, false, "password"); - - const { authUploadDeviceSigningKeys } = mocked(client.getCrypto()!).bootstrapCrossSigning.mock.calls[0][0]; - - const makeRequest = jest.fn(); - await authUploadDeviceSigningKeys!(makeRequest); - expect(makeRequest).toHaveBeenCalledWith({ - type: "m.login.password", - identifier: { - type: "m.id.user", - user: client.getUserId(), - }, - password: "password", - }); - }); - - it("should attempt to upload keys without auth if using token login", async () => { - await createCrossSigning(client, true, undefined); + await createCrossSigning(client); const { authUploadDeviceSigningKeys } = mocked(client.getCrypto()!).bootstrapCrossSigning.mock.calls[0][0]; @@ -65,7 +48,7 @@ describe("CreateCrossSigning", () => { expect(makeRequest).toHaveBeenCalledWith({}); }); - it("should prompt user if password upload not possible", async () => { + it("should prompt user if upload failed with UIA", async () => { const createDialog = jest.spyOn(Modal, "createDialog").mockReturnValue({ finished: Promise.resolve([true]), close: jest.fn(), @@ -81,13 +64,32 @@ describe("CreateCrossSigning", () => { }), ); - await createCrossSigning(client, false, "password"); + await createCrossSigning(client); const { authUploadDeviceSigningKeys } = mocked(client.getCrypto()!).bootstrapCrossSigning.mock.calls[0][0]; - const makeRequest = jest.fn(); + const makeRequest = jest.fn().mockRejectedValue( + new MatrixError({ + flows: [ + { + stages: ["dummy.mystery_flow_nobody_knows"], + }, + ], + }), + ); await authUploadDeviceSigningKeys!(makeRequest); expect(makeRequest).not.toHaveBeenCalledWith(); expect(createDialog).toHaveBeenCalled(); }); + + it("should throw error if server fails with something other than UIA", async () => { + await createCrossSigning(client); + + const { authUploadDeviceSigningKeys } = mocked(client.getCrypto()!).bootstrapCrossSigning.mock.calls[0][0]; + + const error = new HTTPError("Internal Server Error", 500); + const makeRequest = jest.fn().mockRejectedValue(error); + await expect(authUploadDeviceSigningKeys!(makeRequest)).rejects.toThrow(error); + expect(makeRequest).not.toHaveBeenCalledWith(); + }); }); diff --git a/test/unit-tests/stores/AccountPasswordStore-test.ts b/test/unit-tests/stores/AccountPasswordStore-test.ts deleted file mode 100644 index 00fa8e05e66..00000000000 --- a/test/unit-tests/stores/AccountPasswordStore-test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2023 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only -Please see LICENSE files in the repository root for full details. -*/ - -import { AccountPasswordStore } from "../../../src/stores/AccountPasswordStore"; - -jest.useFakeTimers(); - -describe("AccountPasswordStore", () => { - let accountPasswordStore: AccountPasswordStore; - - beforeEach(() => { - accountPasswordStore = new AccountPasswordStore(); - }); - - it("should not have a password by default", () => { - expect(accountPasswordStore.getPassword()).toBeUndefined(); - }); - - describe("when setting a password", () => { - beforeEach(() => { - accountPasswordStore.setPassword("pass1"); - }); - - it("should return the password", () => { - expect(accountPasswordStore.getPassword()).toBe("pass1"); - }); - - describe("and the password timeout exceed", () => { - beforeEach(() => { - jest.advanceTimersToNextTimer(); - }); - - it("should clear the password", () => { - expect(accountPasswordStore.getPassword()).toBeUndefined(); - }); - }); - - describe("and setting another password", () => { - beforeEach(() => { - accountPasswordStore.setPassword("pass2"); - }); - - it("should return the other password", () => { - expect(accountPasswordStore.getPassword()).toBe("pass2"); - }); - }); - }); -}); diff --git a/test/unit-tests/stores/InitialCryptoSetupStore-test.ts b/test/unit-tests/stores/InitialCryptoSetupStore-test.ts index 64b81bade22..8cfae4d6996 100644 --- a/test/unit-tests/stores/InitialCryptoSetupStore-test.ts +++ b/test/unit-tests/stores/InitialCryptoSetupStore-test.ts @@ -8,12 +8,11 @@ Please see LICENSE files in the repository root for full details. import { mocked } from "jest-mock"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { waitFor } from "jest-matrix-react"; +import { sleep } from "matrix-js-sdk/src/utils"; import { createCrossSigning } from "../../../src/CreateCrossSigning"; import { InitialCryptoSetupStore } from "../../../src/stores/InitialCryptoSetupStore"; -import { SdkContextClass } from "../../../src/contexts/SDKContext"; import { createTestClient } from "../../test-utils"; -import { AccountPasswordStore } from "../../../src/stores/AccountPasswordStore"; jest.mock("../../../src/CreateCrossSigning", () => ({ createCrossSigning: jest.fn(), @@ -22,7 +21,6 @@ jest.mock("../../../src/CreateCrossSigning", () => ({ describe("InitialCryptoSetupStore", () => { let testStore: InitialCryptoSetupStore; let client: MatrixClient; - let stores: SdkContextClass; let createCrossSigningResolve: () => void; let createCrossSigningReject: (e: Error) => void; @@ -30,11 +28,6 @@ describe("InitialCryptoSetupStore", () => { beforeEach(() => { testStore = new InitialCryptoSetupStore(); client = createTestClient(); - stores = { - accountPasswordStore: { - getPassword: jest.fn(), - } as unknown as AccountPasswordStore, - } as unknown as SdkContextClass; mocked(createCrossSigning).mockImplementation(() => { return new Promise((resolve, reject) => { @@ -45,7 +38,7 @@ describe("InitialCryptoSetupStore", () => { }); it("should call createCrossSigning when startInitialCryptoSetup is called", async () => { - testStore.startInitialCryptoSetup(client, false, stores, jest.fn()); + testStore.startInitialCryptoSetup(client, jest.fn()); await waitFor(() => expect(createCrossSigning).toHaveBeenCalled()); }); @@ -54,7 +47,7 @@ describe("InitialCryptoSetupStore", () => { const updateSpy = jest.fn(); testStore.on("update", updateSpy); - testStore.startInitialCryptoSetup(client, false, stores, jest.fn()); + testStore.startInitialCryptoSetup(client, jest.fn()); createCrossSigningResolve(); await waitFor(() => expect(updateSpy).toHaveBeenCalled()); @@ -65,21 +58,28 @@ describe("InitialCryptoSetupStore", () => { const updateSpy = jest.fn(); testStore.on("update", updateSpy); - testStore.startInitialCryptoSetup(client, false, stores, jest.fn()); + testStore.startInitialCryptoSetup(client, jest.fn()); createCrossSigningReject(new Error("Test error")); await waitFor(() => expect(updateSpy).toHaveBeenCalled()); expect(testStore.getStatus()).toBe("error"); }); - it("should ignore failures if tokenLogin is true", async () => { - const updateSpy = jest.fn(); - testStore.on("update", updateSpy); + it("should fail to retry once complete", async () => { + testStore.startInitialCryptoSetup(client, jest.fn()); - testStore.startInitialCryptoSetup(client, true, stores, jest.fn()); - createCrossSigningReject(new Error("Test error")); + await waitFor(() => expect(createCrossSigning).toHaveBeenCalled()); + createCrossSigningResolve(); + await sleep(0); // await the next tick + expect(testStore.retry()).toBeFalsy(); + }); - await waitFor(() => expect(updateSpy).toHaveBeenCalled()); - expect(testStore.getStatus()).toBe("complete"); + it("should retry if initial attempt failed", async () => { + testStore.startInitialCryptoSetup(client, jest.fn()); + + await waitFor(() => expect(createCrossSigning).toHaveBeenCalled()); + createCrossSigningReject(new Error("Test error")); + await sleep(0); // await the next tick + expect(testStore.retry()).toBeTruthy(); }); }); diff --git a/test/unit-tests/stores/SetupEncryptionStore-test.ts b/test/unit-tests/stores/SetupEncryptionStore-test.ts index d3d0300a215..b0bc3a73d87 100644 --- a/test/unit-tests/stores/SetupEncryptionStore-test.ts +++ b/test/unit-tests/stores/SetupEncryptionStore-test.ts @@ -11,7 +11,6 @@ import { MatrixClient, Device } from "matrix-js-sdk/src/matrix"; import { SecretStorageKeyDescriptionAesV1, ServerSideSecretStorage } from "matrix-js-sdk/src/secret-storage"; import { BootstrapCrossSigningOpts, CryptoApi, DeviceVerificationStatus } from "matrix-js-sdk/src/crypto-api"; -import { SdkContextClass } from "../../../src/contexts/SDKContext"; import { accessSecretStorage } from "../../../src/SecurityManager"; import { SetupEncryptionStore } from "../../../src/stores/SetupEncryptionStore"; import { emitPromise, stubClient } from "../../test-utils"; @@ -21,7 +20,6 @@ jest.mock("../../../src/SecurityManager", () => ({ })); describe("SetupEncryptionStore", () => { - const cachedPassword = "p4assword"; let client: Mocked; let mockCrypto: Mocked; let mockSecretStorage: Mocked; @@ -47,11 +45,6 @@ describe("SetupEncryptionStore", () => { Object.defineProperty(client, "secretStorage", { value: mockSecretStorage }); setupEncryptionStore = new SetupEncryptionStore(); - SdkContextClass.instance.accountPasswordStore.setPassword(cachedPassword); - }); - - afterEach(() => { - SdkContextClass.instance.accountPasswordStore.clearPassword(); }); describe("start", () => { @@ -172,7 +165,6 @@ describe("SetupEncryptionStore", () => { await setupEncryptionStore.resetConfirm(); expect(mocked(accessSecretStorage)).toHaveBeenCalledWith(expect.any(Function), { - accountPassword: cachedPassword, forceReset: true, resetCrossSigning: true, });