Fix a fresh login creating a new key backup (#12106)

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
Co-authored-by: Valere <valeref@matrix.org>
fix repeated requests to enter 4S key during cross-signing reset (#12059)
This commit is contained in:
Michael Telatynski
2024-01-04 13:58:05 +00:00
committed by GitHub
parent 951c0d8483
commit 1a469f41cd
15 changed files with 302 additions and 348 deletions

View File

@@ -1,62 +0,0 @@
/*
Copyright 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { mocked } from "jest-mock";
import { CryptoApi } from "matrix-js-sdk/src/crypto-api";
import { accessSecretStorage } from "../src/SecurityManager";
import { filterConsole, stubClient } from "./test-utils";
describe("SecurityManager", () => {
describe("accessSecretStorage", () => {
filterConsole("Not setting dehydration key: no SSSS key found");
it("runs the function passed in", async () => {
// Given a client
const crypto = {
bootstrapCrossSigning: () => {},
bootstrapSecretStorage: () => {},
} as unknown as CryptoApi;
const client = stubClient();
mocked(client.hasSecretStorageKey).mockResolvedValue(true);
mocked(client.getCrypto).mockReturnValue(crypto);
// When I run accessSecretStorage
const func = jest.fn();
await accessSecretStorage(func);
// Then we call the passed-in function
expect(func).toHaveBeenCalledTimes(1);
});
describe("expecting errors", () => {
filterConsole("End-to-end encryption is disabled - unable to access secret storage");
it("throws if crypto is unavailable", async () => {
// Given a client with no crypto
const client = stubClient();
mocked(client.hasSecretStorageKey).mockResolvedValue(true);
mocked(client.getCrypto).mockReturnValue(undefined);
// When I run accessSecretStorage
// Then we throw an error
await expect(async () => {
await accessSecretStorage(jest.fn());
}).rejects.toThrow("End-to-end encryption is disabled - unable to access secret storage");
});
});
});
});

View File

@@ -19,13 +19,11 @@ import React from "react";
import { mocked } from "jest-mock";
import CreateKeyBackupDialog from "../../../../../src/async-components/views/dialogs/security/CreateKeyBackupDialog";
import { createTestClient, filterConsole } from "../../../../test-utils";
import { createTestClient } from "../../../../test-utils";
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
jest.mock("../../../../../src/SecurityManager", () => ({
accessSecretStorage: async (func = async () => Promise<void>) => {
await func();
},
accessSecretStorage: jest.fn().mockResolvedValue(undefined),
}));
describe("CreateKeyBackupDialog", () => {
@@ -41,33 +39,16 @@ describe("CreateKeyBackupDialog", () => {
expect(asFragment()).toMatchSnapshot();
});
describe("expecting failure", () => {
filterConsole("Error creating key backup");
it("should display the error message when backup creation failed", async () => {
const matrixClient = createTestClient();
mocked(matrixClient.scheduleAllGroupSessionsForBackup).mockRejectedValue("my error");
MatrixClientPeg.safeGet = MatrixClientPeg.get = () => matrixClient;
it("should display an error message when backup creation failed", async () => {
const matrixClient = createTestClient();
mocked(matrixClient.getCrypto()!.resetKeyBackup).mockImplementation(() => {
throw new Error("failed");
});
MatrixClientPeg.safeGet = MatrixClientPeg.get = () => matrixClient;
const { asFragment } = render(<CreateKeyBackupDialog onFinished={jest.fn()} />);
const { asFragment } = render(<CreateKeyBackupDialog onFinished={jest.fn()} />);
// Check if the error message is displayed
await waitFor(() => expect(screen.getByText("Unable to create key backup")).toBeDefined());
expect(asFragment()).toMatchSnapshot();
});
it("should display an error message when there is no Crypto available", async () => {
const matrixClient = createTestClient();
mocked(matrixClient.getCrypto).mockReturnValue(undefined);
MatrixClientPeg.safeGet = MatrixClientPeg.get = () => matrixClient;
render(<CreateKeyBackupDialog onFinished={jest.fn()} />);
// Check if the error message is displayed
await waitFor(() => expect(screen.getByText("Unable to create key backup")).toBeDefined());
});
// Check if the error message is displayed
await waitFor(() => expect(screen.getByText("Unable to create key backup")).toBeDefined());
expect(asFragment()).toMatchSnapshot();
});
it("should display the success dialog when the key backup is finished", async () => {

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CreateKeyBackupDialog expecting failure should display an error message when backup creation failed 1`] = `
exports[`CreateKeyBackupDialog should display the error message when backup creation failed 1`] = `
<DocumentFragment>
<div
data-focus-guard="true"

View File

@@ -26,6 +26,8 @@ import {
mockClientMethodsCrypto,
mockClientMethodsUser,
} from "../../../test-utils";
import Modal from "../../../../src/Modal";
import ConfirmDestroyCrossSigningDialog from "../../../../src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog";
describe("<CrossSigningPanel />", () => {
const userId = "@alice:server.org";
@@ -43,6 +45,10 @@ describe("<CrossSigningPanel />", () => {
mockClient.isCrossSigningReady.mockResolvedValue(false);
});
afterEach(() => {
jest.restoreAllMocks();
});
it("should render a spinner while loading", () => {
getComponent();
@@ -85,6 +91,21 @@ describe("<CrossSigningPanel />", () => {
expect(screen.getByTestId("summarised-status").innerHTML).toEqual("✅ Cross-signing is ready for use.");
expect(screen.getByText("Cross-signing private keys:").parentElement!).toMatchSnapshot();
});
it("should allow reset of cross-signing", async () => {
mockClient.getCrypto()!.bootstrapCrossSigning = jest.fn().mockResolvedValue(undefined);
getComponent();
await flushPromises();
const modalSpy = jest.spyOn(Modal, "createDialog");
screen.getByRole("button", { name: "Reset" }).click();
expect(modalSpy).toHaveBeenCalledWith(ConfirmDestroyCrossSigningDialog, expect.any(Object));
modalSpy.mock.lastCall![1]!.onFinished(true);
expect(mockClient.getCrypto()!.bootstrapCrossSigning).toHaveBeenCalledWith(
expect.objectContaining({ setupNewCrossSigning: true }),
);
});
});
describe("when cross signing is not ready", () => {

View File

@@ -175,66 +175,68 @@ exports[`<SecurityUserSettingsTab /> renders security section 1`] = `
<table
class="mx_CrossSigningPanel_statusList"
>
<tr>
<th
scope="row"
>
Cross-signing public keys:
</th>
<td>
not found
</td>
</tr>
<tr>
<th
scope="row"
>
Cross-signing private keys:
</th>
<td>
not found in storage
</td>
</tr>
<tr>
<th
scope="row"
>
Master private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
Self signing private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
User signing private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
Homeserver feature support:
</th>
<td>
not found
</td>
</tr>
<tbody>
<tr>
<th
scope="row"
>
Cross-signing public keys:
</th>
<td>
not found
</td>
</tr>
<tr>
<th
scope="row"
>
Cross-signing private keys:
</th>
<td>
not found in storage
</td>
</tr>
<tr>
<th
scope="row"
>
Master private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
Self signing private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
User signing private key:
</th>
<td>
not found locally
</td>
</tr>
<tr>
<th
scope="row"
>
Homeserver feature support:
</th>
<td>
not found
</td>
</tr>
</tbody>
</table>
</details>
</div>

View File

@@ -132,7 +132,6 @@ export function createTestClient(): MatrixClient {
getUserDeviceInfo: jest.fn(),
getUserVerificationStatus: jest.fn(),
getDeviceVerificationStatus: jest.fn(),
resetKeyBackup: jest.fn(),
}),
getPushActionsForEvent: jest.fn(),