Skip to content

Commit

Permalink
Use CryptoApi.getKeyBackupInfo instead of deprecated `MatrixClient.…
Browse files Browse the repository at this point in the history
…getKeyBackupVersion` (#28450)

* Use `CryptoApi.getKeyBackupInfo` instead of deprecated `MatrixClient.getKeyBackupVersion`

* Review changes

---------

Co-authored-by: Michael Telatynski <[email protected]>
  • Loading branch information
florianduros and t3chguy authored Nov 25, 2024
1 parent fd7c50f commit ede91bf
Show file tree
Hide file tree
Showing 17 changed files with 37 additions and 40 deletions.
5 changes: 4 additions & 1 deletion src/DeviceListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,15 @@ export default class DeviceListener {
private async getKeyBackupInfo(): Promise<KeyBackupInfo | null> {
if (!this.client) return null;
const now = new Date().getTime();
const crypto = this.client.getCrypto();
if (!crypto) return null;

if (
!this.keyBackupInfo ||
!this.keyBackupFetchedAt ||
this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL
) {
this.keyBackupInfo = await this.client.getKeyBackupVersion();
this.keyBackupInfo = await crypto.getKeyBackupInfo();
this.keyBackupFetchedAt = now;
}
return this.keyBackupInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
if (!forceReset) {
try {
this.setState({ phase: Phase.Loading });
backupInfo = await cli.getKeyBackupVersion();
backupInfo = await crypto.getKeyBackupInfo();
} catch (e) {
logger.error("Error fetching backup data from server", e);
this.setState({ phase: Phase.LoadError });
Expand Down
2 changes: 1 addition & 1 deletion src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} else {
// otherwise check the server to see if there's a new one
try {
newVersionInfo = await cli.getKeyBackupVersion();
newVersionInfo = (await cli.getCrypto()?.getKeyBackupInfo()) ?? null;
if (newVersionInfo !== null) haveNewVersion = true;
} catch (e) {
logger.error("Saw key backup error but failed to check backup version!", e);
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/dialogs/LogoutDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
}

// backup is not active. see if there is a backup version on the server we ought to back up to.
const backupInfo = await client.getKeyBackupVersion();
const backupInfo = await crypto.getKeyBackupInfo();
this.setState({ backupStatus: backupInfo ? BackupStatus.SERVER_BACKUP_BUT_DISABLED : BackupStatus.NO_BACKUP });
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
});
try {
const cli = MatrixClientPeg.safeGet();
const backupInfo = await cli.getKeyBackupVersion();
const backupInfo = (await cli.getCrypto()?.getKeyBackupInfo()) ?? null;
const has4S = await cli.secretStorage.hasKey();
const backupKeyStored = has4S ? await cli.isKeyBackupKeyStored() : null;
this.setState({
Expand Down
11 changes: 4 additions & 7 deletions src/components/views/settings/SecureBackupPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
this.getUpdatedDiagnostics();
try {
const cli = MatrixClientPeg.safeGet();
const backupInfo = await cli.getKeyBackupVersion();
const backupInfo = (await cli.getCrypto()?.getKeyBackupInfo()) ?? null;
const backupTrustInfo = backupInfo ? await cli.getCrypto()?.isKeyBackupTrusted(backupInfo) : undefined;

const activeBackupVersion = (await cli.getCrypto()?.getActiveSessionBackupVersion()) ?? null;
Expand Down Expand Up @@ -192,12 +192,9 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (!proceed) return;
this.setState({ loading: true });
const versionToDelete = this.state.backupInfo!.version!;
MatrixClientPeg.safeGet()
.getCrypto()
?.deleteKeyBackupVersion(versionToDelete)
.then(() => {
this.loadBackupStatus();
});
// deleteKeyBackupVersion fires a key backup status event
// which will update the UI
MatrixClientPeg.safeGet().getCrypto()?.deleteKeyBackupVersion(versionToDelete);
},
});
};
Expand Down
2 changes: 1 addition & 1 deletion src/stores/SetupEncryptionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class SetupEncryptionStore extends EventEmitter {
this.emit("update");
try {
const cli = MatrixClientPeg.safeGet();
const backupInfo = await cli.getKeyBackupVersion();
const backupInfo = (await cli.getCrypto()?.getKeyBackupInfo()) ?? null;
this.backupInfo = backupInfo;
this.emit("update");

Expand Down
2 changes: 1 addition & 1 deletion test/test-utils/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ export const mockClientMethodsCrypto = (): Partial<
> => ({
isKeyBackupKeyStored: jest.fn(),
getCrossSigningCacheCallbacks: jest.fn().mockReturnValue({ getCrossSigningKeyCache: jest.fn() }),
getKeyBackupVersion: jest.fn().mockResolvedValue(null),
secretStorage: { hasKey: jest.fn() },
getCrypto: jest.fn().mockReturnValue({
getUserDeviceInfo: jest.fn(),
Expand All @@ -163,6 +162,7 @@ export const mockClientMethodsCrypto = (): Partial<
getOwnDeviceKeys: jest.fn().mockReturnValue(new Promise(() => {})),
getCrossSigningKeyId: jest.fn(),
isEncryptionEnabledInRoom: jest.fn().mockResolvedValue(false),
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
}),
});

Expand Down
2 changes: 1 addition & 1 deletion test/test-utils/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export function createTestClient(): MatrixClient {
getDevices: jest.fn().mockResolvedValue({ devices: [{ device_id: "ABCDEFGHI" }] }),
getSessionId: jest.fn().mockReturnValue("iaszphgvfku"),
credentials: { userId: "@userId:matrix.org" },
getKeyBackupVersion: jest.fn(),

secretStorage: {
get: jest.fn(),
Expand Down Expand Up @@ -135,6 +134,7 @@ export function createTestClient(): MatrixClient {
restoreKeyBackupWithPassphrase: jest.fn(),
loadSessionBackupPrivateKeyFromSecretStorage: jest.fn(),
storeSessionBackupPrivateKey: jest.fn(),
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
}),

getPushActionsForEvent: jest.fn(),
Expand Down
10 changes: 5 additions & 5 deletions test/unit-tests/DeviceListener-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ describe("DeviceListener", () => {
}),
getSessionBackupPrivateKey: jest.fn(),
isEncryptionEnabledInRoom: jest.fn(),
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
} as unknown as Mocked<CryptoApi>;
mockClient = getMockClientWithEventEmitter({
isGuest: jest.fn(),
getUserId: jest.fn().mockReturnValue(userId),
getSafeUserId: jest.fn().mockReturnValue(userId),
getKeyBackupVersion: jest.fn().mockResolvedValue(undefined),
getRooms: jest.fn().mockReturnValue([]),
isVersionSupported: jest.fn().mockResolvedValue(true),
isInitialSyncComplete: jest.fn().mockReturnValue(true),
Expand Down Expand Up @@ -354,7 +354,7 @@ describe("DeviceListener", () => {

it("shows set up encryption toast when user has a key backup available", async () => {
// non falsy response
mockClient!.getKeyBackupVersion.mockResolvedValue({} as unknown as KeyBackupInfo);
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as unknown as KeyBackupInfo);
await createAndStart();

expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith(
Expand Down Expand Up @@ -673,7 +673,7 @@ describe("DeviceListener", () => {
describe("When Room Key Backup is not enabled", () => {
beforeEach(() => {
// no backup
mockClient.getKeyBackupVersion.mockResolvedValue(null);
mockCrypto.getKeyBackupInfo.mockResolvedValue(null);
});

it("Should report recovery state as Enabled", async () => {
Expand Down Expand Up @@ -722,7 +722,7 @@ describe("DeviceListener", () => {
});

// no backup
mockClient.getKeyBackupVersion.mockResolvedValue(null);
mockCrypto.getKeyBackupInfo.mockResolvedValue(null);

await createAndStart();

Expand Down Expand Up @@ -872,7 +872,7 @@ describe("DeviceListener", () => {
describe("When Room Key Backup is enabled", () => {
beforeEach(() => {
// backup enabled - just need a mock object
mockClient.getKeyBackupVersion.mockResolvedValue({} as KeyBackupInfo);
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as KeyBackupInfo);
});

const testCases = [
Expand Down
2 changes: 1 addition & 1 deletion test/unit-tests/components/structures/MatrixChat-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ describe("<MatrixChat />", () => {
globalBlacklistUnverifiedDevices: false,
// This needs to not finish immediately because we need to test the screen appears
bootstrapCrossSigning: jest.fn().mockImplementation(() => bootstrapDeferred.promise),
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
}),
secretStorage: {
isStored: jest.fn().mockReturnValue(null),
Expand All @@ -148,7 +149,6 @@ describe("<MatrixChat />", () => {
whoami: jest.fn(),
logout: jest.fn(),
getDeviceId: jest.fn(),
getKeyBackupVersion: jest.fn().mockResolvedValue(null),
});
let mockClient: Mocked<MatrixClient>;
const serverConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ describe("LogoutDialog", () => {
beforeEach(() => {
mockClient = getMockClientWithEventEmitter({
...mockClientMethodsCrypto(),
getKeyBackupVersion: jest.fn(),
});

mockCrypto = mocked(mockClient.getCrypto()!);
Expand Down Expand Up @@ -50,14 +49,14 @@ describe("LogoutDialog", () => {
});

it("Prompts user to connect backup if there is a backup on the server", async () => {
mockClient.getKeyBackupVersion.mockResolvedValue({} as KeyBackupInfo);
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as KeyBackupInfo);
const rendered = renderComponent();
await rendered.findByText("Connect this session to Key Backup");
expect(rendered.container).toMatchSnapshot();
});

it("Prompts user to set up backup if there is no backup on the server", async () => {
mockClient.getKeyBackupVersion.mockResolvedValue(null);
mockCrypto.getKeyBackupInfo.mockResolvedValue(null);
const rendered = renderComponent();
await rendered.findByText("Start using Key Backup");
expect(rendered.container).toMatchSnapshot();
Expand All @@ -69,7 +68,7 @@ describe("LogoutDialog", () => {
describe("when there is an error fetching backups", () => {
filterConsole("Unable to fetch key backup status");
it("prompts user to set up backup", async () => {
mockClient.getKeyBackupVersion.mockImplementation(async () => {
mockCrypto.getKeyBackupInfo.mockImplementation(async () => {
throw new Error("beep");
});
const rendered = renderComponent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe("CreateSecretStorageDialog", () => {
filterConsole("Error fetching backup data from server");

it("shows an error", async () => {
mockClient.getKeyBackupVersion.mockImplementation(async () => {
jest.spyOn(mockClient.getCrypto()!, "getKeyBackupInfo").mockImplementation(async () => {
throw new Error("bleh bleh");
});

Expand All @@ -92,7 +92,7 @@ describe("CreateSecretStorageDialog", () => {
expect(result.container).toMatchSnapshot();

// Now we can get the backup and we retry
mockClient.getKeyBackupVersion.mockRestore();
jest.spyOn(mockClient.getCrypto()!, "getKeyBackupInfo").mockRestore();
await userEvent.click(screen.getByRole("button", { name: "Retry" }));
await screen.findByText("Your keys are now being backed up from this device.");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe("<RestoreKeyBackupDialog />", () => {
beforeEach(() => {
matrixClient = stubClient();
jest.spyOn(recoveryKeyModule, "decodeRecoveryKey").mockReturnValue(new Uint8Array(32));
jest.spyOn(matrixClient, "getKeyBackupVersion").mockResolvedValue({ version: "1" } as KeyBackupInfo);
jest.spyOn(matrixClient.getCrypto()!, "getKeyBackupInfo").mockResolvedValue({ version: "1" } as KeyBackupInfo);
});

it("should render", async () => {
Expand Down Expand Up @@ -99,7 +99,7 @@ describe("<RestoreKeyBackupDialog />", () => {

test("should restore key backup when passphrase is filled", async () => {
// Determine that the passphrase is required
jest.spyOn(matrixClient, "getKeyBackupVersion").mockResolvedValue({
jest.spyOn(matrixClient.getCrypto()!, "getKeyBackupInfo").mockResolvedValue({
version: "1",
auth_data: {
private_key_salt: "salt",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@ describe("<SecureBackupPanel />", () => {
const client = getMockClientWithEventEmitter({
...mockClientMethodsUser(userId),
...mockClientMethodsCrypto(),
getKeyBackupVersion: jest.fn().mockReturnValue("1"),
getClientWellKnown: jest.fn(),
});

const getComponent = () => render(<SecureBackupPanel />);

beforeEach(() => {
client.getKeyBackupVersion.mockResolvedValue({
jest.spyOn(client.getCrypto()!, "getKeyBackupInfo").mockResolvedValue({
version: "1",
algorithm: "test",
auth_data: {
Expand All @@ -52,7 +51,6 @@ describe("<SecureBackupPanel />", () => {
});

mocked(client.secretStorage.hasKey).mockClear().mockResolvedValue(false);
client.getKeyBackupVersion.mockClear();

mocked(accessSecretStorage).mockClear().mockResolvedValue();
});
Expand All @@ -65,8 +63,8 @@ describe("<SecureBackupPanel />", () => {
});

it("handles error fetching backup", async () => {
// getKeyBackupVersion can fail for various reasons
client.getKeyBackupVersion.mockImplementation(async () => {
// getKeyBackupInfo can fail for various reasons
jest.spyOn(client.getCrypto()!, "getKeyBackupInfo").mockImplementation(async () => {
throw new Error("beep beep");
});
const renderResult = getComponent();
Expand All @@ -75,9 +73,9 @@ describe("<SecureBackupPanel />", () => {
});

it("handles absence of backup", async () => {
client.getKeyBackupVersion.mockResolvedValue(null);
jest.spyOn(client.getCrypto()!, "getKeyBackupInfo").mockResolvedValue(null);
getComponent();
// flush getKeyBackupVersion promise
// flush getKeyBackupInfo promise
await flushPromises();
expect(screen.getByText("Back up your keys before signing out to avoid losing them.")).toBeInTheDocument();
});
Expand Down Expand Up @@ -120,7 +118,7 @@ describe("<SecureBackupPanel />", () => {
});

it("deletes backup after confirmation", async () => {
client.getKeyBackupVersion
jest.spyOn(client.getCrypto()!, "getKeyBackupInfo")
.mockResolvedValueOnce({
version: "1",
algorithm: "test",
Expand Down Expand Up @@ -157,7 +155,7 @@ describe("<SecureBackupPanel />", () => {
// flush checkKeyBackup promise
await flushPromises();

client.getKeyBackupVersion.mockClear();
jest.spyOn(client.getCrypto()!, "getKeyBackupInfo").mockClear();
mocked(client.getCrypto()!).isKeyBackupTrusted.mockClear();

fireEvent.click(screen.getByText("Reset"));
Expand All @@ -167,7 +165,7 @@ describe("<SecureBackupPanel />", () => {
await flushPromises();

// backup status refreshed
expect(client.getKeyBackupVersion).toHaveBeenCalled();
expect(client.getCrypto()!.getKeyBackupInfo).toHaveBeenCalled();
expect(client.getCrypto()!.isKeyBackupTrusted).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ describe("<SecurityUserSettingsTab />", () => {
...mockClientMethodsCrypto(),
getRooms: jest.fn().mockReturnValue([]),
getIgnoredUsers: jest.fn(),
getKeyBackupVersion: jest.fn(),
});

const sdkContext = new SdkContextClass();
Expand Down
1 change: 1 addition & 0 deletions test/unit-tests/stores/SetupEncryptionStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe("SetupEncryptionStore", () => {
getDeviceVerificationStatus: jest.fn(),
isDehydrationSupported: jest.fn().mockResolvedValue(false),
startDehydration: jest.fn(),
getKeyBackupInfo: jest.fn().mockResolvedValue(null),
} as unknown as Mocked<CryptoApi>;
client.getCrypto.mockReturnValue(mockCrypto);

Expand Down

0 comments on commit ede91bf

Please sign in to comment.