Skip to content

Commit

Permalink
Playwright: factor out some common code (#49)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
richvdh authored Sep 19, 2024
1 parent 154bf33 commit 1e76313
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 59 deletions.
89 changes: 45 additions & 44 deletions playwright/pages/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -90,9 +90,13 @@ export class Bot extends Client {
}

protected async getClientHandle(): Promise<JSHandle<ExtendedMatrixClient>> {
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<JSHandle<ExtendedMatrixClient>> {
const credentials = await this.getCredentials();
const clientHandle = await this.page.evaluateHandle(
async ({ homeserver, credentials, opts }) => {
function getLogger(loggerName: string): Logger {
const logger = {
Expand Down Expand Up @@ -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;
}
}
45 changes: 30 additions & 15 deletions playwright/pages/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,24 +356,11 @@ export class Client {
}

/**
* Boostraps cross-signing.
* Bootstraps cross-signing.
*/
public async bootstrapCrossSigning(credentials: Credentials): Promise<void> {
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);
}

/**
Expand Down Expand Up @@ -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<MatrixClient>,
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 },
);
}

0 comments on commit 1e76313

Please sign in to comment.