Fix logic in DeviceListener (#30230)

* remove incorrect check for cross-signing

SETUP_ENCRYPTION tries to set up everything (4S, cross-signing and key backup),
rather than just setting up encryption, as its name would imply.
crossSigningReady == false happens when the user's device isn't verified, so it
should trigger VERIFY_THIS_SESSION rather than SETUP_ENCRYPTION

* reorder conditions in allSystemsReady to match the order in the if statements

* explicitly handle secrets missing from 4S

rather than falling back to the SETUP_ENCRYPTION catch-all.  Also, remove
SETUP_ENCRYPTION since it is no longer used.

* convert button handlers to switch statements for consistency

(almost) all the other functions that use make decisions based on Kind use
switch statements

* update i18n (remove obsolete string)
This commit is contained in:
Hubert Chathi
2025-06-30 10:01:06 -04:00
committed by GitHub
parent 58875e5cf2
commit 3d56aa7ff6
5 changed files with 176 additions and 93 deletions

View File

@@ -307,15 +307,6 @@ describe("DeviceListener", () => {
jest.spyOn(mockClient.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(true);
});
it("hides setup encryption toast when cross signing and secret storage are ready", async () => {
mockCrypto!.isCrossSigningReady.mockResolvedValue(true);
mockCrypto!.isSecretStorageReady.mockResolvedValue(true);
mockCrypto!.getActiveSessionBackupVersion.mockResolvedValue("1");
await createAndStart();
expect(SetupEncryptionToast.hideToast).toHaveBeenCalled();
});
it("hides setup encryption toast when it is dismissed", async () => {
const instance = await createAndStart();
instance.dismissEncryptionSetup();
@@ -360,7 +351,15 @@ describe("DeviceListener", () => {
);
});
it("shows an out-of-sync toast when one of the secrets is missing", async () => {
it("hides setup encryption toast when cross signing and secret storage are ready", async () => {
mockCrypto!.isSecretStorageReady.mockResolvedValue(true);
mockCrypto!.getActiveSessionBackupVersion.mockResolvedValue("1");
await createAndStart();
expect(SetupEncryptionToast.hideToast).toHaveBeenCalled();
});
it("shows an out-of-sync toast when one of the secrets is missing locally", async () => {
mockCrypto!.getCrossSigningStatus.mockResolvedValue({
publicKeysOnDevice: true,
privateKeysInSecretStorage: true,
@@ -378,7 +377,7 @@ describe("DeviceListener", () => {
);
});
it("hides the out-of-sync toast when one of the secrets is missing", async () => {
it("hides the out-of-sync toast after we receive the missing secrets", async () => {
mockCrypto!.isSecretStorageReady.mockResolvedValue(true);
mockCrypto!.getActiveSessionBackupVersion.mockResolvedValue("1");
@@ -427,6 +426,18 @@ describe("DeviceListener", () => {
SetupEncryptionToast.Kind.SET_UP_RECOVERY,
);
});
it("shows an out-of-sync toast when one of the secrets is missing from 4S", async () => {
mockCrypto.getKeyBackupInfo.mockResolvedValue({} as unknown as KeyBackupInfo);
mockCrypto.getActiveSessionBackupVersion.mockResolvedValue("1");
mockClient.secretStorage.getDefaultKeyId.mockResolvedValue("foo");
await createAndStart();
expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith(
SetupEncryptionToast.Kind.KEY_STORAGE_OUT_OF_SYNC_STORE,
);
});
});
});
@@ -448,6 +459,16 @@ describe("DeviceListener", () => {
});
it("dispatches keybackup event when key backup is not enabled", async () => {
mockCrypto!.isCrossSigningReady.mockResolvedValue(true);
// current device is verified
mockCrypto!.getDeviceVerificationStatus.mockResolvedValue(
new DeviceVerificationStatus({
trustCrossSignedDevices: true,
crossSigningVerified: true,
}),
);
mockCrypto.getActiveSessionBackupVersion.mockResolvedValue(null);
mockClient.getAccountDataFromServer.mockImplementation((eventType) =>
eventType === BACKUP_DISABLED_ACCOUNT_DATA_KEY ? ({ disabled: true } as any) : null,

View File

@@ -50,7 +50,7 @@ describe("SetupEncryptionToast", () => {
});
});
describe("Key storage out of sync", () => {
describe("Key storage out of sync (retrieve secrets)", () => {
it("should render the toast", async () => {
showToast(Kind.KEY_STORAGE_OUT_OF_SYNC);
@@ -88,6 +88,44 @@ describe("SetupEncryptionToast", () => {
});
});
describe("Key storage out of sync (store secrets)", () => {
it("should render the toast", async () => {
showToast(Kind.KEY_STORAGE_OUT_OF_SYNC_STORE);
await expect(screen.findByText("Your key storage is out of sync.")).resolves.toBeInTheDocument();
});
it("should open settings to the reset flow when 'forgot recovery key' clicked", async () => {
showToast(Kind.KEY_STORAGE_OUT_OF_SYNC_STORE);
const user = userEvent.setup();
await user.click(await screen.findByText("Forgot recovery key?"));
expect(dis.dispatch).toHaveBeenCalledWith({
action: "view_user_settings",
initialTabId: "USER_ENCRYPTION_TAB",
props: { initialEncryptionState: "change_recovery_key" },
});
});
it("should open settings to the reset flow when recovering fails", async () => {
jest.spyOn(SecurityManager, "accessSecretStorage").mockImplementation(async () => {
throw new Error("Something went wrong while recovering!");
});
showToast(Kind.KEY_STORAGE_OUT_OF_SYNC_STORE);
const user = userEvent.setup();
await user.click(await screen.findByText("Enter recovery key"));
expect(dis.dispatch).toHaveBeenCalledWith({
action: "view_user_settings",
initialTabId: "USER_ENCRYPTION_TAB",
props: { initialEncryptionState: "change_recovery_key" },
});
});
});
describe("Turn on key storage", () => {
it("should render the toast", async () => {
showToast(Kind.TURN_ON_KEY_STORAGE);