Use new CrytoApi.restoreKeyBackup & CrytoApi.restoreKeyBackupFromPassphrase api (#28385)

* Use new `CrytoApi.restoreKeyBackup` & `CrytoApi.restoreKeyBackupFromPassprhase` api

* Use new `CrytoApi.restoreKeyBackup` api in `SetupEncryptionStore`

* Add tests to `RestoreKeyBackupDialog`
This commit is contained in:
Florian Duros 2024-11-13 11:32:06 +01:00 committed by GitHub
parent 7b1e303328
commit c67e67af4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 383 additions and 44 deletions

View file

@ -9,6 +9,8 @@
import React from "react";
import { screen, render, waitFor } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
// Needed to be able to mock decodeRecoveryKey
// eslint-disable-next-line no-restricted-imports
import * as recoveryKeyModule from "matrix-js-sdk/src/crypto-api/recovery-key";
@ -17,9 +19,16 @@ import RestoreKeyBackupDialog from "../../../../../../src/components/views/dialo
import { stubClient } from "../../../../../test-utils";
describe("<RestoreKeyBackupDialog />", () => {
const keyBackupRestoreResult = {
total: 2,
imported: 1,
};
let matrixClient: MatrixClient;
beforeEach(() => {
stubClient();
matrixClient = stubClient();
jest.spyOn(recoveryKeyModule, "decodeRecoveryKey").mockReturnValue(new Uint8Array(32));
jest.spyOn(matrixClient, "getKeyBackupVersion").mockResolvedValue({ version: "1" } as KeyBackupInfo);
});
it("should render", async () => {
@ -48,4 +57,71 @@ describe("<RestoreKeyBackupDialog />", () => {
await waitFor(() => expect(screen.getByText("👍 This looks like a valid Security Key!")).toBeInTheDocument());
expect(asFragment()).toMatchSnapshot();
});
it("should restore key backup when the key is cached", async () => {
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup").mockResolvedValue(keyBackupRestoreResult);
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
expect(asFragment()).toMatchSnapshot();
});
it("should restore key backup when the key is in secret storage", async () => {
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup")
// Reject when trying to restore from cache
.mockRejectedValueOnce(new Error("key backup not found"))
// Resolve when trying to restore from secret storage
.mockResolvedValue(keyBackupRestoreResult);
jest.spyOn(matrixClient.secretStorage, "hasKey").mockResolvedValue(true);
jest.spyOn(matrixClient, "isKeyBackupKeyStored").mockResolvedValue({});
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
expect(asFragment()).toMatchSnapshot();
});
it("should restore key backup when security key is filled by user", async () => {
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup")
// Reject when trying to restore from cache
.mockRejectedValueOnce(new Error("key backup not found"))
// Resolve when trying to restore from recovery key
.mockResolvedValue(keyBackupRestoreResult);
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
await userEvent.type(screen.getByRole("textbox"), "my security key");
await userEvent.click(screen.getByRole("button", { name: "Next" }));
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
expect(asFragment()).toMatchSnapshot();
});
test("should restore key backup when passphrase is filled", async () => {
// Determine that the passphrase is required
jest.spyOn(matrixClient, "getKeyBackupVersion").mockResolvedValue({
version: "1",
auth_data: {
private_key_salt: "salt",
private_key_iterations: 1,
},
} as KeyBackupInfo);
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackup")
// Reject when trying to restore from cache
.mockRejectedValue(new Error("key backup not found"));
jest.spyOn(matrixClient.getCrypto()!, "restoreKeyBackupWithPassphrase").mockResolvedValue(
keyBackupRestoreResult,
);
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
await waitFor(() => expect(screen.getByText("Enter Security Phrase")).toBeInTheDocument());
// Not role for password https://github.com/w3c/aria/issues/935
await userEvent.type(screen.getByTestId("passphraseInput"), "my passphrase");
await userEvent.click(screen.getByRole("button", { name: "Next" }));
await waitFor(() => expect(screen.getByText("Successfully restored 1 keys")).toBeInTheDocument());
expect(asFragment()).toMatchSnapshot();
});
});

View file

@ -296,3 +296,263 @@ exports[`<RestoreKeyBackupDialog /> should render 1`] = `
/>
</DocumentFragment>
`;
exports[`<RestoreKeyBackupDialog /> should restore key backup when passphrase is filled 1`] = `
<DocumentFragment>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
<div
aria-labelledby="mx_BaseDialog_title"
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
>
<div
class="mx_Dialog_header"
>
<h1
class="mx_Heading_h3 mx_Dialog_title"
id="mx_BaseDialog_title"
>
Keys restored
</h1>
</div>
<div
class="mx_RestoreKeyBackupDialog_content"
>
<div>
<p>
Successfully restored 1 keys
</p>
<p>
Failed to decrypt 1 sessions!
</p>
<div
class="mx_Dialog_buttons"
>
<span
class="mx_Dialog_buttons_row"
>
<button
class="mx_Dialog_primary"
data-testid="dialog-primary-button"
type="button"
>
OK
</button>
</span>
</div>
</div>
</div>
<div
aria-label="Close dialog"
class="mx_AccessibleButton mx_Dialog_cancelButton"
role="button"
tabindex="0"
/>
</div>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
</DocumentFragment>
`;
exports[`<RestoreKeyBackupDialog /> should restore key backup when security key is filled by user 1`] = `
<DocumentFragment>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
<div
aria-labelledby="mx_BaseDialog_title"
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
>
<div
class="mx_Dialog_header"
>
<h1
class="mx_Heading_h3 mx_Dialog_title"
id="mx_BaseDialog_title"
>
Keys restored
</h1>
</div>
<div
class="mx_RestoreKeyBackupDialog_content"
>
<div>
<p>
Successfully restored 1 keys
</p>
<p>
Failed to decrypt 1 sessions!
</p>
<div
class="mx_Dialog_buttons"
>
<span
class="mx_Dialog_buttons_row"
>
<button
class="mx_Dialog_primary"
data-testid="dialog-primary-button"
type="button"
>
OK
</button>
</span>
</div>
</div>
</div>
<div
aria-label="Close dialog"
class="mx_AccessibleButton mx_Dialog_cancelButton"
role="button"
tabindex="0"
/>
</div>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
</DocumentFragment>
`;
exports[`<RestoreKeyBackupDialog /> should restore key backup when the key is cached 1`] = `
<DocumentFragment>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
<div
aria-labelledby="mx_BaseDialog_title"
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
>
<div
class="mx_Dialog_header"
>
<h1
class="mx_Heading_h3 mx_Dialog_title"
id="mx_BaseDialog_title"
>
Keys restored
</h1>
</div>
<div
class="mx_RestoreKeyBackupDialog_content"
>
<div>
<p>
Successfully restored 1 keys
</p>
<p>
Failed to decrypt 1 sessions!
</p>
<div
class="mx_Dialog_buttons"
>
<span
class="mx_Dialog_buttons_row"
>
<button
class="mx_Dialog_primary"
data-testid="dialog-primary-button"
type="button"
>
OK
</button>
</span>
</div>
</div>
</div>
<div
aria-label="Close dialog"
class="mx_AccessibleButton mx_Dialog_cancelButton"
role="button"
tabindex="0"
/>
</div>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
</DocumentFragment>
`;
exports[`<RestoreKeyBackupDialog /> should restore key backup when the key is in secret storage 1`] = `
<DocumentFragment>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
<div
aria-labelledby="mx_BaseDialog_title"
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
data-focus-lock-disabled="false"
role="dialog"
>
<div
class="mx_Dialog_header"
>
<h1
class="mx_Heading_h3 mx_Dialog_title"
id="mx_BaseDialog_title"
>
Keys restored
</h1>
</div>
<div
class="mx_RestoreKeyBackupDialog_content"
>
<div>
<p>
Successfully restored 1 keys
</p>
<p>
Failed to decrypt 1 sessions!
</p>
<div
class="mx_Dialog_buttons"
>
<span
class="mx_Dialog_buttons_row"
>
<button
class="mx_Dialog_primary"
data-testid="dialog-primary-button"
type="button"
>
OK
</button>
</span>
</div>
</div>
</div>
<div
aria-label="Close dialog"
class="mx_AccessibleButton mx_Dialog_cancelButton"
role="button"
tabindex="0"
/>
</div>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="0"
/>
</DocumentFragment>
`;