From 44c6d1d3f27ae65910f86459c2bd5dc4671c334b Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 22 Jan 2025 12:14:10 +0100 Subject: [PATCH 1/2] test(e2e): `checkDeviceIsConnectedKeyBackup` is checking the key backup with the matrix client and the crypto api instead of relying of the `Security & Privacy` tab. --- .../e2e/crypto/device-verification.spec.ts | 12 ++--- playwright/e2e/crypto/utils.ts | 46 +++++++++++++------ .../encryption-user-tab/recovery.spec.ts | 9 ++-- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/playwright/e2e/crypto/device-verification.spec.ts b/playwright/e2e/crypto/device-verification.spec.ts index 4091b1e6761..ffa269a0c51 100644 --- a/playwright/e2e/crypto/device-verification.spec.ts +++ b/playwright/e2e/crypto/device-verification.spec.ts @@ -68,8 +68,8 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => { // Check that the current device is connected to key backup // For now we don't check that the backup key is in cache because it's a bit flaky, - // as we need to wait for the secret gossiping to happen and the settings dialog doesn't refresh automatically. - await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, false); + // as we need to wait for the secret gossiping to happen. + await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, false); }); test("Verify device with QR code during login", async ({ page, app, credentials, homeserver }) => { @@ -112,9 +112,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => { await checkDeviceIsCrossSigned(app); // Check that the current device is connected to key backup - // For now we don't check that the backup key is in cache because it's a bit flaky, - // as we need to wait for the secret gossiping to happen and the settings dialog doesn't refresh automatically. - await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, false); + await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true); }); test("Verify device with Security Phrase during login", async ({ page, app, credentials, homeserver }) => { @@ -135,7 +133,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => { // Check that the current device is connected to key backup // The backup decryption key should be in cache also, as we got it directly from the 4S - await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, true); + await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true); }); test("Verify device with Security Key during login", async ({ page, app, credentials, homeserver }) => { @@ -158,7 +156,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => { // Check that the current device is connected to key backup // The backup decryption key should be in cache also, as we got it directly from the 4S - await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, true); + await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true); }); test("Handle incoming verification request with SAS", async ({ page, credentials, homeserver, toasts }) => { diff --git a/playwright/e2e/crypto/utils.ts b/playwright/e2e/crypto/utils.ts index 7474c5a4354..c1afa49570f 100644 --- a/playwright/e2e/crypto/utils.ts +++ b/playwright/e2e/crypto/utils.ts @@ -139,12 +139,12 @@ export async function checkDeviceIsCrossSigned(app: ElementAppPage): Promise { @@ -155,23 +155,41 @@ export async function checkDeviceIsConnectedKeyBackup( ); } - await page.getByRole("button", { name: "User menu" }).click(); - await page.locator(".mx_UserMenu_contextMenu").getByRole("menuitem", { name: "Security & Privacy" }).click(); - await expect(page.locator(".mx_Dialog").getByRole("button", { name: "Restore from Backup" })).toBeVisible(); + const backupData = await app.client.evaluate(async (client: MatrixClient) => { + const crypto = client.getCrypto(); + if (!crypto) return; - // expand the advanced section to see the active version in the reports - await page.locator(".mx_SecureBackupPanel_advanced").locator("..").click(); + const backupInfo = await crypto.getKeyBackupInfo(); + const backupKeyStored = Boolean(await client.isKeyBackupKeyStored()); + const backupKeyFromCache = await crypto.getSessionBackupPrivateKey(); + const backupKeyCached = Boolean(backupKeyFromCache); + const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array; + const activeBackupVersion = await crypto.getActiveSessionBackupVersion(); - if (checkBackupKeyInCache) { - const cacheDecryptionKeyStatusElement = page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(2) td"); - await expect(cacheDecryptionKeyStatusElement).toHaveText("cached locally, well formed"); + return { backupInfo, backupKeyStored, backupKeyCached, backupKeyWellFormed, activeBackupVersion }; + }); + + if (!backupData) { + throw new Error("Crypo module is not available"); } - await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText( - expectedBackupVersion + " (Algorithm: m.megolm_backup.v1.curve25519-aes-sha2)", - ); + const { backupInfo, backupKeyStored, backupKeyCached, backupKeyWellFormed, activeBackupVersion } = backupData; + + // We have a key backup + expect(backupInfo).toBeDefined(); + // The key backup version is as expected + expect(backupInfo.version).toBe(expectedBackupVersion); + // The active backup version is as expected + expect(activeBackupVersion).toBe(expectedBackupVersion); + // The backup key is stored in 4S + expect(backupKeyStored).toBe(true); - await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(expectedBackupVersion); + if (checkBackupKeyInCache) { + // The backup key is available locally + expect(backupKeyCached).toBe(true); + // The backup key is well-formed + expect(backupKeyWellFormed).toBe(true); + } } /** diff --git a/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts b/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts index 7ce769059a1..b4ed8f6921f 100644 --- a/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts +++ b/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts @@ -47,8 +47,7 @@ test.describe("Recovery section in Encryption tab", () => { // Check that the current device is connected to key backup // The backup decryption key should be in cache also, as we got it directly from the 4S - await app.closeDialog(); - await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, true); + await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true); }); test( @@ -115,9 +114,8 @@ test.describe("Recovery section in Encryption tab", () => { // The recovery key is now set up and the user can change it await expect(dialog.getByRole("button", { name: "Change recovery key" })).toBeVisible(); - await app.closeDialog(); // Check that the current device is connected to key backup and the backup version is the expected one - await checkDeviceIsConnectedKeyBackup(page, "1", true); + await checkDeviceIsConnectedKeyBackup(app, "1", true); }); // Test what happens if the cross-signing secrets are in secret storage but are not cached in the local DB. @@ -149,8 +147,7 @@ test.describe("Recovery section in Encryption tab", () => { // Check that the current device is connected to key backup // The backup decryption key should be in cache also, as we got it directly from the 4S - await app.closeDialog(); - await checkDeviceIsConnectedKeyBackup(page, expectedBackupVersion, true); + await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true); }, ); }); From 6487612266cadf8a609fa3e5447feea408074e37 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Fri, 24 Jan 2025 11:57:27 +0100 Subject: [PATCH 2/2] test(e2e): renaming and improve documentation --- playwright/e2e/crypto/utils.ts | 35 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/playwright/e2e/crypto/utils.ts b/playwright/e2e/crypto/utils.ts index c1afa49570f..441f44eba8a 100644 --- a/playwright/e2e/crypto/utils.ts +++ b/playwright/e2e/crypto/utils.ts @@ -139,14 +139,14 @@ export async function checkDeviceIsCrossSigned(app: ElementAppPage): Promise { // Sanity check the given backup version: if it's null, something went wrong earlier in the test. if (!expectedBackupVersion) { @@ -160,20 +160,27 @@ export async function checkDeviceIsConnectedKeyBackup( if (!crypto) return; const backupInfo = await crypto.getKeyBackupInfo(); - const backupKeyStored = Boolean(await client.isKeyBackupKeyStored()); - const backupKeyFromCache = await crypto.getSessionBackupPrivateKey(); - const backupKeyCached = Boolean(backupKeyFromCache); - const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array; + const backupKeyIn4S = Boolean(await client.isKeyBackupKeyStored()); + const backupPrivateKeyFromCache = await crypto.getSessionBackupPrivateKey(); + const hasBackupPrivateKeyFromCache = Boolean(backupPrivateKeyFromCache); + const backupPrivateKeyWellFormed = backupPrivateKeyFromCache instanceof Uint8Array; const activeBackupVersion = await crypto.getActiveSessionBackupVersion(); - return { backupInfo, backupKeyStored, backupKeyCached, backupKeyWellFormed, activeBackupVersion }; + return { + backupInfo, + hasBackupPrivateKeyFromCache, + backupPrivateKeyWellFormed, + backupKeyIn4S, + activeBackupVersion, + }; }); if (!backupData) { - throw new Error("Crypo module is not available"); + throw new Error("Crypto module is not available"); } - const { backupInfo, backupKeyStored, backupKeyCached, backupKeyWellFormed, activeBackupVersion } = backupData; + const { backupInfo, backupKeyIn4S, hasBackupPrivateKeyFromCache, backupPrivateKeyWellFormed, activeBackupVersion } = + backupData; // We have a key backup expect(backupInfo).toBeDefined(); @@ -182,13 +189,13 @@ export async function checkDeviceIsConnectedKeyBackup( // The active backup version is as expected expect(activeBackupVersion).toBe(expectedBackupVersion); // The backup key is stored in 4S - expect(backupKeyStored).toBe(true); + expect(backupKeyIn4S).toBe(true); - if (checkBackupKeyInCache) { + if (checkBackupPrivateKeyInCache) { // The backup key is available locally - expect(backupKeyCached).toBe(true); + expect(hasBackupPrivateKeyFromCache).toBe(true); // The backup key is well-formed - expect(backupKeyWellFormed).toBe(true); + expect(backupPrivateKeyWellFormed).toBe(true); } }