mirror of
https://github.com/vector-im/element-call.git
synced 2026-05-01 09:54:37 +00:00
Use synapse API to register instead doing via UI
This commit is contained in:
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -28,11 +28,18 @@ server {
|
||||
# Reason: the lk-jwt-service uses the federation API for the openid token
|
||||
# verification, which requires TLS
|
||||
location ~ ^(/_matrix|/_synapse/client) {
|
||||
proxy_pass "http://homeserver:8008";
|
||||
proxy_pass "http://homeserver:8008";
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
location ~ ^(/_matrix|/_synapse/admin) {
|
||||
proxy_pass "http://homeserver:8008";
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
@@ -73,7 +80,15 @@ server {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location ~ ^(/_matrix|/_synapse/admin) {
|
||||
proxy_pass "http://homeserver-1:18008";
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ max_event_delay_duration: 24h
|
||||
enable_registration: true
|
||||
enable_registration_without_verification: true
|
||||
|
||||
# Shared secret for admin user registration via API (for testing only!)
|
||||
registration_shared_secret: "test_shared_secret_for_local_dev_only"
|
||||
|
||||
report_stats: false
|
||||
serve_server_wellknown: true
|
||||
|
||||
|
||||
@@ -7,11 +7,22 @@ Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import { join } from "path";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const baseURL = process.env.USE_DOCKER
|
||||
? "http://localhost:8080"
|
||||
: "https://localhost:3000";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Needed by the synapse admin API called in fixtures
|
||||
process.env.NODE_EXTRA_CA_CERTS = join(
|
||||
__dirname,
|
||||
"backend/dev_tls_local-ca.crt",
|
||||
);
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
|
||||
142
playwright/utils/synapse-admin.ts
Normal file
142
playwright/utils/synapse-admin.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
Copyright 2026 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { createHmac } from "crypto";
|
||||
|
||||
/**
|
||||
* Response from Synapse registration API
|
||||
*/
|
||||
export interface SynapseRegistrationResponse {
|
||||
access_token: string;
|
||||
user_id: string;
|
||||
home_server: string;
|
||||
device_id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class for interacting with Synapse Admin API
|
||||
* This provides fast user registration without going through the UI
|
||||
*
|
||||
* @see https://matrix-org.github.io/synapse/latest/admin_api/register_api.html
|
||||
*/
|
||||
export class SynapseAdmin {
|
||||
public constructor(
|
||||
private baseUrl: string = "https://synapse.m.localhost",
|
||||
private sharedSecret: string = "test_shared_secret_for_local_dev_only",
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Register a user using the Synapse Admin API
|
||||
* This is much faster than going through the UI registration flow
|
||||
*
|
||||
* @param username - The username (localpart) for the new user
|
||||
* @param password - The password for the new user
|
||||
* @param displayName - Optional display name (defaults to username)
|
||||
* @param admin - Whether the user should be an admin (defaults to false)
|
||||
* @returns Registration response containing access token and user ID
|
||||
*/
|
||||
public async registerUser(
|
||||
username: string,
|
||||
password: string,
|
||||
displayName?: string,
|
||||
admin: boolean = false,
|
||||
): Promise<SynapseRegistrationResponse> {
|
||||
// Get a nonce first
|
||||
const nonce = await this.getNonce();
|
||||
|
||||
// Generate the HMAC
|
||||
const mac = this.generateMac(username, password, admin, nonce);
|
||||
|
||||
// Make the registration request
|
||||
const response = await fetch(`${this.baseUrl}/_synapse/admin/v1/register`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nonce,
|
||||
username,
|
||||
password,
|
||||
displayname: displayName || username,
|
||||
admin,
|
||||
mac,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(
|
||||
`Failed to register user ${username}: ${response.status} ${error}`,
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a nonce for registration
|
||||
* The nonce is required for the HMAC calculation
|
||||
*
|
||||
* @returns A nonce string
|
||||
*/
|
||||
private async getNonce(): Promise<string> {
|
||||
const response = await fetch(`${this.baseUrl}/_synapse/admin/v1/register`, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to get nonce: ${response.status} ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HMAC for shared secret registration
|
||||
* This is the authentication mechanism for the admin API
|
||||
*
|
||||
* @param username - The username
|
||||
* @param password - The password
|
||||
* @param admin - Whether the user is an admin
|
||||
* @param nonce - The nonce from the server
|
||||
* @returns The HMAC hex string
|
||||
*/
|
||||
private generateMac(
|
||||
username: string,
|
||||
password: string,
|
||||
admin: boolean,
|
||||
nonce: string,
|
||||
): string {
|
||||
const mac = createHmac("sha1", this.sharedSecret);
|
||||
mac.update(nonce);
|
||||
mac.update("\x00");
|
||||
mac.update(username);
|
||||
mac.update("\x00");
|
||||
mac.update(password);
|
||||
mac.update("\x00");
|
||||
mac.update(admin ? "admin" : "notadmin");
|
||||
|
||||
return mac.digest("hex");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SynapseAdmin instance for a different homeserver
|
||||
*
|
||||
* @param baseUrl - The base URL of the homeserver
|
||||
* @param sharedSecret - The shared secret (defaults to test secret)
|
||||
* @returns A new SynapseAdmin instance
|
||||
*/
|
||||
public static forHomeserver(
|
||||
baseUrl: string,
|
||||
sharedSecret: string = "test_shared_secret_for_local_dev_only",
|
||||
): SynapseAdmin {
|
||||
return new SynapseAdmin(baseUrl, sharedSecret);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
} from "@playwright/test";
|
||||
import { type MatrixClient } from "matrix-js-sdk";
|
||||
|
||||
import { SynapseAdmin } from "../utils/synapse-admin.ts";
|
||||
|
||||
const PASSWORD = "foobarbaz1!";
|
||||
|
||||
export const HOST1 = "https://app.m.localhost/#/welcome";
|
||||
@@ -74,29 +76,41 @@ export class TestHelpers {
|
||||
clientHandle: JSHandle<MatrixClient>;
|
||||
mxId: string;
|
||||
}> {
|
||||
// Determine which homeserver to use based on the host
|
||||
const synapseBaseUrl =
|
||||
host === HOST2
|
||||
? "https://synapse.othersite.m.localhost"
|
||||
: "https://synapse.m.localhost";
|
||||
|
||||
// Register user via Synapse Admin API to speed things up
|
||||
const synapseAdmin = SynapseAdmin.forHomeserver(synapseBaseUrl);
|
||||
const credentials = await synapseAdmin.registerUser(
|
||||
username,
|
||||
PASSWORD,
|
||||
username,
|
||||
);
|
||||
|
||||
// STEP 2: Open browser and login
|
||||
const userContext = await browser.newContext({
|
||||
reducedMotion: "reduce",
|
||||
});
|
||||
const page = await userContext.newPage();
|
||||
await page.goto(host);
|
||||
await page.getByRole("link", { name: "Create Account" }).click();
|
||||
await page.goto(host + "/#/login");
|
||||
|
||||
await page.getByRole("link", { name: "Sign in" }).click();
|
||||
|
||||
await page.getByRole("textbox", { name: "Username" }).fill(username);
|
||||
await page.getByRole("textbox", { name: "Password", exact: true }).click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Password", exact: true })
|
||||
.fill(PASSWORD);
|
||||
await page.getByRole("textbox", { name: "Confirm password" }).click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Confirm password" })
|
||||
.fill(PASSWORD);
|
||||
await page.getByRole("button", { name: "Register" }).click();
|
||||
await page.getByRole("textbox", { name: "Password" }).fill(PASSWORD);
|
||||
await page.getByRole("button", { name: "Sign in" }).click();
|
||||
|
||||
// 😤For reasons web is staying on an infinite loading page after login, so we reload the page
|
||||
// Super annoying to have to wait...
|
||||
await page.waitForTimeout(2000);
|
||||
await page.reload();
|
||||
|
||||
await expect(
|
||||
page.getByRole("heading", { name: `Welcome ${username}` }),
|
||||
).toBeVisible({
|
||||
// Increase timeout as registration can be slow :/
|
||||
timeout: 15_000,
|
||||
});
|
||||
).toBeVisible();
|
||||
|
||||
await this.maybeDismissBrowserNotSupportedToast(page);
|
||||
await this.maybeDismissServiceWorkerWarningToast(page);
|
||||
@@ -106,11 +120,7 @@ export class TestHelpers {
|
||||
const clientHandle = await page.evaluateHandle(() =>
|
||||
window.mxMatrixClientPeg.get(),
|
||||
);
|
||||
const mxId = (await clientHandle.evaluate(
|
||||
(cli: MatrixClient) => cli.getUserId(),
|
||||
clientHandle,
|
||||
))!;
|
||||
|
||||
const mxId = credentials.user_id;
|
||||
return { page, clientHandle, mxId };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user