diff --git a/playwright/e2e/crypto/backups.spec.ts b/playwright/e2e/crypto/backups.spec.ts index 8826cb4595..feeaf961df 100644 --- a/playwright/e2e/crypto/backups.spec.ts +++ b/playwright/e2e/crypto/backups.spec.ts @@ -9,8 +9,9 @@ Please see LICENSE files in the repository root for full details. import { type Page } from "@playwright/test"; import { test, expect } from "../../element-web-test"; -import { test as masTest, registerAccountMas } from "../oidc"; +import { registerAccountMas } from "../oidc"; import { isDendrite } from "../../plugins/homeserver/dendrite"; +import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts"; async function expectBackupVersionToBe(page: Page, version: string) { await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(5) td")).toHaveText( @@ -20,10 +21,11 @@ async function expectBackupVersionToBe(page: Page, version: string) { await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(version); } -masTest.describe("Encryption state after registration", () => { - masTest.skip(isDendrite, "does not yet support MAS"); +test.describe("Encryption state after registration", () => { + test.use(masHomeserver); + test.skip(isDendrite, "does not yet support MAS"); - masTest("Key backup is enabled by default", async ({ page, mailhogClient, app }) => { + test("Key backup is enabled by default", async ({ page, mailhogClient, app }) => { await page.goto("/#/login"); await page.getByRole("button", { name: "Continue" }).click(); await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!"); @@ -32,7 +34,7 @@ masTest.describe("Encryption state after registration", () => { expect(page.getByText("This session is backing up your keys.")).toBeVisible(); }); - masTest("user is prompted to set up recovery", async ({ page, mailhogClient, app }) => { + test("user is prompted to set up recovery", async ({ page, mailhogClient, app }) => { await page.goto("/#/login"); await page.getByRole("button", { name: "Continue" }).click(); await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!"); diff --git a/playwright/e2e/oidc/index.ts b/playwright/e2e/oidc/index.ts index bc83f8fb63..8ea03ff37a 100644 --- a/playwright/e2e/oidc/index.ts +++ b/playwright/e2e/oidc/index.ts @@ -9,33 +9,7 @@ Please see LICENSE files in the repository root for full details. import { API, Messages } from "mailhog"; import { Page } from "@playwright/test"; -import { test as base, expect } from "../../element-web-test"; - -export const test = base.extend<{}>({ - config: async ({ homeserver, mas, context }, use) => { - const issuer = `http://localhost:${mas.getMappedPort(8080)}/`; - const wellKnown = { - "m.homeserver": { - base_url: homeserver.baseUrl, - }, - "org.matrix.msc2965.authentication": { - issuer, - account: `${issuer}account`, - }, - }; - - // Ensure org.matrix.msc2965.authentication is in well-known - await context.route("https://localhost/.well-known/matrix/client", async (route) => { - await route.fulfill({ json: wellKnown }); - }); - - await use({ - default_server_config: wellKnown, - }); - }, -}); - -export { expect }; +import { expect } from "../../element-web-test"; export async function registerAccountMas( page: Page, diff --git a/playwright/e2e/oidc/oidc-native.spec.ts b/playwright/e2e/oidc/oidc-native.spec.ts index 78696d75ef..e5bb7e2ce6 100644 --- a/playwright/e2e/oidc/oidc-native.spec.ts +++ b/playwright/e2e/oidc/oidc-native.spec.ts @@ -6,16 +6,19 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import { test, expect, registerAccountMas } from "."; +import { test, expect } from "../../element-web-test.ts"; +import { registerAccountMas } from "."; import { ElementAppPage } from "../../pages/ElementAppPage.ts"; import { isDendrite } from "../../plugins/homeserver/dendrite"; +import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts"; test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { + test.use(masHomeserver); test.skip(isDendrite, "does not yet support MAS"); test.slow(); // trace recording takes a while here test("can register the oauth2 client and an account", async ({ context, page, homeserver, mailhogClient, mas }) => { - const tokenUri = `http://localhost:${mas.getMappedPort(8080)}/oauth2/token`; + const tokenUri = `http://${mas.getHost()}:${mas.getMappedPort(8080)}/oauth2/token`; const tokenApiPromise = page.waitForRequest( (request) => request.url() === tokenUri && request.postDataJSON()["grant_type"] === "authorization_code", ); @@ -49,7 +52,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { await newPage.close(); // Assert logging out revokes both tokens - const revokeUri = `http://localhost:${mas.getMappedPort(8080)}/oauth2/revoke`; + const revokeUri = `http://${mas.getHost()}:${mas.getMappedPort(8080)}/oauth2/revoke`; const revokeAccessTokenPromise = page.waitForRequest( (request) => request.url() === revokeUri && request.postDataJSON()["token_type_hint"] === "access_token", ); diff --git a/playwright/e2e/sliding-sync/sliding-sync.spec.ts b/playwright/e2e/sliding-sync/sliding-sync.spec.ts index f0b717b972..dec588c5b2 100644 --- a/playwright/e2e/sliding-sync/sliding-sync.spec.ts +++ b/playwright/e2e/sliding-sync/sliding-sync.spec.ts @@ -31,7 +31,7 @@ const test = base.extend<{ }) .start(); - const proxyAddress = `http://localhost:${container.getMappedPort(8008)}`; + const proxyAddress = `http://${container.getHost()}:${container.getMappedPort(8008)}`; await page.addInitScript((proxyAddress) => { window.localStorage.setItem( "mx_local_settings", diff --git a/playwright/element-web-test.ts b/playwright/element-web-test.ts index e53ebc0818..ed1bdb2b6c 100644 --- a/playwright/element-web-test.ts +++ b/playwright/element-web-test.ts @@ -165,8 +165,14 @@ export const test = base.extend({ window.localStorage.setItem("mx_has_pickle_key", "false"); window.localStorage.setItem("mx_has_access_token", "true"); - // Ensure the language is set to a consistent value - window.localStorage.setItem("mx_local_settings", '{"language":"en"}'); + window.localStorage.setItem( + "mx_local_settings", + JSON.stringify({ + ...JSON.parse(window.localStorage.getItem("mx_local_settings") || "{}"), + // Ensure the language is set to a consistent value + language: "en", + }), + ); }, { baseUrl: homeserver.baseUrl, credentials }, ); diff --git a/playwright/plugins/homeserver/synapse/masHomeserver.ts b/playwright/plugins/homeserver/synapse/masHomeserver.ts new file mode 100644 index 0000000000..a9670eb60e --- /dev/null +++ b/playwright/plugins/homeserver/synapse/masHomeserver.ts @@ -0,0 +1,191 @@ +/* +Copyright 2024 New Vector Ltd. +Copyright 2023 The Matrix.org Foundation C.I.C. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +import { Fixtures } from "@playwright/test"; + +import { Services } from "../../../services.ts"; +import { MatrixAuthenticationServiceContainer } from "../../../testcontainers/mas.ts"; + +export const masHomeserver: Fixtures = { + mas: async ({ _homeserver: homeserver, logger, network, postgres, mailhog }, use) => { + const config = { + clients: [ + { + client_id: "0000000000000000000SYNAPSE", + client_auth_method: "client_secret_basic", + client_secret: "SomeRandomSecret", + }, + ], + matrix: { + homeserver: "localhost", + secret: "AnotherRandomSecret", + endpoint: "http://synapse:8008", + }, + }; + + const container = await new MatrixAuthenticationServiceContainer(postgres) + .withNetwork(network) + .withNetworkAliases("mas") + .withLogConsumer(logger.getConsumer("mas")) + .withConfig(config) + .start(); + + homeserver.withConfig({ + enable_registration: undefined, + enable_registration_without_verification: undefined, + disable_msisdn_registration: undefined, + password_config: undefined, + experimental_features: { + msc3861: { + enabled: true, + issuer: "http://mas:8080/", + issuer_metadata: { + "issuer": `http://${container.getHost()}:${container.getMappedPort(8080)}/`, + "authorization_endpoint": "http://mas:8080/authorize", + "token_endpoint": "http://mas:8080/oauth2/token", + "jwks_uri": "http://mas:8080/oauth2/keys.json", + "registration_endpoint": "http://mas:8080/oauth2/registration", + "scopes_supported": ["openid", "email"], + "response_types_supported": ["code", "id_token", "code id_token"], + "response_modes_supported": ["form_post", "query", "fragment"], + "grant_types_supported": [ + "authorization_code", + "refresh_token", + "client_credentials", + "urn:ietf:params:oauth:grant-type:device_code", + ], + "token_endpoint_auth_methods_supported": [ + "client_secret_basic", + "client_secret_post", + "client_secret_jwt", + "private_key_jwt", + "none", + ], + "token_endpoint_auth_signing_alg_values_supported": [ + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "PS256", + "PS384", + "PS512", + "ES256", + "ES384", + "ES256K", + ], + "revocation_endpoint": "http://mas:8080/oauth2/revoke", + "revocation_endpoint_auth_methods_supported": [ + "client_secret_basic", + "client_secret_post", + "client_secret_jwt", + "private_key_jwt", + "none", + ], + "revocation_endpoint_auth_signing_alg_values_supported": [ + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "PS256", + "PS384", + "PS512", + "ES256", + "ES384", + "ES256K", + ], + "introspection_endpoint": "http://mas:8080/oauth2/introspect", + "introspection_endpoint_auth_methods_supported": [ + "client_secret_basic", + "client_secret_post", + "client_secret_jwt", + "private_key_jwt", + "none", + ], + "introspection_endpoint_auth_signing_alg_values_supported": [ + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "PS256", + "PS384", + "PS512", + "ES256", + "ES384", + "ES256K", + ], + "code_challenge_methods_supported": ["plain", "S256"], + "userinfo_endpoint": "http://mas:8080/oauth2/userinfo", + "subject_types_supported": ["public"], + "id_token_signing_alg_values_supported": [ + "RS256", + "RS384", + "RS512", + "ES256", + "ES384", + "PS256", + "PS384", + "PS512", + "ES256K", + ], + "userinfo_signing_alg_values_supported": [ + "RS256", + "RS384", + "RS512", + "ES256", + "ES384", + "PS256", + "PS384", + "PS512", + "ES256K", + ], + "display_values_supported": ["page"], + "claim_types_supported": ["normal"], + "claims_supported": [ + "iss", + "sub", + "aud", + "iat", + "exp", + "nonce", + "auth_time", + "at_hash", + "c_hash", + ], + "claims_parameter_supported": false, + "request_parameter_supported": false, + "request_uri_parameter_supported": false, + "prompt_values_supported": ["none", "login", "create"], + "device_authorization_endpoint": "http://mas:8080/oauth2/device", + "org.matrix.matrix-authentication-service.graphql_endpoint": "http://mas:8080/graphql", + "account_management_uri": "http://mas:8080/account/", + "account_management_actions_supported": [ + "org.matrix.profile", + "org.matrix.sessions_list", + "org.matrix.session_view", + "org.matrix.session_end", + ], + }, + client_id: config.clients[0].client_id, + client_auth_method: config.clients[0].client_auth_method, + client_secret: config.clients[0].client_secret, + admin_token: config.matrix.secret, + account_management_url: `http://${container.getHost()}:${container.getMappedPort(8080)}/account`, + }, + }, + }); + + await use(container); + await container.stop(); + }, +}; diff --git a/playwright/services.ts b/playwright/services.ts index e9b735c3ab..770b30b8fc 100644 --- a/playwright/services.ts +++ b/playwright/services.ts @@ -11,7 +11,6 @@ import { GenericContainer, Network, StartedNetwork, StartedTestContainer, Wait } import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql"; import { StartedSynapseContainer, SynapseConfigOptions, SynapseContainer } from "./testcontainers/synapse.ts"; -import { MatrixAuthenticationServiceContainer } from "./testcontainers/mas.ts"; import { ContainerLogger } from "./testcontainers/utils.ts"; export interface Services { @@ -26,7 +25,7 @@ export interface Services { synapseConfigOptions: SynapseConfigOptions; _homeserver: SynapseContainer; homeserver: StartedSynapseContainer; - mas: StartedTestContainer; + mas?: StartedTestContainer; } export const test = base.extend({ @@ -88,7 +87,7 @@ export const test = base.extend({ const container = new SynapseContainer(request); await use(container); }, - homeserver: async ({ logger, network, _homeserver: homeserver, synapseConfigOptions }, use) => { + homeserver: async ({ logger, network, _homeserver: homeserver, synapseConfigOptions, mas }, use) => { const container = await homeserver .withNetwork(network) .withNetworkAliases("homeserver") @@ -99,179 +98,10 @@ export const test = base.extend({ await use(container); await container.stop(); }, - mas: async ({ _homeserver: homeserver, logger, network, postgres, mailhog }, use) => { - const config = { - clients: [ - { - client_id: "0000000000000000000SYNAPSE", - client_auth_method: "client_secret_basic", - client_secret: "SomeRandomSecret", - }, - ], - matrix: { - homeserver: "localhost", - secret: "AnotherRandomSecret", - endpoint: "http://synapse:8008", - }, - }; - - const container = await new MatrixAuthenticationServiceContainer(postgres) - .withNetwork(network) - .withNetworkAliases("mas") - .withLogConsumer(logger.getConsumer("mas")) - .withConfig(config) - .start(); - - homeserver.withConfig({ - enable_registration: undefined, - enable_registration_without_verification: undefined, - disable_msisdn_registration: undefined, - experimental_features: { - msc3861: { - enabled: true, - issuer: "http://mas:8080/", - issuer_metadata: { - "issuer": `http://${container.getHost()}:${container.getMappedPort(8080)}/`, - "authorization_endpoint": "http://mas:8080/authorize", - "token_endpoint": "http://mas:8080/oauth2/token", - "jwks_uri": "http://mas:8080/oauth2/keys.json", - "registration_endpoint": "http://mas:8080/oauth2/registration", - "scopes_supported": ["openid", "email"], - "response_types_supported": ["code", "id_token", "code id_token"], - "response_modes_supported": ["form_post", "query", "fragment"], - "grant_types_supported": [ - "authorization_code", - "refresh_token", - "client_credentials", - "urn:ietf:params:oauth:grant-type:device_code", - ], - "token_endpoint_auth_methods_supported": [ - "client_secret_basic", - "client_secret_post", - "client_secret_jwt", - "private_key_jwt", - "none", - ], - "token_endpoint_auth_signing_alg_values_supported": [ - "HS256", - "HS384", - "HS512", - "RS256", - "RS384", - "RS512", - "PS256", - "PS384", - "PS512", - "ES256", - "ES384", - "ES256K", - ], - "revocation_endpoint": "http://mas:8080/oauth2/revoke", - "revocation_endpoint_auth_methods_supported": [ - "client_secret_basic", - "client_secret_post", - "client_secret_jwt", - "private_key_jwt", - "none", - ], - "revocation_endpoint_auth_signing_alg_values_supported": [ - "HS256", - "HS384", - "HS512", - "RS256", - "RS384", - "RS512", - "PS256", - "PS384", - "PS512", - "ES256", - "ES384", - "ES256K", - ], - "introspection_endpoint": "http://mas:8080/oauth2/introspect", - "introspection_endpoint_auth_methods_supported": [ - "client_secret_basic", - "client_secret_post", - "client_secret_jwt", - "private_key_jwt", - "none", - ], - "introspection_endpoint_auth_signing_alg_values_supported": [ - "HS256", - "HS384", - "HS512", - "RS256", - "RS384", - "RS512", - "PS256", - "PS384", - "PS512", - "ES256", - "ES384", - "ES256K", - ], - "code_challenge_methods_supported": ["plain", "S256"], - "userinfo_endpoint": "http://mas:8080/oauth2/userinfo", - "subject_types_supported": ["public"], - "id_token_signing_alg_values_supported": [ - "RS256", - "RS384", - "RS512", - "ES256", - "ES384", - "PS256", - "PS384", - "PS512", - "ES256K", - ], - "userinfo_signing_alg_values_supported": [ - "RS256", - "RS384", - "RS512", - "ES256", - "ES384", - "PS256", - "PS384", - "PS512", - "ES256K", - ], - "display_values_supported": ["page"], - "claim_types_supported": ["normal"], - "claims_supported": [ - "iss", - "sub", - "aud", - "iat", - "exp", - "nonce", - "auth_time", - "at_hash", - "c_hash", - ], - "claims_parameter_supported": false, - "request_parameter_supported": false, - "request_uri_parameter_supported": false, - "prompt_values_supported": ["none", "login", "create"], - "device_authorization_endpoint": "http://mas:8080/oauth2/device", - "org.matrix.matrix-authentication-service.graphql_endpoint": "http://mas:8080/graphql", - "account_management_uri": "http://mas:8080/account/", - "account_management_actions_supported": [ - "org.matrix.profile", - "org.matrix.sessions_list", - "org.matrix.session_view", - "org.matrix.session_end", - ], - }, - client_id: config.clients[0].client_id, - client_auth_method: config.clients[0].client_auth_method, - client_secret: config.clients[0].client_secret, - admin_token: config.matrix.secret, - account_management_url: `http://${container.getHost()}:${container.getMappedPort(8080)}/account`, - }, - }, - }); - - await use(container); - await container.stop(); + // eslint-disable-next-line no-empty-pattern + mas: async ({}, use) => { + // we stub the mas fixture to allow `homeserver` to depend on it to ensure + // when it is specified by `masHomeserver` it is started before the homeserver + await use(undefined); }, });