Skip to content

Commit f822653

Browse files
authored
Dehydration: enable dehydrated device on "Set up recovery" (#29265)
* playwright/dehydration: update check The old "Security & Privacy" tab is going away, so we need a new way to check for dehydrated device existence. * Dehydration: enable dehydrated device on "Set up recovery" Clicking "Set up recovery" should set up a dehydrated device, if that feature is enabled. Fixes #29135 * Empty commit ... to wake up the CLA bot
1 parent 09db599 commit f822653

File tree

2 files changed

+48
-11
lines changed

2 files changed

+48
-11
lines changed

playwright/e2e/crypto/dehydration.spec.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { test, expect } from "../../element-web-test";
1010
import { isDendrite } from "../../plugins/homeserver/dendrite";
1111
import { completeCreateSecretStorageDialog, createBot, logIntoElement } from "./utils.ts";
1212
import { type Client } from "../../pages/client.ts";
13+
import { type ElementAppPage } from "../../pages/ElementAppPage.ts";
1314

1415
const NAME = "Alice";
1516

@@ -49,13 +50,7 @@ test.describe("Dehydration", () => {
4950

5051
await completeCreateSecretStorageDialog(page);
5152

52-
// Open the settings again
53-
await app.settings.openUserSettings("Security & Privacy");
54-
55-
// The Security tab should indicate that there is a dehydrated device present
56-
await expect(securityTab.getByText("Offline device enabled")).toBeVisible();
57-
58-
await app.settings.closeDialog();
53+
await expectDehydratedDeviceEnabled(app);
5954

6055
// the dehydrated device gets created with the name "Dehydrated
6156
// device". We want to make sure that it is not visible as a normal
@@ -64,6 +59,33 @@ test.describe("Dehydration", () => {
6459
await expect(sessionsTab.getByText("Dehydrated device")).not.toBeVisible();
6560
});
6661

62+
test("'Set up recovery' creates dehydrated device", async ({ app, credentials, page }) => {
63+
await logIntoElement(page, credentials);
64+
65+
const settingsDialogLocator = await app.settings.openUserSettings("Encryption");
66+
await settingsDialogLocator.getByRole("button", { name: "Set up recovery" }).click();
67+
68+
// First it displays an informative panel about the recovery key
69+
await expect(settingsDialogLocator.getByRole("heading", { name: "Set up recovery" })).toBeVisible();
70+
await settingsDialogLocator.getByRole("button", { name: "Continue" }).click();
71+
72+
// Next, it displays the new recovery key. We click on the copy button.
73+
await expect(settingsDialogLocator.getByText("Save your recovery key somewhere safe")).toBeVisible();
74+
await settingsDialogLocator.getByRole("button", { name: "Copy" }).click();
75+
const recoveryKey = await app.getClipboard();
76+
await settingsDialogLocator.getByRole("button", { name: "Continue" }).click();
77+
78+
await expect(
79+
settingsDialogLocator.getByText("Enter your recovery key to confirm", { exact: true }),
80+
).toBeVisible();
81+
await settingsDialogLocator.getByRole("textbox").fill(recoveryKey);
82+
await settingsDialogLocator.getByRole("button", { name: "Finish set up" }).click();
83+
84+
await app.settings.closeDialog();
85+
86+
await expectDehydratedDeviceEnabled(app);
87+
});
88+
6789
test("Reset recovery key during login re-creates dehydrated device", async ({
6890
page,
6991
homeserver,
@@ -109,3 +131,16 @@ async function getDehydratedDeviceIds(client: Client): Promise<string[]> {
109131
);
110132
});
111133
}
134+
135+
/** Wait for our user to have a dehydrated device */
136+
async function expectDehydratedDeviceEnabled(app: ElementAppPage): Promise<void> {
137+
// It might be nice to do this via the UI, but currently this info is not exposed via the UI.
138+
//
139+
// Note we might have to wait for the device list to be refreshed, so we wrap in `expect.poll`.
140+
await expect
141+
.poll(async () => {
142+
const dehydratedDeviceIds = await getDehydratedDeviceIds(app.client);
143+
return dehydratedDeviceIds.length;
144+
})
145+
.toEqual(1);
146+
}

src/components/views/settings/encryption/ChangeRecoveryKey.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { EncryptionCard } from "./EncryptionCard";
2626
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
2727
import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
2828
import { copyPlaintext } from "../../../../utils/strings";
29+
import { initialiseDehydrationIfEnabled } from "../../../../utils/device/dehydration.ts";
2930
import { withSecretStorageKeyCache } from "../../../../SecurityManager";
3031

3132
/**
@@ -122,12 +123,13 @@ export function ChangeRecoveryKey({
122123
try {
123124
// We need to enable the cache to avoid to prompt the user to enter the new key
124125
// when we will try to access the secret storage during the bootstrap
125-
await withSecretStorageKeyCache(() =>
126-
crypto.bootstrapSecretStorage({
126+
await withSecretStorageKeyCache(async () => {
127+
await crypto.bootstrapSecretStorage({
127128
setupNewSecretStorage: true,
128129
createSecretStorageKey: async () => recoveryKey,
129-
}),
130-
);
130+
});
131+
await initialiseDehydrationIfEnabled(matrixClient, { createNewKey: true });
132+
});
131133
onFinish();
132134
} catch (e) {
133135
logger.error("Failed to bootstrap secret storage", e);

0 commit comments

Comments
 (0)