Handle cross-signing keys missing locally and/or from secret storage (#31367)
* show correct toast when cross-signing keys missing If cross-signing keys are missing both locally and in 4S, show a new toast saying that identity needs resetting, rather than saying that the device needs to be verified. * refactor: make DeviceListener in charge of device state - move enum from SetupEncryptionToast to DeviceListener - DeviceListener has public method to get device state - DeviceListener emits events to update device state * reset key backup when needed in RecoveryPanelOutOfSync brings RecoveryPanelOutOfSync in line with SetupEncryptionToast behaviour * update strings to agree with designs from Figma * use DeviceListener to determine EncryptionUserSettingsTab display rather than using its own logic * prompt to reset identity in Encryption Settings when needed * fix type * calculate device state even if we aren't going to show a toast * update snapshot * make logs more accurate * add tests * make the bot use a different access token/device * only log in a new session when requested * Mark properties as read-only Co-authored-by: Skye Elliot <actuallyori@gmail.com> * remove some duplicate strings * make accessToken optional instead of using empty string * switch from enum to string union as per review * apply other changes from review * handle errors in accessSecretStorage * remove incorrect testid --------- Co-authored-by: Skye Elliot <actuallyori@gmail.com>
This commit is contained in:
co-authored by
Skye Elliot
parent
ce9c66ba4c
commit
ebd5df633e
+24
-31
@@ -5,6 +5,7 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import React from "react";
|
||||
import { act, render, screen } from "jest-matrix-react";
|
||||
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
} from "../../../../../../../src/components/views/settings/tabs/user/EncryptionUserSettingsTab";
|
||||
import { createTestClient, withClientContextRenderOptions } from "../../../../../../test-utils";
|
||||
import Modal from "../../../../../../../src/Modal";
|
||||
import DeviceListener from "../../../../../../../src/DeviceListener";
|
||||
|
||||
describe("<EncryptionUserSettingsTab />", () => {
|
||||
let matrixClient: MatrixClient;
|
||||
@@ -37,22 +39,21 @@ describe("<EncryptionUserSettingsTab />", () => {
|
||||
userSigningKey: true,
|
||||
},
|
||||
});
|
||||
|
||||
jest.spyOn(DeviceListener.sharedInstance(), "getDeviceState").mockReturnValue("ok");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
function renderComponent(props: { initialState?: State } = {}) {
|
||||
return render(<EncryptionUserSettingsTab {...props} />, withClientContextRenderOptions(matrixClient));
|
||||
}
|
||||
|
||||
it("should display a loading state when the encryption state is computed", () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "isCrossSigningReady").mockImplementation(() => new Promise(() => {}));
|
||||
|
||||
renderComponent();
|
||||
expect(screen.getByLabelText("Loading…")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should display a verify button when the encryption is not set up", async () => {
|
||||
const user = userEvent.setup();
|
||||
jest.spyOn(matrixClient.getCrypto()!, "isCrossSigningReady").mockResolvedValue(false);
|
||||
mocked(DeviceListener.sharedInstance().getDeviceState).mockReturnValue("verify_this_session");
|
||||
|
||||
const { asFragment } = renderComponent();
|
||||
await waitFor(() =>
|
||||
@@ -81,17 +82,7 @@ describe("<EncryptionUserSettingsTab />", () => {
|
||||
});
|
||||
|
||||
it("should display the recovery out of sync panel when secrets are not cached", async () => {
|
||||
jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("1");
|
||||
// Secrets are not cached
|
||||
jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockResolvedValue({
|
||||
privateKeysInSecretStorage: true,
|
||||
publicKeysOnDevice: true,
|
||||
privateKeysCachedLocally: {
|
||||
masterKey: false,
|
||||
selfSigningKey: true,
|
||||
userSigningKey: true,
|
||||
},
|
||||
});
|
||||
mocked(DeviceListener.sharedInstance().getDeviceState).mockReturnValue("key_storage_out_of_sync");
|
||||
|
||||
const user = userEvent.setup();
|
||||
const { asFragment } = renderComponent();
|
||||
@@ -196,18 +187,7 @@ describe("<EncryptionUserSettingsTab />", () => {
|
||||
it("should re-check the encryption state and displays the correct panel when the user clicks cancel the reset identity flow", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("1");
|
||||
|
||||
// Secrets are not cached
|
||||
jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockResolvedValue({
|
||||
privateKeysInSecretStorage: true,
|
||||
publicKeysOnDevice: true,
|
||||
privateKeysCachedLocally: {
|
||||
masterKey: false,
|
||||
selfSigningKey: true,
|
||||
userSigningKey: true,
|
||||
},
|
||||
});
|
||||
mocked(DeviceListener.sharedInstance().getDeviceState).mockReturnValue("key_storage_out_of_sync");
|
||||
|
||||
renderComponent({ initialState: "reset_identity_forgot" });
|
||||
|
||||
@@ -220,4 +200,17 @@ describe("<EncryptionUserSettingsTab />", () => {
|
||||
screen.getByText("Your key storage is out of sync. Click one of the buttons below to fix the problem."),
|
||||
);
|
||||
});
|
||||
|
||||
it("should display the identity needs reset panel when the user's identity needs resetting", async () => {
|
||||
mocked(DeviceListener.sharedInstance().getDeviceState).mockReturnValue("identity_needs_reset");
|
||||
|
||||
const user = userEvent.setup();
|
||||
const { asFragment } = renderComponent();
|
||||
|
||||
await waitFor(() => screen.getByRole("button", { name: "Continue with reset" }));
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Continue with reset" }));
|
||||
expect(screen.getByRole("heading", { name: "You need to reset your identity" })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user