mirror of https://github.com/vector-im/riot-web
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>pull/28776/head
parent
2c4a079153
commit
cd7cf86b96
|
@ -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<boolean> {
|
||||
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<void> {
|
||||
export async function createCrossSigning(cli: MatrixClient): Promise<void> {
|
||||
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<UIAResponse<void>>,
|
||||
): Promise<void> => {
|
||||
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"),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -431,8 +431,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
// 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<IProps, IState> {
|
|||
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<IProps, IState> {
|
|||
this.showScreen("forgot_password");
|
||||
};
|
||||
|
||||
private onRegisterFlowComplete = (credentials: IMatrixClientCreds, password: string): Promise<void> => {
|
||||
return this.onUserCompletedLoginFlow(credentials, password);
|
||||
private onRegisterFlowComplete = (credentials: IMatrixClientCreds): Promise<void> => {
|
||||
return this.onUserCompletedLoginFlow(credentials);
|
||||
};
|
||||
|
||||
// returns a promise which resolves to the new MatrixClient
|
||||
|
@ -2003,9 +1999,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
* 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<void> => {
|
||||
this.stores.accountPasswordStore.setPassword(password);
|
||||
|
||||
private onUserCompletedLoginFlow = async (credentials: IMatrixClientCreds): Promise<void> => {
|
||||
// Create and start the client
|
||||
await Lifecycle.setLoggedIn(credentials);
|
||||
await this.postLoginSetup();
|
||||
|
|
|
@ -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<IProps, IState>
|
|||
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;
|
||||
|
|
|
@ -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<void>;
|
||||
onLoggedIn(params: IMatrixClientCreds): Promise<void>;
|
||||
// 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<IProps, IState> {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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<typeof setTimeout>;
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
|
@ -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<void> {
|
||||
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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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<void>((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());
|
||||
await waitFor(() => expect(createCrossSigning).toHaveBeenCalled());
|
||||
createCrossSigningResolve();
|
||||
await sleep(0); // await the next tick
|
||||
expect(testStore.retry()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should retry if initial attempt failed", async () => {
|
||||
testStore.startInitialCryptoSetup(client, jest.fn());
|
||||
|
||||
await waitFor(() => expect(createCrossSigning).toHaveBeenCalled());
|
||||
createCrossSigningReject(new Error("Test error"));
|
||||
|
||||
await waitFor(() => expect(updateSpy).toHaveBeenCalled());
|
||||
expect(testStore.getStatus()).toBe("complete");
|
||||
await sleep(0); // await the next tick
|
||||
expect(testStore.retry()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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<MatrixClient>;
|
||||
let mockCrypto: Mocked<CryptoApi>;
|
||||
let mockSecretStorage: Mocked<ServerSideSecretStorage>;
|
||||
|
@ -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,
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue