From 1e7631386e15bace6d5e3944ab541fae7bba40c5 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Date: Thu, 19 Sep 2024 08:13:04 +0100 Subject: [PATCH] Playwright: factor out some common code (#49) * playwright: factor out `bootstrapCrossSigningForClient` method Pull this out so it can be used elsewhere. Also expose the `resetKeys` param, which might be useful in future. * playwright: bot.ts: use `bootstrapCrossSigningForClient` ... instead of reinventing it. * Only setup cross signing if `startClient` is set --- playwright/pages/bot.ts | 89 +++++++++++++++++++------------------- playwright/pages/client.ts | 45 ++++++++++++------- 2 files changed, 75 insertions(+), 59 deletions(-) diff --git a/playwright/pages/bot.ts b/playwright/pages/bot.ts index b7542338b6..d50a0e84ee 100644 --- a/playwright/pages/bot.ts +++ b/playwright/pages/bot.ts @@ -14,7 +14,7 @@ import type { Logger } from "matrix-js-sdk/src/logger"; import type { SecretStorageKeyDescription } from "matrix-js-sdk/src/secret-storage"; import type { Credentials, HomeserverInstance } from "../plugins/homeserver"; import type { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api"; -import { Client } from "./client"; +import { bootstrapCrossSigningForClient, Client } from "./client"; export interface CreateBotOpts { /** @@ -90,9 +90,13 @@ export class Bot extends Client { } protected async getClientHandle(): Promise> { - if (this.handlePromise) return this.handlePromise; + if (!this.handlePromise) this.handlePromise = this.buildClient(); + return this.handlePromise; + } - this.handlePromise = this.page.evaluateHandle( + private async buildClient(): Promise> { + const credentials = await this.getCredentials(); + const clientHandle = await this.page.evaluateHandle( async ({ homeserver, credentials, opts }) => { function getLogger(loggerName: string): Logger { const logger = { @@ -172,53 +176,50 @@ export class Bot extends Client { }); } - if (!opts.startClient) { - return cli; - } - - await cli.initRustCrypto({ useIndexedDB: false }); - cli.setGlobalErrorOnUnknownDevices(false); - await cli.startClient(); - - if (opts.bootstrapCrossSigning) { - // XXX: workaround https://github.com/element-hq/element-web/issues/26755 - // wait for out device list to be available, as a proxy for the device keys having been uploaded. - await cli.getCrypto()!.getUserDeviceInfo([credentials.userId]); - - await cli.getCrypto()!.bootstrapCrossSigning({ - authUploadDeviceSigningKeys: async (func) => { - await func({ - type: "m.login.password", - identifier: { - type: "m.id.user", - user: credentials.userId, - }, - password: credentials.password, - }); - }, - }); - } - - if (opts.bootstrapSecretStorage) { - const passphrase = "new passphrase"; - const recoveryKey = await cli.getCrypto().createRecoveryKeyFromPassphrase(passphrase); - Object.assign(cli, { __playwright_recovery_key: recoveryKey }); - - await cli.getCrypto()!.bootstrapSecretStorage({ - setupNewSecretStorage: true, - setupNewKeyBackup: true, - createSecretStorageKey: () => Promise.resolve(recoveryKey), - }); - } - return cli; }, { homeserver: this.homeserver.config, - credentials: await this.getCredentials(), + credentials, opts: this.opts, }, ); - return this.handlePromise; + + // If we weren't configured to start the client, bail out now. + if (!this.opts.startClient) { + return clientHandle; + } + + await clientHandle.evaluate(async (cli) => { + await cli.initRustCrypto({ useIndexedDB: false }); + cli.setGlobalErrorOnUnknownDevices(false); + await cli.startClient(); + }); + + if (this.opts.bootstrapCrossSigning) { + // XXX: workaround https://github.com/element-hq/element-web/issues/26755 + // wait for out device list to be available, as a proxy for the device keys having been uploaded. + await clientHandle.evaluate(async (cli, credentials) => { + await cli.getCrypto()!.getUserDeviceInfo([credentials.userId]); + }, credentials); + + await bootstrapCrossSigningForClient(clientHandle, credentials); + } + + if (this.opts.bootstrapSecretStorage) { + await clientHandle.evaluate(async (cli) => { + const passphrase = "new passphrase"; + const recoveryKey = await cli.getCrypto().createRecoveryKeyFromPassphrase(passphrase); + Object.assign(cli, { __playwright_recovery_key: recoveryKey }); + + await cli.getCrypto()!.bootstrapSecretStorage({ + setupNewSecretStorage: true, + setupNewKeyBackup: true, + createSecretStorageKey: () => Promise.resolve(recoveryKey), + }); + }); + } + + return clientHandle; } } diff --git a/playwright/pages/client.ts b/playwright/pages/client.ts index 002a3340b2..06e05fdcfa 100644 --- a/playwright/pages/client.ts +++ b/playwright/pages/client.ts @@ -356,24 +356,11 @@ export class Client { } /** - * Boostraps cross-signing. + * Bootstraps cross-signing. */ public async bootstrapCrossSigning(credentials: Credentials): Promise { const client = await this.prepareClient(); - return client.evaluate(async (client, credentials) => { - await client.getCrypto().bootstrapCrossSigning({ - authUploadDeviceSigningKeys: async (func) => { - await func({ - type: "m.login.password", - identifier: { - type: "m.id.user", - user: credentials.userId, - }, - password: credentials.password, - }); - }, - }); - }, credentials); + return bootstrapCrossSigningForClient(client, credentials); } /** @@ -439,3 +426,31 @@ export class Client { ); } } + +/** Call `CryptoApi.bootstrapCrossSigning` on the given Matrix client, using the given credentials to authenticate + * the UIA request. + */ +export function bootstrapCrossSigningForClient( + client: JSHandle, + credentials: Credentials, + resetKeys: boolean = false, +) { + return client.evaluate( + async (client, { credentials, resetKeys }) => { + await client.getCrypto().bootstrapCrossSigning({ + authUploadDeviceSigningKeys: async (func) => { + await func({ + type: "m.login.password", + identifier: { + type: "m.id.user", + user: credentials.userId, + }, + password: credentials.password, + }); + }, + setupNewCrossSigning: resetKeys, + }); + }, + { credentials, resetKeys }, + ); +}