diff --git a/package.json b/package.json index b36807f7c3..846a8ca6d9 100644 --- a/package.json +++ b/package.json @@ -178,6 +178,7 @@ "@sentry/webpack-plugin": "^2.7.1", "@stylistic/eslint-plugin": "^2.9.0", "@svgr/webpack": "^8.0.0", + "@testcontainers/postgresql": "^10.16.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.4.8", "@testing-library/react": "^16.0.0", @@ -281,6 +282,7 @@ "stylelint-scss": "^6.0.0", "stylelint-value-no-unknown-custom-properties": "^6.0.1", "terser-webpack-plugin": "^5.3.9", + "testcontainers": "^10.16.0", "ts-node": "^10.9.1", "ts-prune": "^0.10.3", "typescript": "5.7.2", diff --git a/playwright/e2e/app-loading/guest-registration.spec.ts b/playwright/e2e/app-loading/guest-registration.spec.ts index 4455baed23..8e96a937f0 100644 --- a/playwright/e2e/app-loading/guest-registration.spec.ts +++ b/playwright/e2e/app-loading/guest-registration.spec.ts @@ -13,11 +13,13 @@ Please see LICENSE files in the repository root for full details. import { expect, test } from "../../element-web-test"; test.use({ - startHomeserverOpts: "guest-enabled", + synapseConfigOptions: { + allow_guest_access: true, + }, config: async ({ homeserver }, use) => { await use({ default_server_config: { - "m.homeserver": { base_url: homeserver.config.baseUrl }, + "m.homeserver": { base_url: homeserver.baseUrl }, }, }); }, diff --git a/playwright/e2e/crypto/backups.spec.ts b/playwright/e2e/crypto/backups.spec.ts index 40c7dc0ac6..8826cb4595 100644 --- a/playwright/e2e/crypto/backups.spec.ts +++ b/playwright/e2e/crypto/backups.spec.ts @@ -23,19 +23,19 @@ async function expectBackupVersionToBe(page: Page, version: string) { masTest.describe("Encryption state after registration", () => { masTest.skip(isDendrite, "does not yet support MAS"); - masTest("Key backup is enabled by default", async ({ page, mailhog, app }) => { + masTest("Key backup is enabled by default", async ({ page, mailhogClient, app }) => { await page.goto("/#/login"); await page.getByRole("button", { name: "Continue" }).click(); - await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!"); + await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!"); await app.settings.openUserSettings("Security & Privacy"); expect(page.getByText("This session is backing up your keys.")).toBeVisible(); }); - masTest("user is prompted to set up recovery", async ({ page, mailhog, app }) => { + masTest("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, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!"); + await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!"); await page.getByRole("button", { name: "Add room" }).click(); await page.getByRole("menuitem", { name: "New room" }).click(); diff --git a/playwright/e2e/crypto/dehydration.spec.ts b/playwright/e2e/crypto/dehydration.spec.ts index 39629c8262..58bd5847b4 100644 --- a/playwright/e2e/crypto/dehydration.spec.ts +++ b/playwright/e2e/crypto/dehydration.spec.ts @@ -13,14 +13,16 @@ import { viewRoomSummaryByName } from "../right-panel/utils"; import { isDendrite } from "../../plugins/homeserver/dendrite"; const test = base.extend({ - // eslint-disable-next-line no-empty-pattern - startHomeserverOpts: async ({}, use) => { - await use("dehydration"); + synapseConfigOptions: { + experimental_features: { + msc2697_enabled: false, + msc3814_enabled: true, + }, }, config: async ({ homeserver, context }, use) => { const wellKnown = { "m.homeserver": { - base_url: homeserver.config.baseUrl, + base_url: homeserver.baseUrl, }, "org.matrix.msc3814": true, }; diff --git a/playwright/e2e/crypto/utils.ts b/playwright/e2e/crypto/utils.ts index 337ff3d634..3771fd73a7 100644 --- a/playwright/e2e/crypto/utils.ts +++ b/playwright/e2e/crypto/utils.ts @@ -148,7 +148,7 @@ export async function logIntoElement( // select homeserver await page.getByRole("button", { name: "Edit" }).click(); - await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl); + await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.baseUrl); await page.getByRole("button", { name: "Continue", exact: true }).click(); // wait for the dialog to go away diff --git a/playwright/e2e/forgot-password/forgot-password.spec.ts b/playwright/e2e/forgot-password/forgot-password.spec.ts index 0a12514d9e..b307637acd 100644 --- a/playwright/e2e/forgot-password/forgot-password.spec.ts +++ b/playwright/e2e/forgot-password/forgot-password.spec.ts @@ -8,6 +8,8 @@ Please see LICENSE files in the repository root for full details. import { expect, test } from "../../element-web-test"; import { selectHomeserver } from "../utils"; +import { emailHomeserver } from "../../plugins/homeserver/synapse/emailHomeserver.ts"; +import { isDendrite } from "../../plugins/homeserver/dendrite"; const username = "user1234"; // this has to be password-like enough to please zxcvbn. Needless to say it's just from pwgen. @@ -15,16 +17,8 @@ const password = "oETo7MPf0o"; const email = "user@nowhere.dummy"; test.describe("Forgot Password", () => { - test.use({ - startHomeserverOpts: ({ mailhog }, use) => - use({ - template: "email", - variables: { - SMTP_HOST: "host.containers.internal", - SMTP_PORT: mailhog.instance.smtpPort, - }, - }), - }); + test.skip(isDendrite, "not yet wired up"); + test.use(emailHomeserver); test("renders properly", { tag: "@screenshot" }, async ({ page, homeserver }) => { await page.goto("/"); @@ -32,7 +26,7 @@ test.describe("Forgot Password", () => { await page.getByRole("link", { name: "Sign in" }).click(); // need to select a homeserver at this stage, before entering the forgot password flow - await selectHomeserver(page, homeserver.config.baseUrl); + await selectHomeserver(page, homeserver.baseUrl); await page.getByRole("button", { name: "Forgot password?" }).click(); @@ -47,7 +41,7 @@ test.describe("Forgot Password", () => { await page.goto("/"); await page.getByRole("link", { name: "Sign in" }).click(); - await selectHomeserver(page, homeserver.config.baseUrl); + await selectHomeserver(page, homeserver.baseUrl); await page.getByRole("button", { name: "Forgot password?" }).click(); diff --git a/playwright/e2e/login/consent.spec.ts b/playwright/e2e/login/consent.spec.ts index 4d8dd821e0..8fd5837957 100644 --- a/playwright/e2e/login/consent.spec.ts +++ b/playwright/e2e/login/consent.spec.ts @@ -7,10 +7,11 @@ Please see LICENSE files in the repository root for full details. */ import { test, expect } from "../../element-web-test"; +import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts"; test.describe("Consent", () => { + test.use(consentHomeserver); test.use({ - startHomeserverOpts: "consent", displayName: "Bob", }); diff --git a/playwright/e2e/login/login.spec.ts b/playwright/e2e/login/login.spec.ts index e1307f7402..ed0ad6c1b5 100644 --- a/playwright/e2e/login/login.spec.ts +++ b/playwright/e2e/login/login.spec.ts @@ -13,6 +13,8 @@ import { doTokenRegistration } from "./utils"; import { isDendrite } from "../../plugins/homeserver/dendrite"; import { selectHomeserver } from "../utils"; import { Credentials, HomeserverInstance } from "../../plugins/homeserver"; +import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts"; +import { legacyOAuthHomeserver } from "../../plugins/homeserver/synapse/legacyOAuthHomeserver.ts"; const username = "user1234"; const password = "p4s5W0rD"; @@ -70,7 +72,7 @@ const DEVICE_SIGNING_KEYS_BODY = { async function login(page: Page, homeserver: HomeserverInstance) { await page.getByRole("link", { name: "Sign in" }).click(); - await selectHomeserver(page, homeserver.config.baseUrl); + await selectHomeserver(page, homeserver.baseUrl); await page.getByRole("textbox", { name: "Username" }).fill(username); await page.getByPlaceholder("Password").fill(password); @@ -79,7 +81,7 @@ async function login(page: Page, homeserver: HomeserverInstance) { test.describe("Login", () => { test.describe("Password login", () => { - test.use({ startHomeserverOpts: "consent" }); + test.use(consentHomeserver); let creds: Credentials; @@ -101,7 +103,7 @@ test.describe("Login", () => { await page.getByRole("link", { name: "Sign in" }).click(); // first pick the homeserver, as otherwise the user picker won't be visible - await selectHomeserver(page, homeserver.config.baseUrl); + await selectHomeserver(page, homeserver.baseUrl); await page.getByRole("button", { name: "Edit" }).click(); @@ -114,7 +116,7 @@ test.describe("Login", () => { await expect(page.locator(".mx_ServerPicker_server")).toHaveText("server.invalid"); // switch back to the custom homeserver - await selectHomeserver(page, homeserver.config.baseUrl); + await selectHomeserver(page, homeserver.baseUrl); await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible(); // Disabled because flaky - see https://github.com/vector-im/element-web/issues/24688 @@ -142,10 +144,10 @@ test.describe("Login", () => { homeserver, request, }) => { - const res = await request.post( - `${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, - { headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY }, - ); + const res = await request.post(`${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, { + headers: { Authorization: `Bearer ${creds.accessToken}` }, + data: DEVICE_SIGNING_KEYS_BODY, + }); if (res.status() / 100 !== 2) { console.log("Uploading dummy keys failed", await res.json()); } @@ -172,7 +174,7 @@ test.describe("Login", () => { request, }) => { const res = await request.post( - `${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, + `${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, { headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY }, ); if (res.status() / 100 !== 2) { @@ -203,7 +205,7 @@ test.describe("Login", () => { }) => { console.log(`uid ${creds.userId} body`, DEVICE_SIGNING_KEYS_BODY); const res = await request.post( - `${homeserver.config.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, + `${homeserver.baseUrl}/_matrix/client/v3/keys/device_signing/upload`, { headers: { Authorization: `Bearer ${creds.accessToken}` }, data: DEVICE_SIGNING_KEYS_BODY }, ); if (res.status() / 100 !== 2) { @@ -226,14 +228,7 @@ test.describe("Login", () => { // tests for old-style SSO login, in which we exchange tokens with Synapse, and Synapse talks to an auth server test.describe("SSO login", () => { test.skip(isDendrite, "does not yet support SSO"); - - test.use({ - startHomeserverOpts: ({ oAuthServer }, use) => - use({ - template: "default", - oAuthServerPort: oAuthServer.port, - }), - }); + test.use(legacyOAuthHomeserver); test("logs in with SSO and lands on the home screen", async ({ page, homeserver }) => { // If this test fails with a screen showing "Timeout connecting to remote server", it is most likely due to @@ -247,7 +242,7 @@ test.describe("Login", () => { }); test.describe("logout", () => { - test.use({ startHomeserverOpts: "consent" }); + test.use(consentHomeserver); test("should go to login page on logout", async ({ page, user }) => { await page.getByRole("button", { name: "User menu" }).click(); @@ -262,8 +257,8 @@ test.describe("Login", () => { }); test.describe("logout with logout_redirect_url", () => { + test.use(consentHomeserver); test.use({ - startHomeserverOpts: "consent", config: { // We redirect to decoder-ring because it's a predictable page that isn't Element itself. // We could use example.org, matrix.org, or something else, however this puts dependency of external diff --git a/playwright/e2e/login/overwrite_login.spec.ts b/playwright/e2e/login/overwrite_login.spec.ts index 6d06bbc429..6f82c30331 100644 --- a/playwright/e2e/login/overwrite_login.spec.ts +++ b/playwright/e2e/login/overwrite_login.spec.ts @@ -24,7 +24,7 @@ test.describe("Overwrite login action", () => { expect(credentials.userId).not.toBe(bobRegister.userId); const clientCredentials /* IMatrixClientCreds */ = { - homeserverUrl: homeserver.config.baseUrl, + homeserverUrl: homeserver.baseUrl, ...bobRegister, }; diff --git a/playwright/e2e/login/soft_logout.spec.ts b/playwright/e2e/login/soft_logout.spec.ts index ca0c11132a..777fbbd0ae 100644 --- a/playwright/e2e/login/soft_logout.spec.ts +++ b/playwright/e2e/login/soft_logout.spec.ts @@ -11,16 +11,11 @@ import { Page } from "@playwright/test"; import { test, expect } from "../../element-web-test"; import { doTokenRegistration } from "./utils"; import { Credentials } from "../../plugins/homeserver"; -import { isDendrite } from "../../plugins/homeserver/dendrite"; +import { legacyOAuthHomeserver } from "../../plugins/homeserver/synapse/legacyOAuthHomeserver.ts"; test.describe("Soft logout", () => { test.use({ displayName: "Alice", - startHomeserverOpts: ({ oAuthServer }, use) => - use({ - template: "default", - oAuthServerPort: oAuthServer.port, - }), }); test.describe("with password user", () => { @@ -47,8 +42,7 @@ test.describe("Soft logout", () => { }); test.describe("with SSO user", () => { - test.skip(isDendrite, "does not yet support SSO"); - + test.use(legacyOAuthHomeserver); test.use({ user: async ({ page, homeserver }, use) => { const user = await doTokenRegistration(page, homeserver); diff --git a/playwright/e2e/login/utils.ts b/playwright/e2e/login/utils.ts index dc856d586f..ae794bd911 100644 --- a/playwright/e2e/login/utils.ts +++ b/playwright/e2e/login/utils.ts @@ -6,9 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import { Page, expect } from "@playwright/test"; +import { Page, expect, Fixtures } from "@playwright/test"; import { Credentials, HomeserverInstance } from "../../plugins/homeserver"; +import { Services } from "../../services.ts"; /** Visit the login page, choose to log in with "OAuth test", register a new account, and redirect back to Element */ @@ -19,7 +20,7 @@ export async function doTokenRegistration( await page.goto("/#/login"); await page.getByRole("button", { name: "Edit" }).click(); - await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl); + await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.baseUrl); await page.getByRole("button", { name: "Continue" }).click(); // wait for the dialog to go away await expect(page.locator(".mx_ServerPickerDialog")).toHaveCount(0); diff --git a/playwright/e2e/oidc/index.ts b/playwright/e2e/oidc/index.ts index 9403406d80..0ed405df14 100644 --- a/playwright/e2e/oidc/index.ts +++ b/playwright/e2e/oidc/index.ts @@ -10,41 +10,163 @@ import { API, Messages } from "mailhog"; import { Page } from "@playwright/test"; import { test as base, expect } from "../../element-web-test"; -import { MatrixAuthenticationService } from "../../plugins/matrix-authentication-service"; -import { StartHomeserverOpts } from "../../plugins/homeserver"; -export const test = base.extend<{ - masPrepare: MatrixAuthenticationService; - mas: MatrixAuthenticationService; -}>({ - // There's a bit of a chicken and egg problem between MAS & Synapse where they each need to know how to reach each other - // so spinning up a MAS is split into the prepare & start stage: prepare mas -> homeserver -> start mas to disentangle this. - masPrepare: async ({ context }, use) => { - const mas = new MatrixAuthenticationService(context); - await mas.prepare(); - await use(mas); - }, - mas: [ - async ({ masPrepare: mas, homeserver, mailhog }, use, testInfo) => { - await mas.start(homeserver, mailhog.instance); - await use(mas); - await mas.stop(testInfo); - }, - { auto: true }, - ], - startHomeserverOpts: async ({ masPrepare }, use) => { +export const test = base.extend<{}>({ + synapseConfigOptions: async ({ mas }, use) => { await use({ - template: "mas-oidc", - variables: { - MAS_PORT: masPrepare.port, + 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://localhost:${mas.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: "0000000000000000000SYNAPSE", + client_auth_method: "client_secret_basic", + client_secret: "SomeRandomSecret", + admin_token: "AnotherRandomSecret", + account_management_url: `http://localhost:${mas.getMappedPort(8080)}/account`, + }, }, }); }, - config: async ({ homeserver, startHomeserverOpts, context }, use) => { - const issuer = `http://localhost:${(startHomeserverOpts as StartHomeserverOpts).variables["MAS_PORT"]}/`; + config: async ({ homeserver, mas, context }, use) => { + const issuer = `http://localhost:${mas.getMappedPort(8080)}/`; const wellKnown = { "m.homeserver": { - base_url: homeserver.config.baseUrl, + base_url: homeserver.baseUrl, }, "org.matrix.msc2965.authentication": { issuer, diff --git a/playwright/e2e/oidc/oidc-native.spec.ts b/playwright/e2e/oidc/oidc-native.spec.ts index b523f37f3b..78696d75ef 100644 --- a/playwright/e2e/oidc/oidc-native.spec.ts +++ b/playwright/e2e/oidc/oidc-native.spec.ts @@ -7,22 +7,22 @@ Please see LICENSE files in the repository root for full details. */ import { test, expect, registerAccountMas } from "."; -import { isDendrite } from "../../plugins/homeserver/dendrite"; import { ElementAppPage } from "../../pages/ElementAppPage.ts"; +import { isDendrite } from "../../plugins/homeserver/dendrite"; test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { 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, mailhog, mas }) => { - const tokenUri = `http://localhost:${mas.port}/oauth2/token`; + 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 tokenApiPromise = page.waitForRequest( (request) => request.url() === tokenUri && request.postDataJSON()["grant_type"] === "authorization_code", ); await page.goto("/#/login"); await page.getByRole("button", { name: "Continue" }).click(); - await registerAccountMas(page, mailhog.api, "alice", "alice@email.com", "Pa$sW0rD!"); + await registerAccountMas(page, mailhogClient, "alice", "alice@email.com", "Pa$sW0rD!"); // Eventually, we should end up at the home screen. await expect(page).toHaveURL(/\/#\/home$/, { timeout: 10000 }); @@ -49,7 +49,7 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => { await newPage.close(); // Assert logging out revokes both tokens - const revokeUri = `http://localhost:${mas.port}/oauth2/revoke`; + const revokeUri = `http://localhost:${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/register/email.spec.ts b/playwright/e2e/register/email.spec.ts index 665e20ef01..0c42b7062d 100644 --- a/playwright/e2e/register/email.spec.ts +++ b/playwright/e2e/register/email.spec.ts @@ -7,25 +7,18 @@ Please see LICENSE files in the repository root for full details. */ import { test, expect } from "../../element-web-test"; +import { emailHomeserver } from "../../plugins/homeserver/synapse/emailHomeserver.ts"; import { isDendrite } from "../../plugins/homeserver/dendrite"; test.describe("Email Registration", async () => { test.skip(isDendrite, "not yet wired up"); - + test.use(emailHomeserver); test.use({ - startHomeserverOpts: ({ mailhog }, use) => - use({ - template: "email", - variables: { - SMTP_HOST: "host.containers.internal", - SMTP_PORT: mailhog.instance.smtpPort, - }, - }), config: ({ homeserver }, use) => use({ default_server_config: { "m.homeserver": { - base_url: homeserver.config.baseUrl, + base_url: homeserver.baseUrl, }, "m.identity_server": { base_url: "https://server.invalid", @@ -41,7 +34,7 @@ test.describe("Email Registration", async () => { test( "registers an account and lands on the use case selection screen", { tag: "@screenshot" }, - async ({ page, mailhog, request, checkA11y }) => { + async ({ page, mailhogClient, request, checkA11y }) => { await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible(); // Hide the server text as it contains the randomly allocated Homeserver port const screenshotOptions = { mask: [page.locator(".mx_ServerPicker_server")] }; @@ -58,7 +51,7 @@ test.describe("Email Registration", async () => { await expect(page.getByText("An error was encountered when sending the email")).not.toBeVisible(); - const messages = await mailhog.api.messages(); + const messages = await mailhogClient.messages(); expect(messages.items).toHaveLength(1); expect(messages.items[0].to).toEqual("alice@email.com"); const [emailLink] = messages.items[0].text.match(/http.+/); diff --git a/playwright/e2e/register/register.spec.ts b/playwright/e2e/register/register.spec.ts index c127436266..63fe24dffd 100644 --- a/playwright/e2e/register/register.spec.ts +++ b/playwright/e2e/register/register.spec.ts @@ -7,11 +7,10 @@ Please see LICENSE files in the repository root for full details. */ import { test, expect } from "../../element-web-test"; +import { consentHomeserver } from "../../plugins/homeserver/synapse/consentHomeserver.ts"; test.describe("Registration", () => { - test.use({ - startHomeserverOpts: "consent", - }); + test.use(consentHomeserver); test.beforeEach(async ({ page }) => { await page.goto("/#/register"); @@ -27,7 +26,7 @@ test.describe("Registration", () => { await expect(page.locator(".mx_Dialog")).toMatchScreenshot("server-picker.png"); await checkA11y(); - await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl); + await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.baseUrl); await page.getByRole("button", { name: "Continue", exact: true }).click(); // wait for the dialog to go away await expect(page.getByRole("dialog")).not.toBeVisible(); @@ -88,7 +87,7 @@ test.describe("Registration", () => { test("should require username to fulfil requirements and be available", async ({ homeserver, page }) => { await page.getByRole("button", { name: "Edit", exact: true }).click(); await expect(page.getByRole("button", { name: "Continue", exact: true })).toBeVisible(); - await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl); + await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.baseUrl); await page.getByRole("button", { name: "Continue", exact: true }).click(); // wait for the dialog to go away await expect(page.getByRole("dialog")).not.toBeVisible(); diff --git a/playwright/e2e/sliding-sync/sliding-sync.spec.ts b/playwright/e2e/sliding-sync/sliding-sync.spec.ts index f812fe7aec..d105904a48 100644 --- a/playwright/e2e/sliding-sync/sliding-sync.spec.ts +++ b/playwright/e2e/sliding-sync/sliding-sync.spec.ts @@ -7,21 +7,29 @@ Please see LICENSE files in the repository root for full details. */ import { Page, Request } from "@playwright/test"; +import { GenericContainer, StartedTestContainer } from "testcontainers"; import { test as base, expect } from "../../element-web-test"; import type { ElementAppPage } from "../../pages/ElementAppPage"; import type { Bot } from "../../pages/bot"; -import { ProxyInstance, SlidingSyncProxy } from "../../plugins/sliding-sync-proxy"; const test = base.extend<{ - slidingSyncProxy: ProxyInstance; + slidingSyncProxy: StartedTestContainer; testRoom: { roomId: string; name: string }; joinedBot: Bot; }>({ - slidingSyncProxy: async ({ context, page, homeserver }, use) => { - const proxy = new SlidingSyncProxy(homeserver.config.dockerUrl, context); - const proxyInstance = await proxy.start(); - const proxyAddress = `http://localhost:${proxyInstance.port}`; + slidingSyncProxy: async ({ network, postgres, page, homeserver }, use, testInfo) => { + const container = await new GenericContainer("ghcr.io/matrix-org/sliding-sync:v0.99.3") + .withNetwork(network) + .withExposedPorts(8008) + .withEnvironment({ + SYNCV3_SECRET: "bwahahaha", + SYNCV3_DB: `user=postgres dbname=postgres password=${postgres.getPassword()} host=${postgres.getHost()} sslmode=disable`, + SYNCV3_SERVER: `http://${homeserver.getNetworkNames()[0]}:8008`, + }) + .start(); + + const proxyAddress = `http://localhost:${container.getMappedPort(8008)}`; await page.addInitScript((proxyAddress) => { window.localStorage.setItem( "mx_local_settings", @@ -31,8 +39,8 @@ const test = base.extend<{ ); window.localStorage.setItem("mx_labs_feature_feature_sliding_sync", "true"); }, proxyAddress); - await use(proxyInstance); - await proxy.stop(); + await use(container); + await container.stop(); }, // Ensure slidingSyncProxy is set up before the user fixture as it relies on an init script credentials: async ({ slidingSyncProxy, credentials }, use) => { diff --git a/playwright/element-web-test.ts b/playwright/element-web-test.ts index 8206d76609..e53ebc0818 100644 --- a/playwright/element-web-test.ts +++ b/playwright/element-web-test.ts @@ -6,24 +6,20 @@ 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 as base, expect as baseExpect, Locator, Page, ExpectMatcherState, ElementHandle } from "@playwright/test"; +import { expect as baseExpect, Locator, Page, ExpectMatcherState, ElementHandle } from "@playwright/test"; import { sanitizeForFilePath } from "playwright-core/lib/utils"; import AxeBuilder from "@axe-core/playwright"; import _ from "lodash"; -import { basename, extname } from "node:path"; +import { extname } from "node:path"; -import type mailhog from "mailhog"; import type { IConfigOptions } from "../src/IConfigOptions"; -import { Credentials, Homeserver, HomeserverInstance, StartHomeserverOpts } from "./plugins/homeserver"; -import { Synapse } from "./plugins/homeserver/synapse"; -import { Dendrite, Pinecone } from "./plugins/homeserver/dendrite"; -import { Instance, MailHogServer } from "./plugins/mailhog"; +import { Credentials } from "./plugins/homeserver"; import { ElementAppPage } from "./pages/ElementAppPage"; -import { OAuthServer } from "./plugins/oauth_server"; import { Crypto } from "./pages/crypto"; import { Toasts } from "./pages/toasts"; import { Bot, CreateBotOpts } from "./pages/bot"; import { Webserver } from "./plugins/webserver"; +import { test as base } from "./services.ts"; // Enable experimental service worker support // See https://playwright.dev/docs/service-workers-experimental#how-to-enable @@ -68,14 +64,6 @@ export interface Fixtures { */ config: typeof CONFIG_JSON; - /** - * The options with which to run the {@link #homeserver} fixture. - */ - startHomeserverOpts: StartHomeserverOpts | string; - - homeserver: HomeserverInstance; - oAuthServer: { port: number }; - /** * The displayname to use for the user registered in {@link #credentials}. * @@ -113,7 +101,6 @@ export interface Fixtures { */ app: ElementAppPage; - mailhog: { api: mailhog.API; instance: Instance }; crypto: Crypto; room?: { roomId: string }; toasts: Toasts; @@ -150,45 +137,6 @@ export const test = base.extend({ await use(page); }, - startHomeserverOpts: "default", - homeserver: async ({ request, startHomeserverOpts: opts }, use, testInfo) => { - if (typeof opts === "string") { - opts = { template: opts }; - } - - let server: Homeserver; - const homeserverName = process.env["PLAYWRIGHT_HOMESERVER"]; - switch (homeserverName) { - case "dendrite": - server = new Dendrite(request); - break; - case "pinecone": - server = new Pinecone(request); - break; - default: - server = new Synapse(request); - } - - await use(await server.start(opts)); - const logs = await server.stop(); - - if (testInfo.status !== "passed") { - for (const path of logs) { - await testInfo.attach(`homeserver-${basename(path)}`, { - path, - contentType: "text/plain", - }); - } - } - }, - // eslint-disable-next-line no-empty-pattern - oAuthServer: async ({}, use) => { - const server = new OAuthServer(); - const port = server.start(); - await use({ port }); - server.stop(); - }, - displayName: undefined, credentials: async ({ homeserver, displayName: testDisplayName }, use) => { const names = ["Alice", "Bob", "Charlie", "Daniel", "Eve", "Frank", "Grace", "Hannah", "Isaac", "Judy"]; @@ -220,7 +168,7 @@ export const test = base.extend({ // Ensure the language is set to a consistent value window.localStorage.setItem("mx_local_settings", '{"language":"en"}'); }, - { baseUrl: homeserver.config.baseUrl, credentials }, + { baseUrl: homeserver.baseUrl, credentials }, ); await use(page); }, @@ -265,14 +213,6 @@ export const test = base.extend({ await use(bot); }, - // eslint-disable-next-line no-empty-pattern - mailhog: async ({}, use) => { - const mailhog = new MailHogServer(); - const instance = await mailhog.start(); - await use(instance); - await mailhog.stop(); - }, - // eslint-disable-next-line no-empty-pattern webserver: async ({}, use) => { const webserver = new Webserver(); diff --git a/playwright/pages/bot.ts b/playwright/pages/bot.ts index d50a0e84ee..6b5b2a326c 100644 --- a/playwright/pages/bot.ts +++ b/playwright/pages/bot.ts @@ -97,7 +97,7 @@ export class Bot extends Client { private async buildClient(): Promise> { const credentials = await this.getCredentials(); const clientHandle = await this.page.evaluateHandle( - async ({ homeserver, credentials, opts }) => { + async ({ baseUrl, credentials, opts }) => { function getLogger(loggerName: string): Logger { const logger = { getChild: (namespace: string) => getLogger(`${loggerName}:${namespace}`), @@ -157,7 +157,7 @@ export class Bot extends Client { }; const cli = new window.matrixcs.MatrixClient({ - baseUrl: homeserver.baseUrl, + baseUrl, userId: credentials.userId, deviceId: credentials.deviceId, accessToken: credentials.accessToken, @@ -179,7 +179,7 @@ export class Bot extends Client { return cli; }, { - homeserver: this.homeserver.config, + baseUrl: this.homeserver.baseUrl, credentials, opts: this.opts, }, diff --git a/playwright/pages/crypto.ts b/playwright/pages/crypto.ts index f221412a7c..138dfa1c00 100644 --- a/playwright/pages/crypto.ts +++ b/playwright/pages/crypto.ts @@ -27,7 +27,7 @@ export class Crypto { accessToken: window.mxMatrixClientPeg.get().getAccessToken(), })); - const res = await this.request.post(`${this.homeserver.config.baseUrl}/_matrix/client/v3/keys/query`, { + const res = await this.request.post(`${this.homeserver.baseUrl}/_matrix/client/v3/keys/query`, { headers: { Authorization: `Bearer ${accessToken}` }, data: { device_keys: { [userId]: [] } }, }); diff --git a/playwright/plugins/docker/index.ts b/playwright/plugins/docker/index.ts deleted file mode 100644 index 6cc13860be..0000000000 --- a/playwright/plugins/docker/index.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* -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 * as os from "os"; -import * as crypto from "crypto"; -import * as childProcess from "child_process"; -import * as fse from "fs-extra"; - -/** - * @param cmd - command to execute - * @param args - arguments to pass to executed command - * @param suppressOutput - whether to suppress the stdout and stderr resulting from this command. - * @return Promise which resolves to an object containing the string value of what was - * written to stdout and stderr by the executed command. - */ -const exec = (cmd: string, args: string[], suppressOutput = false): Promise<{ stdout: string; stderr: string }> => { - return new Promise((resolve, reject) => { - if (!suppressOutput) { - const log = ["Running command:", cmd, ...args, "\n"].join(" "); - // When in CI mode we combine reports from multiple runners into a single HTML report - // which has separate files for stdout and stderr, so we print the executed command to both - process.stdout.write(log); - if (process.env.CI) process.stderr.write(log); - } - const { stdout, stderr } = childProcess.execFile(cmd, args, { encoding: "utf8" }, (err, stdout, stderr) => { - if (err) reject(err); - resolve({ stdout, stderr }); - if (!suppressOutput) { - process.stdout.write("\n"); - if (process.env.CI) process.stderr.write("\n"); - } - }); - if (!suppressOutput) { - stdout.pipe(process.stdout); - stderr.pipe(process.stderr); - } - }); -}; - -export class Docker { - public id: string; - - async run(opts: { image: string; containerName: string; params?: string[]; cmd?: string[] }): Promise { - const userInfo = os.userInfo(); - const params = opts.params ?? []; - - const isPodman = await Docker.isPodman(); - if (params.includes("-v") && userInfo.uid >= 0) { - // Run the docker container as our uid:gid to prevent problems with permissions. - if (isPodman) { - // Note: this setup is for podman rootless containers. - - // In podman, run as root in the container, which maps to the current - // user on the host. This is probably the default since Synapse's - // Dockerfile doesn't specify, but we're being explicit here - // because it's important for the permissions to work. - params.push("-u", "0:0"); - - // Tell Synapse not to switch UID - params.push("-e", "UID=0"); - params.push("-e", "GID=0"); - } else { - params.push("-u", `${userInfo.uid}:${userInfo.gid}`); - } - } - - // Make host.containers.internal work to allow the container to talk to other services via host ports. - if (isPodman) { - params.push("--network"); - params.push("slirp4netns:allow_host_loopback=true"); - } else { - // Docker for Desktop includes a host-gateway mapping on host.docker.internal but to simplify the config - // we use the Podman variant host.containers.internal in all environments. - params.push("--add-host"); - params.push("host.containers.internal:host-gateway"); - } - - // Provided we are not running in CI, add a `--rm` parameter. - // There is no need to remove containers in CI (since they are automatically removed anyway), and - // `--rm` means that if a container crashes this means its logs are wiped out. - if (!process.env.CI) params.unshift("--rm"); - - const args = [ - "run", - "--name", - `${opts.containerName}-${crypto.randomBytes(4).toString("hex")}`, - "-d", - ...params, - opts.image, - ]; - - if (opts.cmd) args.push(...opts.cmd); - - const { stdout } = await exec("docker", args); - this.id = stdout.trim(); - return this.id; - } - - async stop(): Promise { - try { - await exec("docker", ["stop", this.id]); - } catch (err) { - console.error(`Failed to stop docker container`, this.id, err); - } - } - - /** - * @param params - list of parameters to pass to `docker exec` - * @param suppressOutput - whether to suppress the stdout and stderr resulting from this command. - */ - async exec(params: string[], suppressOutput = true): Promise { - await exec("docker", ["exec", this.id, ...params], suppressOutput); - } - - async getContainerIp(): Promise { - const { stdout } = await exec("docker", ["inspect", "-f", "{{ .NetworkSettings.IPAddress }}", this.id]); - return stdout.trim(); - } - - async persistLogsToFile(args: { stdoutFile?: string; stderrFile?: string }): Promise { - const stdoutFile = args.stdoutFile ? await fse.open(args.stdoutFile, "w") : "ignore"; - const stderrFile = args.stderrFile ? await fse.open(args.stderrFile, "w") : "ignore"; - await new Promise((resolve) => { - childProcess - .spawn("docker", ["logs", this.id], { - stdio: ["ignore", stdoutFile, stderrFile], - }) - .once("close", resolve); - }); - if (args.stdoutFile) await fse.close(stdoutFile); - if (args.stderrFile) await fse.close(stderrFile); - } - - /** - * Detects whether the docker command is actually podman. - * To do this, it looks for "podman" in the output of "docker --help". - */ - static _isPodman?: boolean; - static async isPodman(): Promise { - if (Docker._isPodman === undefined) { - const { stdout } = await exec("docker", ["--help"], true); - Docker._isPodman = stdout.toLowerCase().includes("podman"); - } - return Docker._isPodman; - } -} diff --git a/playwright/plugins/homeserver/dendrite/index.ts b/playwright/plugins/homeserver/dendrite/index.ts index 0886dc1586..9bb1674649 100644 --- a/playwright/plugins/homeserver/dendrite/index.ts +++ b/playwright/plugins/homeserver/dendrite/index.ts @@ -6,142 +6,39 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import * as path from "node:path"; -import * as os from "node:os"; -import * as fse from "fs-extra"; - -import { getFreePort } from "../../utils/port"; -import { Homeserver, HomeserverConfig, HomeserverInstance, StartHomeserverOpts } from "../"; -import { randB64Bytes } from "../../utils/rand"; -import { Synapse } from "../synapse"; -import { Docker } from "../../docker"; - -const dockerConfigDir = "/etc/dendrite/"; -const dendriteConfigFile = "dendrite.yaml"; - -// Surprisingly, Dendrite implements the same register user Admin API Synapse, so we can just extend it -export class Dendrite extends Synapse implements Homeserver, HomeserverInstance { - protected image = "matrixdotorg/dendrite-monolith:main"; - protected entrypoint = "/usr/bin/dendrite"; - - /** - * Start a dendrite instance: the template must be the name of one of the templates - * in the playwright/plugins/dendritedocker/templates directory - * @param opts - */ - public async start(opts: StartHomeserverOpts): Promise { - const denCfg = await cfgDirFromTemplate(this.image, opts); - - console.log(`Starting dendrite with config dir ${denCfg.configDir}...`); - - const dendriteId = await this.docker.run({ - image: this.image, - params: [ - "-v", - `${denCfg.configDir}:` + dockerConfigDir, - "-p", - `${denCfg.port}:8008/tcp`, - "--entrypoint", - this.entrypoint, - ], - containerName: `react-sdk-playwright-dendrite`, - cmd: ["--config", dockerConfigDir + dendriteConfigFile, "--really-enable-open-registration", "true", "run"], - }); - - console.log(`Started dendrite with id ${dendriteId} on port ${denCfg.port}.`); - - // Await Dendrite healthcheck - await this.docker.exec([ - "curl", - "--connect-timeout", - "30", - "--retry", - "30", - "--retry-delay", - "1", - "--retry-all-errors", - "--silent", - "http://localhost:8008/_matrix/client/versions", - ]); - - const dockerUrl = `http://${await this.docker.getContainerIp()}:8008`; - this.config = { - ...denCfg, - serverId: dendriteId, - dockerUrl, - }; - return this; - } - - public async stop(): Promise { - if (!this.config) throw new Error("Missing existing dendrite instance, did you call stop() before start()?"); - - const dendriteLogsPath = path.join("playwright", "dendritelogs", this.config.serverId); - await fse.ensureDir(dendriteLogsPath); - - await this.docker.persistLogsToFile({ - stdoutFile: path.join(dendriteLogsPath, "stdout.log"), - stderrFile: path.join(dendriteLogsPath, "stderr.log"), - }); - - await this.docker.stop(); - - await fse.remove(this.config.configDir); - - console.log(`Stopped dendrite id ${this.config.serverId}.`); - - return [path.join(dendriteLogsPath, "stdout.log"), path.join(dendriteLogsPath, "stderr.log")]; - } -} - -export class Pinecone extends Dendrite { - protected image = "matrixdotorg/dendrite-demo-pinecone:main"; - protected entrypoint = "/usr/bin/dendrite-demo-pinecone"; -} - -async function cfgDirFromTemplate( - dendriteImage: string, - opts: StartHomeserverOpts, -): Promise> { - const template = "default"; // XXX: for now we only have one template - const templateDir = path.join(__dirname, "templates", template); - - const stats = await fse.stat(templateDir); - if (!stats?.isDirectory) { - throw new Error(`No such template: ${template}`); - } - const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-dendritedocker-")); - - // copy the contents of the template dir, omitting homeserver.yaml as we'll template that - console.log(`Copy ${templateDir} -> ${tempDir}`); - await fse.copy(templateDir, tempDir, { filter: (f) => path.basename(f) !== dendriteConfigFile }); - - const registrationSecret = randB64Bytes(16); - - const port = await getFreePort(); - const baseUrl = `http://localhost:${port}`; - - // now copy homeserver.yaml, applying substitutions - console.log(`Gen ${path.join(templateDir, dendriteConfigFile)}`); - let hsYaml = await fse.readFile(path.join(templateDir, dendriteConfigFile), "utf8"); - hsYaml = hsYaml.replace(/{{REGISTRATION_SECRET}}/g, registrationSecret); - await fse.writeFile(path.join(tempDir, dendriteConfigFile), hsYaml); - - const docker = new Docker(); - await docker.run({ - image: dendriteImage, - params: ["--entrypoint=", "-v", `${tempDir}:/mnt`], - containerName: `react-sdk-playwright-dendrite-keygen`, - cmd: ["/usr/bin/generate-keys", "-private-key", "/mnt/matrix_key.pem"], - }); - - return { - port, - baseUrl, - configDir: tempDir, - registrationSecret, - }; -} +// export const dendriteHomeserver: Fixtures & Fixtures = { +// _homeserver: async ({ request }, use) => { +// const container = new SynapseContainer(request); +// await use(container); +// +// container.withConfig({ +// oidc_providers: [ +// { +// idp_id: "test", +// idp_name: "OAuth test", +// issuer: `http://localhost:${port}/oauth`, +// authorization_endpoint: `http://localhost:${port}/oauth/auth.html`, +// // the token endpoint receives requests from synapse, +// // rather than the webapp, so needs to escape the docker container. +// token_endpoint: `http://host.testcontainers.internal:${port}/oauth/token`, +// userinfo_endpoint: `http://host.testcontainers.internal:${port}/oauth/userinfo`, +// client_id: "synapse", +// discover: false, +// scopes: ["profile"], +// skip_verification: true, +// client_auth_method: "none", +// user_mapping_provider: { +// config: { +// display_name_template: "{{ user.name }}", +// }, +// }, +// }, +// ], +// }); +// await use(container); +// server.stop(); +// }, +// }; export function isDendrite(): boolean { return process.env["PLAYWRIGHT_HOMESERVER"] === "dendrite" || process.env["PLAYWRIGHT_HOMESERVER"] === "pinecone"; diff --git a/playwright/plugins/homeserver/dendrite/templates/default/dendrite.yaml b/playwright/plugins/homeserver/dendrite/templates/default/dendrite.yaml deleted file mode 100644 index 634cebbc87..0000000000 --- a/playwright/plugins/homeserver/dendrite/templates/default/dendrite.yaml +++ /dev/null @@ -1,378 +0,0 @@ -# This is the Dendrite configuration file. -# -# The configuration is split up into sections - each Dendrite component has a -# configuration section, in addition to the "global" section which applies to -# all components. - -# The version of the configuration file. -version: 2 - -# Global Matrix configuration. This configuration applies to all components. -global: - # The domain name of this homeserver. - server_name: localhost - - # The path to the signing private key file, used to sign requests and events. - # Note that this is NOT the same private key as used for TLS! To generate a - # signing key, use "./bin/generate-keys --private-key matrix_key.pem". - private_key: matrix_key.pem - - # The paths and expiry timestamps (as a UNIX timestamp in millisecond precision) - # to old signing keys that were formerly in use on this domain name. These - # keys will not be used for federation request or event signing, but will be - # provided to any other homeserver that asks when trying to verify old events. - old_private_keys: - # If the old private key file is available: - # - private_key: old_matrix_key.pem - # expired_at: 1601024554498 - # If only the public key (in base64 format) and key ID are known: - # - public_key: mn59Kxfdq9VziYHSBzI7+EDPDcBS2Xl7jeUdiiQcOnM= - # key_id: ed25519:mykeyid - # expired_at: 1601024554498 - - # How long a remote server can cache our server signing key before requesting it - # again. Increasing this number will reduce the number of requests made by other - # servers for our key but increases the period that a compromised key will be - # considered valid by other homeservers. - key_validity_period: 168h0m0s - - # Global database connection pool, for PostgreSQL monolith deployments only. If - # this section is populated then you can omit the "database" blocks in all other - # sections. For polylith deployments, or monolith deployments using SQLite databases, - # you must configure the "database" block for each component instead. - # database: - # connection_string: postgresql://username:password@hostname/dendrite?sslmode=disable - # max_open_conns: 90 - # max_idle_conns: 5 - # conn_max_lifetime: -1 - - # Configuration for in-memory caches. Caches can often improve performance by - # keeping frequently accessed items (like events, identifiers etc.) in memory - # rather than having to read them from the database. - cache: - # The estimated maximum size for the global cache in bytes, or in terabytes, - # gigabytes, megabytes or kilobytes when the appropriate 'tb', 'gb', 'mb' or - # 'kb' suffix is specified. Note that this is not a hard limit, nor is it a - # memory limit for the entire process. A cache that is too small may ultimately - # provide little or no benefit. - max_size_estimated: 1gb - - # The maximum amount of time that a cache entry can live for in memory before - # it will be evicted and/or refreshed from the database. Lower values result in - # easier admission of new cache entries but may also increase database load in - # comparison to higher values, so adjust conservatively. Higher values may make - # it harder for new items to make it into the cache, e.g. if new rooms suddenly - # become popular. - max_age: 1h - - # The server name to delegate server-server communications to, with optional port - # e.g. localhost:443 - well_known_server_name: "" - - # The server name to delegate client-server communications to, with optional port - # e.g. localhost:443 - well_known_client_name: "" - - # Lists of domains that the server will trust as identity servers to verify third - # party identifiers such as phone numbers and email addresses. - trusted_third_party_id_servers: - - matrix.org - - vector.im - - # Disables federation. Dendrite will not be able to communicate with other servers - # in the Matrix federation and the federation API will not be exposed. - disable_federation: false - - # Configures the handling of presence events. Inbound controls whether we receive - # presence events from other servers, outbound controls whether we send presence - # events for our local users to other servers. - presence: - enable_inbound: false - enable_outbound: false - - # Configures phone-home statistics reporting. These statistics contain the server - # name, number of active users and some information on your deployment config. - # We use this information to understand how Dendrite is being used in the wild. - report_stats: - enabled: false - endpoint: https://matrix.org/report-usage-stats/push - - # Server notices allows server admins to send messages to all users on the server. - server_notices: - enabled: false - # The local part, display name and avatar URL (as a mxc:// URL) for the user that - # will send the server notices. These are visible to all users on the deployment. - local_part: "_server" - display_name: "Server Alerts" - avatar_url: "" - # The room name to be used when sending server notices. This room name will - # appear in user clients. - room_name: "Server Alerts" - - # Configuration for NATS JetStream - jetstream: - # A list of NATS Server addresses to connect to. If none are specified, an - # internal NATS server will be started automatically when running Dendrite in - # monolith mode. For polylith deployments, it is required to specify the address - # of at least one NATS Server node. - addresses: - # - localhost:4222 - - # Disable the validation of TLS certificates of NATS. This is - # not recommended in production since it may allow NATS traffic - # to be sent to an insecure endpoint. - disable_tls_validation: false - - # Persistent directory to store JetStream streams in. This directory should be - # preserved across Dendrite restarts. - storage_path: ./ - - # The prefix to use for stream names for this homeserver - really only useful - # if you are running more than one Dendrite server on the same NATS deployment. - topic_prefix: Dendrite - - # Configuration for Prometheus metric collection. - metrics: - enabled: false - basic_auth: - username: metrics - password: metrics - - # Optional DNS cache. The DNS cache may reduce the load on DNS servers if there - # is no local caching resolver available for use. - dns_cache: - enabled: false - cache_size: 256 - cache_lifetime: "5m" # 5 minutes; https://pkg.go.dev/time@master#ParseDuration - -# Configuration for the Appservice API. -app_service_api: - # Disable the validation of TLS certificates of appservices. This is - # not recommended in production since it may allow appservice traffic - # to be sent to an insecure endpoint. - disable_tls_validation: false - - # Appservice configuration files to load into this homeserver. - config_files: - # - /path/to/appservice_registration.yaml - -# Configuration for the Client API. -client_api: - # Prevents new users from being able to register on this homeserver, except when - # using the registration shared secret below. - registration_disabled: false - - # Prevents new guest accounts from being created. Guest registration is also - # disabled implicitly by setting 'registration_disabled' above. - guests_disabled: true - - # If set, allows registration by anyone who knows the shared secret, regardless - # of whether registration is otherwise disabled. - registration_shared_secret: "{{REGISTRATION_SECRET}}" - - # Whether to require reCAPTCHA for registration. If you have enabled registration - # then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used - # for coordinated spam attacks. - enable_registration_captcha: false - - # Settings for ReCAPTCHA. - recaptcha_public_key: "" - recaptcha_private_key: "" - recaptcha_bypass_secret: "" - - # To use hcaptcha.com instead of ReCAPTCHA, set the following parameters, otherwise just keep them empty. - # recaptcha_siteverify_api: "https://hcaptcha.com/siteverify" - # recaptcha_api_js_url: "https://js.hcaptcha.com/1/api.js" - # recaptcha_form_field: "h-captcha-response" - # recaptcha_sitekey_class: "h-captcha" - - # TURN server information that this homeserver should send to clients. - turn: - turn_user_lifetime: "5m" - turn_uris: - # - turn:turn.server.org?transport=udp - # - turn:turn.server.org?transport=tcp - turn_shared_secret: "" - # If your TURN server requires static credentials, then you will need to enter - # them here instead of supplying a shared secret. Note that these credentials - # will be visible to clients! - # turn_username: "" - # turn_password: "" - - # Settings for rate-limited endpoints. Rate limiting kicks in after the threshold - # number of "slots" have been taken by requests from a specific host. Each "slot" - # will be released after the cooloff time in milliseconds. Server administrators - # and appservice users are exempt from rate limiting by default. - rate_limiting: - enabled: true - threshold: 20 - cooloff_ms: 500 - exempt_user_ids: - # - "@user:domain.com" - -# Configuration for the Federation API. -federation_api: - # How many times we will try to resend a failed transaction to a specific server. The - # backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc. Once - # the max retries are exceeded, Dendrite will no longer try to send transactions to - # that server until it comes back to life and connects to us again. - send_max_retries: 16 - - # Disable the validation of TLS certificates of remote federated homeservers. Do not - # enable this option in production as it presents a security risk! - disable_tls_validation: false - - # Disable HTTP keepalives, which also prevents connection reuse. Dendrite will typically - # keep HTTP connections open to remote hosts for 5 minutes as they can be reused much - # more quickly than opening new connections each time. Disabling keepalives will close - # HTTP connections immediately after a successful request but may result in more CPU and - # memory being used on TLS handshakes for each new connection instead. - disable_http_keepalives: false - - # Perspective keyservers to use as a backup when direct key fetches fail. This may - # be required to satisfy key requests for servers that are no longer online when - # joining some rooms. - key_perspectives: - - server_name: matrix.org - keys: - - key_id: ed25519:auto - public_key: Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw - - key_id: ed25519:a_RXGa - public_key: l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ - - # This option will control whether Dendrite will prefer to look up keys directly - # or whether it should try perspective servers first, using direct fetches as a - # last resort. - prefer_direct_fetch: false - - database: - connection_string: file:dendrite-federationapi.db - -# Configuration for the Media API. -media_api: - # Storage path for uploaded media. May be relative or absolute. - base_path: ./media_store - - # The maximum allowed file size (in bytes) for media uploads to this homeserver - # (0 = unlimited). If using a reverse proxy, ensure it allows requests at least - #this large (e.g. the client_max_body_size setting in nginx). - max_file_size_bytes: 10485760 - - # Whether to dynamically generate thumbnails if needed. - dynamic_thumbnails: false - - # The maximum number of simultaneous thumbnail generators to run. - max_thumbnail_generators: 10 - - # A list of thumbnail sizes to be generated for media content. - thumbnail_sizes: - - width: 32 - height: 32 - method: crop - - width: 96 - height: 96 - method: crop - - width: 640 - height: 480 - method: scale - - database: - connection_string: file:dendrite-mediaapi.db - -# Configuration for enabling experimental MSCs on this homeserver. -mscs: - mscs: - # - msc2836 # (Threading, see https://github.com/matrix-org/matrix-doc/pull/2836) - # - msc2946 # (Spaces Summary, see https://github.com/matrix-org/matrix-doc/pull/2946) - - database: - connection_string: file:dendrite-msc.db - -# Configuration for the Sync API. -sync_api: - # This option controls which HTTP header to inspect to find the real remote IP - # address of the client. This is likely required if Dendrite is running behind - # a reverse proxy server. - # real_ip_header: X-Real-IP - - # Configuration for the full-text search engine. - search: - # Whether or not search is enabled. - enabled: false - - # The path where the search index will be created in. - index_path: "./searchindex" - - # The language most likely to be used on the server - used when indexing, to - # ensure the returned results match expectations. A full list of possible languages - # can be found at https://github.com/blevesearch/bleve/tree/master/analysis/lang - language: "en" - - database: - connection_string: file:dendrite-syncapi.db - -# Configuration for the User API. -user_api: - # The cost when hashing passwords on registration/login. Default: 10. Min: 4, Max: 31 - # See https://pkg.go.dev/golang.org/x/crypto/bcrypt for more information. - # Setting this lower makes registration/login consume less CPU resources at the cost - # of security should the database be compromised. Setting this higher makes registration/login - # consume more CPU resources but makes it harder to brute force password hashes. This value - # can be lowered if performing tests or on embedded Dendrite instances (e.g WASM builds). - bcrypt_cost: 10 - - # The length of time that a token issued for a relying party from - # /_matrix/client/r0/user/{userId}/openid/request_token endpoint - # is considered to be valid in milliseconds. - # The default lifetime is 3600000ms (60 minutes). - # openid_token_lifetime_ms: 3600000 - - # Users who register on this homeserver will automatically be joined to the rooms listed under "auto_join_rooms" option. - # By default, any room aliases included in this list will be created as a publicly joinable room - # when the first user registers for the homeserver. If the room already exists, - # make certain it is a publicly joinable room, i.e. the join rule of the room must be set to 'public'. - # As Spaces are just rooms under the hood, Space aliases may also be used. - auto_join_rooms: - # - "#main:matrix.org" - - account_database: - connection_string: file:dendrite-userapi.db - -room_server: - database: - connection_string: file:dendrite-roomserverapi.db - -key_server: - database: - connection_string: file:dendrite-keyserverapi.db - -relay_api: - database: - connection_string: file:dendrite-relayapi.db - -# Configuration for Opentracing. -# See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on -# how this works and how to set it up. -tracing: - enabled: false - jaeger: - serviceName: "" - disabled: false - rpc_metrics: false - tags: [] - sampler: null - reporter: null - headers: null - baggage_restrictions: null - throttler: null - -# Logging configuration. The "std" logging type controls the logs being sent to -# stdout. The "file" logging type controls logs being written to a log folder on -# the disk. Supported log levels are "debug", "info", "warn", "error". -logging: - - type: std - level: debug - - type: file - level: debug - params: - path: ./logs diff --git a/playwright/plugins/homeserver/index.ts b/playwright/plugins/homeserver/index.ts index c17ea15f55..b6b4b5ee99 100644 --- a/playwright/plugins/homeserver/index.ts +++ b/playwright/plugins/homeserver/index.ts @@ -6,16 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -export interface HomeserverConfig { - readonly configDir: string; - readonly baseUrl: string; - readonly port: number; - readonly registrationSecret: string; - readonly dockerUrl: string; -} - export interface HomeserverInstance { - readonly config: HomeserverConfig; + readonly baseUrl: string; /** * Register a user on the given Homeserver using the shared registration secret. @@ -42,27 +34,6 @@ export interface HomeserverInstance { setThreepid(userId: string, medium: string, address: string): Promise; } -export interface StartHomeserverOpts { - /** path to template within playwright/plugins/{homeserver}docker/template/ directory. */ - template: string; - - /** Port of an OAuth server to configure the homeserver to use */ - oAuthServerPort?: number; - - /** Additional variables to inject into the configuration template **/ - variables?: Record; -} - -export interface Homeserver { - start(opts: StartHomeserverOpts): Promise; - /** - * Stop this test homeserver instance. - * - * @returns A list of paths relative to the cwd for logfiles generated during this test run. - */ - stop(): Promise; -} - export interface Credentials { accessToken: string; userId: string; diff --git a/playwright/plugins/homeserver/synapse/consentHomeserver.ts b/playwright/plugins/homeserver/synapse/consentHomeserver.ts new file mode 100644 index 0000000000..46c4c9f23d --- /dev/null +++ b/playwright/plugins/homeserver/synapse/consentHomeserver.ts @@ -0,0 +1,56 @@ +/* +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"; + +export const consentHomeserver: Fixtures = { + _homeserver: async ({ _homeserver: container, mailhog }, use) => { + container + .withCopyDirectoriesToContainer([ + { source: "playwright/plugins/homeserver/synapse/res", target: "/data/res" }, + ]) + .withConfig({ + email: { + enable_notifs: false, + smtp_host: "mailhog", + smtp_port: 1025, + smtp_user: "username", + smtp_pass: "password", + require_transport_security: false, + notif_from: "Your Friendly %(app)s homeserver ", + app_name: "Matrix", + notif_template_html: "notif_mail.html", + notif_template_text: "notif_mail.txt", + notif_for_new_users: true, + client_base_url: "http://localhost/element", + }, + user_consent: { + template_dir: "/data/res/templates/privacy", + version: "1.0", + server_notice_content: { + msgtype: "m.text", + body: "To continue using this homeserver you must review and agree to the terms and conditions at %(consent_uri)s", + }, + send_server_notice_to_guests: true, + block_events_error: + "To continue using this homeserver you must review and agree to the terms and conditions at %(consent_uri)s", + require_at_registration: true, + }, + server_notices: { + system_mxid_localpart: "notices", + system_mxid_display_name: "Server Notices", + system_mxid_avatar_url: "mxc://localhost/oumMVlgDnLYFaPVkExemNVVZ", + room_name: "Server Notices", + }, + }) + .withConfigField("listeners[0].resources[0].names", ["client", "consent"]); + await use(container); + }, +}; diff --git a/playwright/plugins/homeserver/synapse/emailHomeserver.ts b/playwright/plugins/homeserver/synapse/emailHomeserver.ts new file mode 100644 index 0000000000..07dfe69264 --- /dev/null +++ b/playwright/plugins/homeserver/synapse/emailHomeserver.ts @@ -0,0 +1,28 @@ +/* +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"; + +export const emailHomeserver: Fixtures = { + _homeserver: async ({ _homeserver: container, mailhog }, use) => { + container.withConfig({ + enable_registration_without_verification: undefined, + disable_msisdn_registration: undefined, + registrations_require_3pid: ["email"], + email: { + smtp_host: "mailhog", + smtp_port: 25, + notif_from: "Your Friendly %(app)s homeserver ", + app_name: "my_branded_matrix_server", + }, + }); + await use(container); + }, +}; diff --git a/playwright/plugins/homeserver/synapse/index.ts b/playwright/plugins/homeserver/synapse/index.ts deleted file mode 100644 index c98a1f59bc..0000000000 --- a/playwright/plugins/homeserver/synapse/index.ts +++ /dev/null @@ -1,239 +0,0 @@ -/* -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 * as path from "node:path"; -import * as os from "node:os"; -import * as crypto from "node:crypto"; -import * as fse from "fs-extra"; -import { APIRequestContext } from "@playwright/test"; - -import { getFreePort } from "../../utils/port"; -import { Docker } from "../../docker"; -import { HomeserverConfig, HomeserverInstance, Homeserver, StartHomeserverOpts, Credentials } from ".."; -import { randB64Bytes } from "../../utils/rand"; - -// Docker tag to use for synapse docker image. -// We target a specific digest as every now and then a Synapse update will break our CI. -// This digest is updated by the playwright-image-updates.yaml workflow periodically. -const DOCKER_TAG = "develop@sha256:39f94b005e87cd3042c2535c37d8d9f915a88072fe79f6283ac18977fe134321"; - -async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise> { - const templateDir = path.join(__dirname, "templates", opts.template); - - const stats = await fse.stat(templateDir); - if (!stats?.isDirectory) { - throw new Error(`No such template: ${opts.template}`); - } - const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-synapsedocker-")); - - // copy the contents of the template dir, omitting homeserver.yaml as we'll template that - console.log(`Copy ${templateDir} -> ${tempDir}`); - await fse.copy(templateDir, tempDir, { filter: (f) => path.basename(f) !== "homeserver.yaml" }); - - const registrationSecret = randB64Bytes(16); - const macaroonSecret = randB64Bytes(16); - const formSecret = randB64Bytes(16); - - const port = await getFreePort(); - const baseUrl = `http://localhost:${port}`; - - // now copy homeserver.yaml, applying substitutions - const templateHomeserver = path.join(templateDir, "homeserver.yaml"); - const outputHomeserver = path.join(tempDir, "homeserver.yaml"); - console.log(`Gen ${templateHomeserver} -> ${outputHomeserver}`); - let hsYaml = await fse.readFile(templateHomeserver, "utf8"); - hsYaml = hsYaml.replace(/{{REGISTRATION_SECRET}}/g, registrationSecret); - hsYaml = hsYaml.replace(/{{MACAROON_SECRET_KEY}}/g, macaroonSecret); - hsYaml = hsYaml.replace(/{{FORM_SECRET}}/g, formSecret); - hsYaml = hsYaml.replace(/{{PUBLIC_BASEURL}}/g, baseUrl); - if (opts.oAuthServerPort) { - hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort.toString()); - } - if (opts.variables) { - for (const key in opts.variables) { - hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), String(opts.variables[key])); - } - } - - await fse.writeFile(outputHomeserver, hsYaml); - - // now generate a signing key (we could use synapse's config generation for - // this, or we could just do this...) - // NB. This assumes the homeserver.yaml specifies the key in this location - const signingKey = randB64Bytes(32); - const outputSigningKey = path.join(tempDir, "localhost.signing.key"); - console.log(`Gen -> ${outputSigningKey}`); - await fse.writeFile(outputSigningKey, `ed25519 x ${signingKey}`); - - // Allow anyone to read, write and execute in the /temp/react-sdk-synapsedocker-xxx directory - // so that the DIND setup that we use to update the playwright screenshots work without any issues. - await fse.chmod(tempDir, 0o757); - - return { - port, - baseUrl, - configDir: tempDir, - registrationSecret, - }; -} - -export class Synapse implements Homeserver, HomeserverInstance { - protected docker: Docker = new Docker(); - public config: HomeserverConfig & { serverId: string }; - - private adminToken?: string; - - public constructor(private readonly request: APIRequestContext) {} - - /** - * Start a synapse instance: the template must be the name of - * one of the templates in the playwright/plugins/synapsedocker/templates - * directory. - */ - public async start(opts: StartHomeserverOpts): Promise { - if (this.config) await this.stop(); - - const synCfg = await cfgDirFromTemplate(opts); - console.log(`Starting synapse with config dir ${synCfg.configDir}...`); - const dockerSynapseParams = ["-v", `${synCfg.configDir}:/data`, "-p", `${synCfg.port}:8008/tcp`]; - const synapseId = await this.docker.run({ - image: `ghcr.io/element-hq/synapse:${DOCKER_TAG}`, - containerName: `react-sdk-playwright-synapse`, - params: dockerSynapseParams, - cmd: ["run"], - }); - console.log(`Started synapse with id ${synapseId} on port ${synCfg.port}.`); - // Await Synapse healthcheck - await this.docker.exec([ - "curl", - "--connect-timeout", - "30", - "--retry", - "30", - "--retry-delay", - "1", - "--retry-all-errors", - "--silent", - "http://localhost:8008/health", - ]); - const dockerUrl = `http://${await this.docker.getContainerIp()}:8008`; - this.config = { - ...synCfg, - serverId: synapseId, - dockerUrl, - }; - return this; - } - - public async stop(): Promise { - if (!this.config) throw new Error("Missing existing synapse instance, did you call stop() before start()?"); - const id = this.config.serverId; - const synapseLogsPath = path.join("playwright", "logs", "synapse", id); - await fse.ensureDir(synapseLogsPath); - await this.docker.persistLogsToFile({ - stdoutFile: path.join(synapseLogsPath, "stdout.log"), - stderrFile: path.join(synapseLogsPath, "stderr.log"), - }); - await this.docker.stop(); - await fse.remove(this.config.configDir); - console.log(`Stopped synapse id ${id}.`); - - return [path.join(synapseLogsPath, "stdout.log"), path.join(synapseLogsPath, "stderr.log")]; - } - - private async registerUserInternal( - username: string, - password: string, - displayName?: string, - admin = false, - ): Promise { - const url = `${this.config.baseUrl}/_synapse/admin/v1/register`; - const { nonce } = await this.request.get(url).then((r) => r.json()); - const mac = crypto - .createHmac("sha1", this.config.registrationSecret) - .update(`${nonce}\0${username}\0${password}\0${admin ? "" : "not"}admin`) - .digest("hex"); - const res = await this.request.post(url, { - data: { - nonce, - username, - password, - mac, - admin, - displayname: displayName, - }, - }); - - if (!res.ok()) { - throw await res.json(); - } - - const data = await res.json(); - return { - homeServer: data.home_server, - accessToken: data.access_token, - userId: data.user_id, - deviceId: data.device_id, - password, - displayName, - }; - } - - public registerUser(username: string, password: string, displayName?: string): Promise { - return this.registerUserInternal(username, password, displayName, false); - } - - public async loginUser(userId: string, password: string): Promise { - const url = `${this.config.baseUrl}/_matrix/client/v3/login`; - const res = await this.request.post(url, { - data: { - type: "m.login.password", - identifier: { - type: "m.id.user", - user: userId, - }, - password: password, - }, - }); - const json = await res.json(); - - return { - password, - accessToken: json.access_token, - userId: json.user_id, - deviceId: json.device_id, - homeServer: json.home_server, - }; - } - - public async setThreepid(userId: string, medium: string, address: string): Promise { - if (this.adminToken === undefined) { - const result = await this.registerUserInternal("admin", "totalyinsecureadminpassword", undefined, true); - this.adminToken = result.accessToken; - } - - const url = `${this.config.baseUrl}/_synapse/admin/v2/users/${userId}`; - const res = await this.request.put(url, { - data: { - threepids: [ - { - medium, - address, - }, - ], - }, - headers: { - Authorization: `Bearer ${this.adminToken}`, - }, - }); - - if (!res.ok()) { - throw await res.json(); - } - } -} diff --git a/playwright/plugins/homeserver/synapse/legacyOAuthHomeserver.ts b/playwright/plugins/homeserver/synapse/legacyOAuthHomeserver.ts new file mode 100644 index 0000000000..3561c1e686 --- /dev/null +++ b/playwright/plugins/homeserver/synapse/legacyOAuthHomeserver.ts @@ -0,0 +1,48 @@ +/* +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 { TestContainers } from "testcontainers"; + +import { Services } from "../../../services.ts"; +import { OAuthServer } from "../../oauth_server"; + +export const legacyOAuthHomeserver: Fixtures = { + _homeserver: async ({ _homeserver: container }, use) => { + const server = new OAuthServer(); + const port = server.start(); + + await TestContainers.exposeHostPorts(port); + container.withConfig({ + oidc_providers: [ + { + idp_id: "test", + idp_name: "OAuth test", + issuer: `http://localhost:${port}/oauth`, + authorization_endpoint: `http://localhost:${port}/oauth/auth.html`, + // the token endpoint receives requests from synapse, + // rather than the webapp, so needs to escape the docker container. + token_endpoint: `http://host.testcontainers.internal:${port}/oauth/token`, + userinfo_endpoint: `http://host.testcontainers.internal:${port}/oauth/userinfo`, + client_id: "synapse", + discover: false, + scopes: ["profile"], + skip_verification: true, + client_auth_method: "none", + user_mapping_provider: { + config: { + display_name_template: "{{ user.name }}", + }, + }, + }, + ], + }); + await use(container); + server.stop(); + }, +}; diff --git a/playwright/plugins/homeserver/synapse/templates/consent/res/templates/privacy/en/1.0.html b/playwright/plugins/homeserver/synapse/res/templates/privacy/en/1.0.html similarity index 100% rename from playwright/plugins/homeserver/synapse/templates/consent/res/templates/privacy/en/1.0.html rename to playwright/plugins/homeserver/synapse/res/templates/privacy/en/1.0.html diff --git a/playwright/plugins/homeserver/synapse/templates/consent/res/templates/privacy/en/success.html b/playwright/plugins/homeserver/synapse/res/templates/privacy/en/success.html similarity index 100% rename from playwright/plugins/homeserver/synapse/templates/consent/res/templates/privacy/en/success.html rename to playwright/plugins/homeserver/synapse/res/templates/privacy/en/success.html diff --git a/playwright/plugins/homeserver/synapse/templates/COPYME/README.md b/playwright/plugins/homeserver/synapse/templates/COPYME/README.md deleted file mode 100644 index df1ed89e6e..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/COPYME/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Meta-template for synapse templates - -To make another template, you can copy this directory diff --git a/playwright/plugins/homeserver/synapse/templates/COPYME/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/COPYME/homeserver.yaml deleted file mode 100644 index cb58dc8661..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/COPYME/homeserver.yaml +++ /dev/null @@ -1,72 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -# XXX: This won't actually be right: it lets docker allocate an ephemeral port, -# so we have a chicken-and-egg problem -public_baseurl: http://localhost:8008/ -# Listener is always port 8008 (configured in the container) -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client, federation, consent] - compress: false - -# An sqlite in-memory database is fast & automatically wipes each time -database: - name: "sqlite3" - args: - database: ":memory:" - -# Needs to be configured to log to the console like a good docker process -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -# These placeholders will be be replaced with values generated at start -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -# Signing key must be here: it will be generated to this file -signing_key_path: "/data/localhost.signing.key" -email: - enable_notifs: false - smtp_host: "localhost" - smtp_port: 25 - smtp_user: "exampleusername" - smtp_pass: "examplepassword" - require_transport_security: False - notif_from: "Your Friendly %(app)s homeserver " - app_name: Matrix - notif_template_html: notif_mail.html - notif_template_text: notif_mail.txt - notif_for_new_users: True - client_base_url: "http://localhost/element" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true diff --git a/playwright/plugins/homeserver/synapse/templates/COPYME/log.config b/playwright/plugins/homeserver/synapse/templates/COPYME/log.config deleted file mode 100644 index ac232762da..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/COPYME/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: INFO - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: INFO - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/playwright/plugins/homeserver/synapse/templates/consent/README.md b/playwright/plugins/homeserver/synapse/templates/consent/README.md deleted file mode 100644 index 713e55f9d5..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/consent/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured with user privacy consent enabled diff --git a/playwright/plugins/homeserver/synapse/templates/consent/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/consent/homeserver.yaml deleted file mode 100644 index d3a4fa520c..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/consent/homeserver.yaml +++ /dev/null @@ -1,84 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client, federation, consent] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" -email: - enable_notifs: false - smtp_host: "localhost" - smtp_port: 25 - smtp_user: "exampleusername" - smtp_pass: "examplepassword" - require_transport_security: False - notif_from: "Your Friendly %(app)s homeserver " - app_name: Matrix - notif_template_html: notif_mail.html - notif_template_text: notif_mail.txt - notif_for_new_users: True - client_base_url: "http://localhost/element" - -user_consent: - template_dir: /data/res/templates/privacy - version: 1.0 - server_notice_content: - msgtype: m.text - body: >- - To continue using this homeserver you must review and agree to the - terms and conditions at %(consent_uri)s - send_server_notice_to_guests: True - block_events_error: >- - To continue using this homeserver you must review and agree to the - terms and conditions at %(consent_uri)s - require_at_registration: true - -server_notices: - system_mxid_localpart: notices - system_mxid_display_name: "Server Notices" - system_mxid_avatar_url: "mxc://localhost:5005/oumMVlgDnLYFaPVkExemNVVZ" - room_name: "Server Notices" -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true diff --git a/playwright/plugins/homeserver/synapse/templates/consent/log.config b/playwright/plugins/homeserver/synapse/templates/consent/log.config deleted file mode 100644 index b9123d0f5b..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/consent/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: DEBUG - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: DEBUG - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/playwright/plugins/homeserver/synapse/templates/default/README.md b/playwright/plugins/homeserver/synapse/templates/default/README.md deleted file mode 100644 index 8f6b11f999..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/default/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured with user privacy consent disabled diff --git a/playwright/plugins/homeserver/synapse/templates/default/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/default/homeserver.yaml deleted file mode 100644 index 539a917b20..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/default/homeserver.yaml +++ /dev/null @@ -1,106 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 -rc_joins: - local: - per_second: 9999 - burst_count: 9999 - remote: - per_second: 9999 - burst_count: 9999 -rc_joins_per_room: - per_second: 9999 - burst_count: 9999 -rc_3pid_validation: - per_second: 1000 - burst_count: 1000 - -rc_invites: - per_room: - per_second: 1000 - burst_count: 1000 - per_user: - per_second: 1000 - burst_count: 1000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true - -ui_auth: - session_timeout: "300s" - -oidc_providers: - - idp_id: test - idp_name: "OAuth test" - issuer: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth" - authorization_endpoint: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth/auth.html" - # the token endpoint receives requests from synapse, rather than the webapp, so needs to escape the docker container. - token_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/token" - userinfo_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/userinfo" - client_id: "synapse" - discover: false - scopes: ["profile"] - skip_verification: true - client_auth_method: none - user_mapping_provider: - config: - display_name_template: "{{ user.name }}" - -# Inhibit background updates as this Synapse isn't long-lived -background_updates: - min_batch_size: 100000 - sleep_duration_ms: 100000 - -experimental_features: - # Needed for e2e/crypto/crypto.spec.ts > Cryptography > decryption failure - # messages > non-joined historical messages. - # Can be removed after Synapse enables it by default - msc4115_membership_on_events: true - -enable_authenticated_media: true diff --git a/playwright/plugins/homeserver/synapse/templates/default/log.config b/playwright/plugins/homeserver/synapse/templates/default/log.config deleted file mode 100644 index b9123d0f5b..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/default/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: DEBUG - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: DEBUG - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/playwright/plugins/homeserver/synapse/templates/dehydration/README.md b/playwright/plugins/homeserver/synapse/templates/dehydration/README.md deleted file mode 100644 index 18f7923e6d..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/dehydration/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured with device dehydration v2 enabled diff --git a/playwright/plugins/homeserver/synapse/templates/dehydration/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/dehydration/homeserver.yaml deleted file mode 100644 index c3ac5d6536..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/dehydration/homeserver.yaml +++ /dev/null @@ -1,102 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 -rc_joins: - local: - per_second: 9999 - burst_count: 9999 - remote: - per_second: 9999 - burst_count: 9999 -rc_joins_per_room: - per_second: 9999 - burst_count: 9999 -rc_3pid_validation: - per_second: 1000 - burst_count: 1000 - -rc_invites: - per_room: - per_second: 1000 - burst_count: 1000 - per_user: - per_second: 1000 - burst_count: 1000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true - -ui_auth: - session_timeout: "300s" - -oidc_providers: - - idp_id: test - idp_name: "OAuth test" - issuer: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth" - authorization_endpoint: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth/auth.html" - # the token endpoint receives requests from synapse, rather than the webapp, so needs to escape the docker container. - token_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/token" - userinfo_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/userinfo" - client_id: "synapse" - discover: false - scopes: ["profile"] - skip_verification: true - client_auth_method: none - user_mapping_provider: - config: - display_name_template: "{{ user.name }}" - -# Inhibit background updates as this Synapse isn't long-lived -background_updates: - min_batch_size: 100000 - sleep_duration_ms: 100000 - -experimental_features: - msc2697_enabled: false - msc3814_enabled: true diff --git a/playwright/plugins/homeserver/synapse/templates/dehydration/log.config b/playwright/plugins/homeserver/synapse/templates/dehydration/log.config deleted file mode 100644 index b9123d0f5b..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/dehydration/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: DEBUG - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: DEBUG - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/playwright/plugins/homeserver/synapse/templates/email/README.md b/playwright/plugins/homeserver/synapse/templates/email/README.md deleted file mode 100644 index 40c23ba0be..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/email/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured to require an email for registration diff --git a/playwright/plugins/homeserver/synapse/templates/email/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/email/homeserver.yaml deleted file mode 100644 index fc20641ab4..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/email/homeserver.yaml +++ /dev/null @@ -1,44 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -enable_registration: true -registrations_require_3pid: - - email -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true - -ui_auth: - session_timeout: "300s" - -email: - smtp_host: "%SMTP_HOST%" - smtp_port: %SMTP_PORT% - notif_from: "Your Friendly %(app)s homeserver " - app_name: my_branded_matrix_server diff --git a/playwright/plugins/homeserver/synapse/templates/email/log.config b/playwright/plugins/homeserver/synapse/templates/email/log.config deleted file mode 100644 index ac232762da..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/email/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: INFO - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: INFO - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/playwright/plugins/homeserver/synapse/templates/guest-enabled/README.md b/playwright/plugins/homeserver/synapse/templates/guest-enabled/README.md deleted file mode 100644 index e1fef0b9d4..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/guest-enabled/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured with guest registration enabled. diff --git a/playwright/plugins/homeserver/synapse/templates/guest-enabled/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/guest-enabled/homeserver.yaml deleted file mode 100644 index 1faa39c3a7..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/guest-enabled/homeserver.yaml +++ /dev/null @@ -1,105 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 -rc_joins: - local: - per_second: 9999 - burst_count: 9999 - remote: - per_second: 9999 - burst_count: 9999 -rc_joins_per_room: - per_second: 9999 - burst_count: 9999 -rc_3pid_validation: - per_second: 1000 - burst_count: 1000 - -rc_invites: - per_room: - per_second: 1000 - burst_count: 1000 - per_user: - per_second: 1000 - burst_count: 1000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -allow_guest_access: true -enable_registration: true -enable_registration_without_verification: true -disable_msisdn_registration: false -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true - -ui_auth: - session_timeout: "300s" - -oidc_providers: - - idp_id: test - idp_name: "OAuth test" - issuer: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth" - authorization_endpoint: "http://localhost:{{OAUTH_SERVER_PORT}}/oauth/auth.html" - # the token endpoint receives requests from synapse, rather than the webapp, so needs to escape the docker container. - token_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/token" - userinfo_endpoint: "http://host.containers.internal:{{OAUTH_SERVER_PORT}}/oauth/userinfo" - client_id: "synapse" - discover: false - scopes: ["profile"] - skip_verification: true - client_auth_method: none - user_mapping_provider: - config: - display_name_template: "{{ user.name }}" - -# Inhibit background updates as this Synapse isn't long-lived -background_updates: - min_batch_size: 100000 - sleep_duration_ms: 100000 - -experimental_features: - # Needed for e2e/crypto/crypto.spec.ts > Cryptography > decryption failure - # messages > non-joined historical messages. - # Can be removed after Synapse enables it by default - msc4115_membership_on_events: true diff --git a/playwright/plugins/homeserver/synapse/templates/guest-enabled/log.config b/playwright/plugins/homeserver/synapse/templates/guest-enabled/log.config deleted file mode 100644 index ac232762da..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/guest-enabled/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: INFO - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: INFO - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/playwright/plugins/homeserver/synapse/templates/mas-oidc/README.md b/playwright/plugins/homeserver/synapse/templates/mas-oidc/README.md deleted file mode 100644 index 223ff436a8..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/mas-oidc/README.md +++ /dev/null @@ -1 +0,0 @@ -A synapse configured with auth delegated to via matrix authentication service diff --git a/playwright/plugins/homeserver/synapse/templates/mas-oidc/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/mas-oidc/homeserver.yaml deleted file mode 100644 index 802d97acad..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/mas-oidc/homeserver.yaml +++ /dev/null @@ -1,194 +0,0 @@ -server_name: "localhost" -pid_file: /data/homeserver.pid -public_baseurl: "{{PUBLIC_BASEURL}}" -listeners: - - port: 8008 - tls: false - bind_addresses: ["::"] - type: http - x_forwarded: true - - resources: - - names: [client] - compress: false - -database: - name: "sqlite3" - args: - database: ":memory:" - -log_config: "/data/log.config" - -rc_messages_per_second: 10000 -rc_message_burst_count: 10000 -rc_registration: - per_second: 10000 - burst_count: 10000 -rc_joins: - local: - per_second: 9999 - burst_count: 9999 - remote: - per_second: 9999 - burst_count: 9999 -rc_joins_per_room: - per_second: 9999 - burst_count: 9999 -rc_3pid_validation: - per_second: 1000 - burst_count: 1000 - -rc_invites: - per_room: - per_second: 1000 - burst_count: 1000 - per_user: - per_second: 1000 - burst_count: 1000 - -rc_login: - address: - per_second: 10000 - burst_count: 10000 - account: - per_second: 10000 - burst_count: 10000 - failed_attempts: - per_second: 10000 - burst_count: 10000 - -media_store_path: "/data/media_store" -uploads_path: "/data/uploads" -registration_shared_secret: "{{REGISTRATION_SECRET}}" -report_stats: false -macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" -form_secret: "{{FORM_SECRET}}" -signing_key_path: "/data/localhost.signing.key" - -trusted_key_servers: - - server_name: "matrix.org" -suppress_key_server_warning: true - -ui_auth: - session_timeout: "300s" - -# Inhibit background updates as this Synapse isn't long-lived -background_updates: - min_batch_size: 100000 - sleep_duration_ms: 100000 - -serve_server_wellknown: true -experimental_features: - msc3861: - enabled: true - - issuer: http://localhost:%MAS_PORT%/ - # We have to bake in the metadata here as we need to override `introspection_endpoint` - issuer_metadata: { - "issuer": "http://localhost:%MAS_PORT%/", - "authorization_endpoint": "http://localhost:%MAS_PORT%/authorize", - "token_endpoint": "http://localhost:%MAS_PORT%/oauth2/token", - "jwks_uri": "http://localhost:%MAS_PORT%/oauth2/keys.json", - "registration_endpoint": "http://localhost:%MAS_PORT%/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://localhost:%MAS_PORT%/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", - ], - # This is the only changed value - "introspection_endpoint": "http://host.containers.internal:%MAS_PORT%/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://localhost:%MAS_PORT%/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://localhost:%MAS_PORT%/oauth2/device", - "org.matrix.matrix-authentication-service.graphql_endpoint": "http://localhost:%MAS_PORT%/graphql", - "account_management_uri": "http://localhost:%MAS_PORT%/account/", - "account_management_actions_supported": - [ - "org.matrix.profile", - "org.matrix.sessions_list", - "org.matrix.session_view", - "org.matrix.session_end", - ], - } - - # Matches the `client_id` in the auth service config - client_id: 0000000000000000000SYNAPSE - # Matches the `client_auth_method` in the auth service config - client_auth_method: client_secret_basic - # Matches the `client_secret` in the auth service config - client_secret: "SomeRandomSecret" - - # Matches the `matrix.secret` in the auth service config - admin_token: "AnotherRandomSecret" - - # URL to advertise to clients where users can self-manage their account - account_management_url: "http://localhost:%MAS_PORT%/account" diff --git a/playwright/plugins/homeserver/synapse/templates/mas-oidc/log.config b/playwright/plugins/homeserver/synapse/templates/mas-oidc/log.config deleted file mode 100644 index b9123d0f5b..0000000000 --- a/playwright/plugins/homeserver/synapse/templates/mas-oidc/log.config +++ /dev/null @@ -1,50 +0,0 @@ -# Log configuration for Synapse. -# -# This is a YAML file containing a standard Python logging configuration -# dictionary. See [1] for details on the valid settings. -# -# Synapse also supports structured logging for machine readable logs which can -# be ingested by ELK stacks. See [2] for details. -# -# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema -# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html - -version: 1 - -formatters: - precise: - format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' - -handlers: - # A handler that writes logs to stderr. Unused by default, but can be used - # instead of "buffer" and "file" in the logger handlers. - console: - class: logging.StreamHandler - formatter: precise - -loggers: - synapse.storage.SQL: - # beware: increasing this to DEBUG will make synapse log sensitive - # information such as access tokens. - level: DEBUG - - twisted: - # We send the twisted logging directly to the file handler, - # to work around https://github.com/matrix-org/synapse/issues/3471 - # when using "buffer" logger. Use "console" to log to stderr instead. - handlers: [console] - propagate: false - -root: - level: DEBUG - - # Write logs to the `buffer` handler, which will buffer them together in memory, - # then write them to a file. - # - # Replace "buffer" with "console" to log to stderr instead. (Note that you'll - # also need to update the configuration for the `twisted` logger above, in - # this case.) - # - handlers: [console] - -disable_existing_loggers: false diff --git a/playwright/plugins/mailhog/index.ts b/playwright/plugins/mailhog/index.ts deleted file mode 100644 index e9e5f08b71..0000000000 --- a/playwright/plugins/mailhog/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* -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 mailhog from "mailhog"; - -import { getFreePort } from "../utils/port"; -import { Docker } from "../docker"; - -export interface Instance { - host: string; - smtpPort: number; - httpPort: number; - containerId: string; -} - -export class MailHogServer { - private readonly docker: Docker = new Docker(); - private instance?: Instance; - - async start(): Promise<{ api: mailhog.API; instance: Instance }> { - if (this.instance) throw new Error("Mailhog server is already running!"); - const smtpPort = await getFreePort(); - const httpPort = await getFreePort(); - console.log(`Starting mailhog...`); - const containerId = await this.docker.run({ - image: "mailhog/mailhog:latest", - containerName: `react-sdk-playwright-mailhog`, - params: ["-p", `${smtpPort}:1025/tcp`, "-p", `${httpPort}:8025/tcp`], - }); - console.log(`Started mailhog on ports smtp=${smtpPort} http=${httpPort}.`); - const host = await this.docker.getContainerIp(); - this.instance = { smtpPort, httpPort, containerId, host }; - return { api: mailhog({ host: "localhost", port: httpPort }), instance: this.instance }; - } - - async stop(): Promise { - if (!this.instance) throw new Error("Missing existing mailhog instance, did you call stop() before start()?"); - await this.docker.stop(); - console.log(`Stopped mailhog id ${this.instance.containerId}.`); - this.instance = undefined; - } -} diff --git a/playwright/plugins/matrix-authentication-service/config.yaml b/playwright/plugins/matrix-authentication-service/config.yaml deleted file mode 100644 index e7ab83e736..0000000000 --- a/playwright/plugins/matrix-authentication-service/config.yaml +++ /dev/null @@ -1,153 +0,0 @@ -clients: - - client_id: 0000000000000000000SYNAPSE - client_auth_method: client_secret_basic - client_secret: "SomeRandomSecret" -http: - listeners: - - name: web - resources: - - name: discovery - - name: human - - name: oauth - - name: compat - - name: graphql - playground: true - - name: assets - path: /usr/local/share/mas-cli/assets/ - binds: - - address: "[::]:8080" - proxy_protocol: false - - name: internal - resources: - - name: health - binds: - - host: localhost - port: 8081 - proxy_protocol: false - trusted_proxies: - - 192.128.0.0/16 - - 172.16.0.0/12 - - 10.0.0.0/10 - - 127.0.0.1/8 - - fd00::/8 - - ::1/128 - public_base: "http://localhost:{{MAS_PORT}}/" - issuer: http://localhost:{{MAS_PORT}}/ -database: - host: "{{POSTGRES_HOST}}" - port: 5432 - database: postgres - username: postgres - password: "{{POSTGRES_PASSWORD}}" - max_connections: 10 - min_connections: 0 - connect_timeout: 30 - idle_timeout: 600 - max_lifetime: 1800 -telemetry: - tracing: - exporter: none - propagators: [] - metrics: - exporter: none - sentry: - dsn: null -templates: - path: /usr/local/share/mas-cli/templates/ - assets_manifest: /usr/local/share/mas-cli/manifest.json - translations_path: /usr/local/share/mas-cli/translations/ -email: - from: '"Authentication Service" ' - reply_to: '"Authentication Service" ' - transport: smtp - mode: plain - hostname: "host.containers.internal" - port: %{{SMTP_PORT}} - username: username - password: password - -secrets: - encryption: 984b18e207c55ad5fbb2a49b217481a722917ee87b2308d4cf314c83fed8e3b5 - keys: - - kid: YEAhzrKipJ - key: | - -----BEGIN RSA PRIVATE KEY----- - MIIEowIBAAKCAQEAuIV+AW5vx52I4CuumgSxp6yvKfIAnRdALeZZCoFkIGxUli1B - S79NJ3ls46oLh1pSD9RrhaMp6HTNoi4K3hnP9Q9v77pD7KwdFKG3UdG1zksIB0s/ - +/Ey/DmX4LPluwBBS7r/LkQ1jk745lENA++oiDqZf2D/uP8jCHlvaSNyVKTqi1ki - OXPd4T4xBUjzuas9ze5jQVSYtfOidgnv1EzUipbIxgvH1jNt4raRlmP8mOq7xEnW - R+cF5x6n/g17PdSEfrwO4kz6aKGZuMP5lVlDEEnMHKabFSQDBl7+Mpok6jXutbtA - uiBnsKEahF9eoj4na4fpbRNPdIVyoaN5eGvm5wIDAQABAoIBAApyFCYEmHNWaa83 - CdVSOrRhRDE9r+c0r79pcNT1ajOjrk4qFa4yEC4R46YntCtfY5Hd1pBkIjU0l4d8 - z8Su9WTMEOwjQUEepS7L0NLi6kXZXYT8L40VpGs+32grBvBFHW0qEtQNrHJ36gMv - x2rXoFTF7HaXiSJx3wvVxAbRqOE9tBXLsmNHaWaAdWQG5o77V9+zvMri3cAeEg2w - VkKokb0dza7es7xG3tqS26k69SrwGeeuKo7qCHPH2cfyWmY5Yhv8iOoA59JzzbiK - UdxyzCHskrPSpRKVkVVwmY3RBt282TmSRG7td7e5ESSj50P2e5BI5uu1Hp/dvU4F - vYjV7kECgYEA6WqYoUpVsgQiqhvJwJIc/8gRm0mUy8TenI36z4Iim01Nt7fibWH7 - XnsFqLGjXtYNVWvBcCrUl9doEnRbJeG2eRGbGKYAWVrOeFvwM4fYvw9GoOiJdDj4 - cgWDe7eHbHE+UTqR7Nnr/UBfipoNWDh6X68HRBuXowh0Q6tOfxsrRFECgYEAyl/V - 4b8bFp3pKZZCb+KPSYsQf793cRmrBexPcLWcDPYbMZQADEZ/VLjbrNrpTOWxUWJT - hr8MrWswnHO+l5AFu5CNO+QgV2dHLk+2w8qpdpFRPJCfXfo2D3wZ0c4cv3VCwv1V - 5y7f6XWVjDWZYV4wj6c3shxZJjZ+9Hbhf3/twbcCgYA6fuRRR3fCbRbi2qPtBrEN - yO3gpMgNaQEA6vP4HPzfPrhDWmn8T5nXS61XYW03zxz4U1De81zj0K/cMBzHmZFJ - NghQXQmpWwBzWVcREvJWr1Vb7erEnaJlsMwKrSvbGWYspSj82oAxr3hCG+lMOpsw - b4S6pM+TpAK/EqdRY1WsgQKBgQCGoMaaTRXqL9bC0bEU2XVVCWxKb8c3uEmrwQ7/ - /fD4NmjUzI5TnDps1CVfkqoNe+hAKddDFqmKXHqUOfOaxDbsFje+lf5l5tDVoDYH - fjTKKdYPIm7CiAeauYY7qpA5Vfq52Opixy4yEwUPp0CII67OggFtPaqY3zwJyWQt - +57hdQKBgGCXM/KKt7ceUDcNJxSGjvu0zD9D5Sv2ihYlEBT/JLaTCCJdvzREevaJ - 1d+mpUAt0Lq6A8NWOMq8HPaxAik3rMQ0WtM5iG+XgsUqvTSb7NcshArDLuWGnW3m - MC4rM0UBYAS4QweduUSH1imrwH/1Gu5+PxbiecceRMMggWpzu0Bq - -----END RSA PRIVATE KEY----- - - kid: 8J1AxrlNZT - key: | - -----BEGIN EC PRIVATE KEY----- - MHcCAQEEIF1cjfIOEdy3BXJ72x6fKpEB8WP1ddZAUJAaqqr/6CpToAoGCCqGSM49 - AwEHoUQDQgAEfHdNuI1Yeh3/uOq2PlnW2vymloOVpwBYebbw4VVsna9xhnutIdQW - dE8hkX8Yb0pIDasrDiwllVLzSvsWJAI0Kw== - -----END EC PRIVATE KEY----- - - kid: 3BW6un1EBi - key: | - -----BEGIN EC PRIVATE KEY----- - MIGkAgEBBDA+3ZV17r8TsiMdw1cpbTSNbyEd5SMy3VS1Mk/kz6O2Ev/3QZut8GE2 - q3eGtLBoVQigBwYFK4EEACKhZANiAASs8Wxjk/uRimRKXnPr2/wDaXkN9wMDjYQK - mZULb+0ZP1/cXmuXuri8hUGhQvIU8KWY9PkpV+LMPEdpE54mHPKSLjq5CDXoSZ/P - 9f7cdRaOZ000KQPZfIFR9ujJTtDN7Vs= - -----END EC PRIVATE KEY----- - - kid: pkZ0pTKK0X - key: | - -----BEGIN EC PRIVATE KEY----- - MHQCAQEEIHenfsXYPc5yzjZKUfvmydDR1YRwdsfZYvwHf/2wsYxooAcGBSuBBAAK - oUQDQgAEON1x7Vlu+nA0KvC5vYSOHhDUkfLYNZwYSLPFVT02h9E13yFFMIJegIBl - Aer+6PMZpPc8ycyeH9N+U9NAyliBhQ== - -----END EC PRIVATE KEY----- -passwords: - enabled: true - schemes: - - version: 1 - algorithm: argon2id -matrix: - homeserver: localhost - secret: AnotherRandomSecret - endpoint: "{{SYNAPSE_URL}}" -policy: - wasm_module: /usr/local/share/mas-cli/policy.wasm - client_registration_entrypoint: client_registration/violation - register_entrypoint: register/violation - authorization_grant_entrypoint: authorization_grant/violation - password_entrypoint: password/violation - email_entrypoint: email/violation - data: - client_registration: - allow_insecure_uris: true # allow non-SSL and localhost URIs - allow_missing_contacts: true # EW doesn't have contacts at this time -upstream_oauth2: - providers: [] -branding: - service_name: null - policy_uri: null - tos_uri: null - imprint: null - logo_uri: null -experimental: - access_token_ttl: 300 - compat_token_ttl: 300 diff --git a/playwright/plugins/matrix-authentication-service/index.ts b/playwright/plugins/matrix-authentication-service/index.ts deleted file mode 100644 index eaad350b82..0000000000 --- a/playwright/plugins/matrix-authentication-service/index.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* -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 path, { basename } from "node:path"; -import os from "node:os"; -import * as fse from "fs-extra"; -import { BrowserContext, TestInfo } from "@playwright/test"; - -import { getFreePort } from "../utils/port"; -import { Docker } from "../docker"; -import { PG_PASSWORD, PostgresDocker } from "../postgres"; -import { HomeserverInstance } from "../homeserver"; -import { Instance as MailhogInstance } from "../mailhog"; - -// Docker tag to use for `ghcr.io/matrix-org/matrix-authentication-service` image. -// We use a debug tag so that we have a shell and can run all 3 necessary commands in one run. -const TAG = "0.8.0-debug"; - -export interface ProxyInstance { - containerId: string; - postgresId: string; - configDir: string; - port: number; -} - -async function cfgDirFromTemplate(opts: { - postgresHost: string; - synapseUrl: string; - masPort: string; - smtpPort: string; -}): Promise<{ - configDir: string; -}> { - const configPath = path.join(__dirname, "config.yaml"); - const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-mas-")); - - const outputHomeserver = path.join(tempDir, "config.yaml"); - console.log(`Gen ${configPath} -> ${outputHomeserver}`); - let config = await fse.readFile(configPath, "utf8"); - config = config.replace(/{{MAS_PORT}}/g, opts.masPort); - config = config.replace(/{{POSTGRES_HOST}}/g, opts.postgresHost); - config = config.replace(/{{POSTGRES_PASSWORD}}/g, PG_PASSWORD); - config = config.replace(/%{{SMTP_PORT}}/g, opts.smtpPort); - config = config.replace(/{{SYNAPSE_URL}}/g, opts.synapseUrl); - - await fse.writeFile(outputHomeserver, config); - - // Allow anyone to read, write and execute in the temp directory - // so that the DIND setup that we use to update the playwright screenshots work without any issues. - await fse.chmod(tempDir, 0o757); - - return { - configDir: tempDir, - }; -} - -export class MatrixAuthenticationService { - private readonly masDocker = new Docker(); - private readonly postgresDocker = new PostgresDocker("mas"); - private instance: ProxyInstance; - public port: number; - - constructor(private context: BrowserContext) {} - - async prepare(): Promise<{ port: number }> { - this.port = await getFreePort(); - return { port: this.port }; - } - - async start(homeserver: HomeserverInstance, mailhog: MailhogInstance): Promise { - console.log(new Date(), "Starting mas..."); - - if (!this.port) await this.prepare(); - const port = this.port; - const { containerId: postgresId, ipAddress: postgresIp } = await this.postgresDocker.start(); - const { configDir } = await cfgDirFromTemplate({ - masPort: port.toString(), - postgresHost: postgresIp, - synapseUrl: homeserver.config.dockerUrl, - smtpPort: mailhog.smtpPort.toString(), - }); - - console.log(new Date(), "starting mas container...", TAG); - const containerId = await this.masDocker.run({ - image: "ghcr.io/matrix-org/matrix-authentication-service:" + TAG, - containerName: "react-sdk-playwright-mas", - params: ["-p", `${port}:8080/tcp`, "-v", `${configDir}:/config`, "--entrypoint", "sh"], - cmd: [ - "-c", - "mas-cli database migrate --config /config/config.yaml && " + - "mas-cli config sync --config /config/config.yaml && " + - "mas-cli server --config /config/config.yaml", - ], - }); - console.log(new Date(), "started!"); - - // Set up redirects - const baseUrl = `http://localhost:${port}`; - for (const path of [ - "**/_matrix/client/*/login", - "**/_matrix/client/*/login/**", - "**/_matrix/client/*/logout", - "**/_matrix/client/*/refresh", - ]) { - await this.context.route(path, async (route) => { - await route.continue({ - url: new URL(route.request().url().split("/").slice(3).join("/"), baseUrl).href, - }); - }); - } - - this.instance = { containerId, postgresId, port, configDir }; - return this.instance; - } - - async stop(testInfo: TestInfo): Promise { - if (!this.instance) return; // nothing to stop - const id = this.instance.containerId; - const logPath = path.join("playwright", "logs", "matrix-authentication-service", id); - await fse.ensureDir(logPath); - await this.masDocker.persistLogsToFile({ - stdoutFile: path.join(logPath, "stdout.log"), - stderrFile: path.join(logPath, "stderr.log"), - }); - - await this.masDocker.stop(); - await this.postgresDocker.stop(); - - if (testInfo.status !== "passed") { - const logs = [path.join(logPath, "stdout.log"), path.join(logPath, "stderr.log")]; - for (const path of logs) { - await testInfo.attach(`mas-${basename(path)}`, { - path, - contentType: "text/plain", - }); - } - await testInfo.attach("mas-config.yaml", { - path: path.join(this.instance.configDir, "config.yaml"), - contentType: "text/plain", - }); - } - - await fse.remove(this.instance.configDir); - console.log(new Date(), "Stopped mas."); - } -} diff --git a/playwright/plugins/postgres/index.ts b/playwright/plugins/postgres/index.ts deleted file mode 100644 index 23d37ffef6..0000000000 --- a/playwright/plugins/postgres/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* -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 { Docker } from "../docker"; - -export const PG_PASSWORD = "p4S5w0rD"; - -/** - * Class to manage a postgres database in docker - */ -export class PostgresDocker extends Docker { - /** - * @param key an opaque string to use when naming the docker containers instantiated by this class - */ - public constructor(private key: string) { - super(); - } - - private async waitForPostgresReady(ipAddress: string): Promise { - const waitTimeMillis = 30000; - const startTime = new Date().getTime(); - let lastErr: Error | null = null; - while (new Date().getTime() - startTime < waitTimeMillis) { - try { - // Note that we specify the IP address rather than letting it connect to the local - // socket: that's the listener we care about and empirically it matters. - await this.exec(["pg_isready", "-h", ipAddress, "-U", "postgres"], true); - lastErr = null; - break; - } catch (err) { - console.log("pg_isready: failed"); - lastErr = err; - } - } - if (lastErr) { - console.log("rethrowing"); - throw lastErr; - } - } - - public async start(): Promise<{ - ipAddress: string; - containerId: string; - }> { - console.log(new Date(), "starting postgres container"); - const containerId = await this.run({ - image: "postgres", - containerName: `react-sdk-playwright-postgres-${this.key}`, - params: ["--tmpfs=/pgtmpfs", "-e", "PGDATA=/pgtmpfs", "-e", `POSTGRES_PASSWORD=${PG_PASSWORD}`], - // Optimise for testing - https://www.postgresql.org/docs/current/non-durability.html - cmd: ["-c", `fsync=off`, "-c", `synchronous_commit=off`, "-c", `full_page_writes=off`], - }); - - const ipAddress = await this.getContainerIp(); - console.log(new Date(), "postgres container up"); - - await this.waitForPostgresReady(ipAddress); - console.log(new Date(), "postgres container ready"); - return { ipAddress, containerId }; - } -} diff --git a/playwright/plugins/sliding-sync-proxy/index.ts b/playwright/plugins/sliding-sync-proxy/index.ts deleted file mode 100644 index 81bfe79fc1..0000000000 --- a/playwright/plugins/sliding-sync-proxy/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* -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 type { BrowserContext, Route } from "@playwright/test"; -import { getFreePort } from "../utils/port"; -import { Docker } from "../docker"; -import { PG_PASSWORD, PostgresDocker } from "../postgres"; - -// Docker tag to use for `ghcr.io/matrix-org/sliding-sync` image. -const SLIDING_SYNC_PROXY_TAG = "v0.99.3"; - -export interface ProxyInstance { - containerId: string; - postgresId: string; - port: number; -} - -export class SlidingSyncProxy { - private readonly proxyDocker = new Docker(); - private readonly postgresDocker = new PostgresDocker("sliding-sync"); - private instance: ProxyInstance; - - constructor( - private synapseIp: string, - private context: BrowserContext, - ) {} - - private syncHandler = async (route: Route) => { - if (!this.instance) return route.abort("blockedbyclient"); - - const baseUrl = `http://localhost:${this.instance.port}`; - await route.continue({ - url: new URL(route.request().url().split("/").slice(3).join("/"), baseUrl).href, - }); - }; - - async start(): Promise { - console.log(new Date(), "Starting sliding sync proxy..."); - - const { ipAddress: postgresIp, containerId: postgresId } = await this.postgresDocker.start(); - - const port = await getFreePort(); - console.log(new Date(), "starting proxy container...", SLIDING_SYNC_PROXY_TAG); - const containerId = await this.proxyDocker.run({ - image: "ghcr.io/matrix-org/sliding-sync:" + SLIDING_SYNC_PROXY_TAG, - containerName: "react-sdk-playwright-sliding-sync-proxy", - params: [ - "-p", - `${port}:8008/tcp`, - "-e", - "SYNCV3_SECRET=bwahahaha", - "-e", - `SYNCV3_SERVER=${this.synapseIp}`, - "-e", - `SYNCV3_DB=user=postgres dbname=postgres password=${PG_PASSWORD} host=${postgresIp} sslmode=disable`, - ], - }); - console.log(new Date(), "started!"); - - this.instance = { containerId, postgresId, port }; - await this.context.route("**/_matrix/client/unstable/org.matrix.msc3575/sync*", this.syncHandler); - return this.instance; - } - - async stop(): Promise { - await this.context.unroute("**/_matrix/client/unstable/org.matrix.msc3575/sync*", this.syncHandler); - - await this.postgresDocker.stop(); - await this.proxyDocker.stop(); - console.log(new Date(), "Stopped sliding sync proxy."); - } -} diff --git a/playwright/services.ts b/playwright/services.ts new file mode 100644 index 0000000000..bf4bad18fc --- /dev/null +++ b/playwright/services.ts @@ -0,0 +1,113 @@ +/* +Copyright 2024 New Vector Ltd. + +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 as base } from "@playwright/test"; +import mailhog from "mailhog"; +import { GenericContainer, Network, StartedNetwork, StartedTestContainer, Wait } from "testcontainers"; +import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql"; + +import { StartedSynapseContainer, SynapseConfigOptions, SynapseContainer } from "./testcontainers/synapse.ts"; +import { MatrixAuthenticationServiceContainer } from "./testcontainers/mas.ts"; + +export interface Services { + network: StartedNetwork; + postgres: StartedPostgreSqlContainer; + + mailhog: StartedTestContainer; + mailhogClient: mailhog.API; + + synapseConfigOptions: SynapseConfigOptions; + _homeserver: SynapseContainer; + homeserver: StartedSynapseContainer; + mas: StartedTestContainer; +} + +// TODO logs +export const test = base.extend({ + // eslint-disable-next-line no-empty-pattern + network: async ({}, use) => { + const network = await new Network().start(); + await use(network); + await network.stop(); + }, + postgres: async ({ network }, use) => { + const container = await new PostgreSqlContainer() + .withNetwork(network) + .withNetworkAliases("postgres") + .withTmpFs({ + "/dev/shm/pgdata/data": "", + }) + .withEnvironment({ + PG_DATA: "/dev/shm/pgdata/data", + }) + .withCommand([ + "-c", + "shared_buffers=128MB", + "-c", + `fsync=off`, + "-c", + `synchronous_commit=off`, + "-c", + "full_page_writes=off", + ]) + .start(); + await use(container); + await container.stop(); + }, + + mailhog: async ({ network }, use) => { + const container = await new GenericContainer("mailhog/mailhog:latest") + .withNetwork(network) + .withNetworkAliases("mailhog") + .withExposedPorts(8025) + .withWaitStrategy(Wait.forListeningPorts()) + .start(); + await use(container); + await container.stop(); + }, + mailhogClient: async ({ mailhog: container }, use) => { + await use(mailhog({ host: container.getHost(), port: container.getMappedPort(8025) })); + }, + + synapseConfigOptions: [{}, { option: true }], + _homeserver: async ({ request }, use) => { + const container = new SynapseContainer(request); + await use(container); + }, + homeserver: async ({ network, _homeserver: homeserver, synapseConfigOptions }, use) => { + const container = await homeserver + .withNetwork(network) + .withNetworkAliases("homeserver") + .withConfig(synapseConfigOptions) + .start(); + + await use(container); + await container.stop(); + }, + mas: async ({ network, homeserver }, use) => { + const container = await new MatrixAuthenticationServiceContainer() + .withNetwork(network) + .withNetworkAliases("mas") + .withConfig({ + clients: [ + { + client_id: "0000000000000000000SYNAPSE", + client_auth_method: "client_secret_basic", + client_secret: "SomeRandomSecret", + }, + ], + matrix: { + homeserver: "localhost", + secret: "AnotherRandomSecret", + endpoint: "http://synapse:8008", + }, + }) + .start(); + await use(container); + await container.stop(); + }, +}); diff --git a/playwright/testcontainers/dendrite.ts b/playwright/testcontainers/dendrite.ts new file mode 100644 index 0000000000..e073fbcada --- /dev/null +++ b/playwright/testcontainers/dendrite.ts @@ -0,0 +1,280 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +import { GenericContainer, Wait } from "testcontainers"; +import { APIRequestContext } from "@playwright/test"; +import * as YAML from "yaml"; +import { deepCopy } from "matrix-js-sdk/src/utils"; +import { set } from "lodash"; + +import { getFreePort } from "../plugins/utils/port.ts"; +import { randB64Bytes } from "../plugins/utils/rand.ts"; +import { StartedSynapseContainer } from "./synapse.ts"; + +const DEFAULT_CONFIG = { + version: 2, + global: { + server_name: "localhost", + private_key: "matrix_key.pem", + old_private_keys: null, + key_validity_period: "168h0m0s", + cache: { + max_size_estimated: "1gb", + max_age: "1h", + }, + well_known_server_name: "", + well_known_client_name: "", + trusted_third_party_id_servers: ["matrix.org", "vector.im"], + disable_federation: false, + presence: { + enable_inbound: false, + enable_outbound: false, + }, + report_stats: { + enabled: false, + endpoint: "https://matrix.org/report-usage-stats/push", + }, + server_notices: { + enabled: false, + local_part: "_server", + display_name: "Server Alerts", + avatar_url: "", + room_name: "Server Alerts", + }, + jetstream: { + addresses: null, + disable_tls_validation: false, + storage_path: "./", + topic_prefix: "Dendrite", + }, + metrics: { + enabled: false, + basic_auth: { + username: "metrics", + password: "metrics", + }, + }, + dns_cache: { + enabled: false, + cache_size: 256, + cache_lifetime: "5m", + }, + }, + app_service_api: { + disable_tls_validation: false, + config_files: null, + }, + client_api: { + registration_disabled: false, + guests_disabled: true, + registration_shared_secret: "secret", + enable_registration_captcha: false, + recaptcha_public_key: "", + recaptcha_private_key: "", + recaptcha_bypass_secret: "", + turn: { + turn_user_lifetime: "5m", + turn_uris: null, + turn_shared_secret: "", + }, + rate_limiting: { + enabled: true, + threshold: 20, + cooloff_ms: 500, + exempt_user_ids: null, + }, + }, + federation_api: { + send_max_retries: 16, + disable_tls_validation: false, + disable_http_keepalives: false, + key_perspectives: [ + { + server_name: "matrix.org", + keys: [ + { + key_id: "ed25519:auto", + public_key: "Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw", + }, + { + key_id: "ed25519:a_RXGa", + public_key: "l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ", + }, + ], + }, + ], + prefer_direct_fetch: false, + database: { + connection_string: "file:dendrite-federationapi.db", + }, + }, + media_api: { + base_path: "./media_store", + max_file_size_bytes: 10485760, + dynamic_thumbnails: false, + max_thumbnail_generators: 10, + thumbnail_sizes: [ + { + width: 32, + height: 32, + method: "crop", + }, + { + width: 96, + height: 96, + method: "crop", + }, + { + width: 640, + height: 480, + method: "scale", + }, + ], + database: { + connection_string: "file:dendrite-mediaapi.db", + }, + }, + mscs: { + mscs: null, + database: { + connection_string: "file:dendrite-msc.db", + }, + }, + sync_api: { + search: { + enabled: false, + index_path: "./searchindex", + language: "en", + }, + database: { + connection_string: "file:dendrite-syncapi.db", + }, + }, + user_api: { + bcrypt_cost: 10, + auto_join_rooms: null, + account_database: { + connection_string: "file:dendrite-userapi.db", + }, + }, + room_server: { + database: { + connection_string: "file:dendrite-roomserverapi.db", + }, + }, + key_server: { + database: { + connection_string: "file:dendrite-keyserverapi.db", + }, + }, + relay_api: { + database: { + connection_string: "file:dendrite-relayapi.db", + }, + }, + tracing: { + enabled: false, + jaeger: { + serviceName: "", + disabled: false, + rpc_metrics: false, + tags: [], + sampler: null, + reporter: null, + headers: null, + baggage_restrictions: null, + throttler: null, + }, + }, + logging: [ + { + type: "std", + level: "debug", + }, + { + type: "file", + level: "debug", + params: { + path: "./logs", + }, + }, + ], +}; + +export class DendriteContainer extends GenericContainer { + private config: typeof DEFAULT_CONFIG; + + constructor( + private request: APIRequestContext, + image = "matrixdotorg/dendrite-monolith:main", + entrypoint = "/usr/bin/dendrite", + ) { + super(image); + + this.config = deepCopy(DEFAULT_CONFIG); + this.config.client_api.registration_shared_secret = randB64Bytes(16); + + this.withEntrypoint([entrypoint]) + .withCommand([ + "--config", + "/etc/dendrite/dendrite.yaml", + "--really-enable-open-registration", + "true", + "run", + ]) + .withWaitStrategy(Wait.forHttp("/_matrix/client/versions", 8008)); + + // const docker = new Docker(); + // await docker.run({ + // image: dendriteImage, + // params: ["--entrypoint=", "-v", `${tempDir}:/mnt`], + // containerName: `react-sdk-playwright-dendrite-keygen`, + // cmd: ["/usr/bin/generate-keys", "-private-key", "/mnt/matrix_key.pem"], + // }); + } + + public withConfigField(key: string, value: any): this { + set(this.config, key, value); + return this; + } + + public withConfig(config: Partial): this { + this.config = { + ...this.config, + ...config, + }; + return this; + } + + public override async start(): Promise { + const port = await getFreePort(); + + this.withExposedPorts({ + container: 8008, + host: port, + }).withCopyContentToContainer([ + { + target: "/etc/dendrite/dendrite.yaml", + content: YAML.stringify(this.config), + }, + ]); + + // Surprisingly, Dendrite implements the same register user Admin API Synapse, so we can just extend it + return new StartedSynapseContainer( + await super.start(), + `http://localhost:${port}`, + this.config.client_api.registration_shared_secret, + this.request, + ); + } +} + +export class PineconeContainer extends DendriteContainer { + constructor(request: APIRequestContext) { + super(request, "matrixdotorg/dendrite-demo-pinecone:main", "/usr/bin/dendrite-demo-pinecone"); + } +} diff --git a/playwright/testcontainers/mas.ts b/playwright/testcontainers/mas.ts new file mode 100644 index 0000000000..b4e5bf7537 --- /dev/null +++ b/playwright/testcontainers/mas.ts @@ -0,0 +1,206 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers"; +import crypto from "node:crypto"; +import * as YAML from "yaml"; + +import { getFreePort } from "../plugins/utils/port.ts"; +import { Credentials } from "../plugins/homeserver"; +import { deepCopy } from "matrix-js-sdk/src/utils.ts"; + +const DEFAULT_CONFIG = { + http: { + listeners: [ + { + name: "web", + resources: [ + { + name: "discovery", + }, + { + name: "human", + }, + { + name: "oauth", + }, + { + name: "compat", + }, + { + name: "graphql", + playground: true, + }, + { + name: "assets", + path: "/usr/local/share/mas-cli/assets/", + }, + ], + binds: [ + { + address: "[::]:8080", + }, + ], + proxy_protocol: false, + }, + { + name: "internal", + resources: [ + { + name: "health", + }, + ], + binds: [ + { + host: "localhost", + port: 8081, + }, + ], + proxy_protocol: false, + }, + ], + trusted_proxies: ["192.128.0.0/16", "172.16.0.0/12", "10.0.0.0/10", "127.0.0.1/8", "fd00::/8", "::1/128"], + public_base: "", // Needs to be set + issuer: "", // Needs to be set + }, + database: { + host: "postgres", + port: 5432, + database: "postgres", + username: "postgres", + password: "p4S5w0rD", + max_connections: 10, + min_connections: 0, + connect_timeout: 30, + idle_timeout: 600, + max_lifetime: 1800, + }, + telemetry: { + tracing: { + exporter: "none", + propagators: [], + }, + metrics: { + exporter: "none", + }, + sentry: { + dsn: null, + }, + }, + templates: { + path: "/usr/local/share/mas-cli/templates/", + assets_manifest: "/usr/local/share/mas-cli/manifest.json", + translations_path: "/usr/local/share/mas-cli/translations/", + }, + email: { + from: '"Authentication Service" ', + reply_to: '"Authentication Service" ', + transport: "smtp", + mode: "plain", + hostname: "mailhog", + port: 1025, + username: "username", + password: "password", + }, + secrets: { + encryption: "984b18e207c55ad5fbb2a49b217481a722917ee87b2308d4cf314c83fed8e3b5", + keys: [ + { + kid: "YEAhzrKipJ", + key: "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAuIV+AW5vx52I4CuumgSxp6yvKfIAnRdALeZZCoFkIGxUli1B\nS79NJ3ls46oLh1pSD9RrhaMp6HTNoi4K3hnP9Q9v77pD7KwdFKG3UdG1zksIB0s/\n+/Ey/DmX4LPluwBBS7r/LkQ1jk745lENA++oiDqZf2D/uP8jCHlvaSNyVKTqi1ki\nOXPd4T4xBUjzuas9ze5jQVSYtfOidgnv1EzUipbIxgvH1jNt4raRlmP8mOq7xEnW\nR+cF5x6n/g17PdSEfrwO4kz6aKGZuMP5lVlDEEnMHKabFSQDBl7+Mpok6jXutbtA\nuiBnsKEahF9eoj4na4fpbRNPdIVyoaN5eGvm5wIDAQABAoIBAApyFCYEmHNWaa83\nCdVSOrRhRDE9r+c0r79pcNT1ajOjrk4qFa4yEC4R46YntCtfY5Hd1pBkIjU0l4d8\nz8Su9WTMEOwjQUEepS7L0NLi6kXZXYT8L40VpGs+32grBvBFHW0qEtQNrHJ36gMv\nx2rXoFTF7HaXiSJx3wvVxAbRqOE9tBXLsmNHaWaAdWQG5o77V9+zvMri3cAeEg2w\nVkKokb0dza7es7xG3tqS26k69SrwGeeuKo7qCHPH2cfyWmY5Yhv8iOoA59JzzbiK\nUdxyzCHskrPSpRKVkVVwmY3RBt282TmSRG7td7e5ESSj50P2e5BI5uu1Hp/dvU4F\nvYjV7kECgYEA6WqYoUpVsgQiqhvJwJIc/8gRm0mUy8TenI36z4Iim01Nt7fibWH7\nXnsFqLGjXtYNVWvBcCrUl9doEnRbJeG2eRGbGKYAWVrOeFvwM4fYvw9GoOiJdDj4\ncgWDe7eHbHE+UTqR7Nnr/UBfipoNWDh6X68HRBuXowh0Q6tOfxsrRFECgYEAyl/V\n4b8bFp3pKZZCb+KPSYsQf793cRmrBexPcLWcDPYbMZQADEZ/VLjbrNrpTOWxUWJT\nhr8MrWswnHO+l5AFu5CNO+QgV2dHLk+2w8qpdpFRPJCfXfo2D3wZ0c4cv3VCwv1V\n5y7f6XWVjDWZYV4wj6c3shxZJjZ+9Hbhf3/twbcCgYA6fuRRR3fCbRbi2qPtBrEN\nyO3gpMgNaQEA6vP4HPzfPrhDWmn8T5nXS61XYW03zxz4U1De81zj0K/cMBzHmZFJ\nNghQXQmpWwBzWVcREvJWr1Vb7erEnaJlsMwKrSvbGWYspSj82oAxr3hCG+lMOpsw\nb4S6pM+TpAK/EqdRY1WsgQKBgQCGoMaaTRXqL9bC0bEU2XVVCWxKb8c3uEmrwQ7/\n/fD4NmjUzI5TnDps1CVfkqoNe+hAKddDFqmKXHqUOfOaxDbsFje+lf5l5tDVoDYH\nfjTKKdYPIm7CiAeauYY7qpA5Vfq52Opixy4yEwUPp0CII67OggFtPaqY3zwJyWQt\n+57hdQKBgGCXM/KKt7ceUDcNJxSGjvu0zD9D5Sv2ihYlEBT/JLaTCCJdvzREevaJ\n1d+mpUAt0Lq6A8NWOMq8HPaxAik3rMQ0WtM5iG+XgsUqvTSb7NcshArDLuWGnW3m\nMC4rM0UBYAS4QweduUSH1imrwH/1Gu5+PxbiecceRMMggWpzu0Bq\n-----END RSA PRIVATE KEY-----\n", + }, + { + kid: "8J1AxrlNZT", + key: "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIF1cjfIOEdy3BXJ72x6fKpEB8WP1ddZAUJAaqqr/6CpToAoGCCqGSM49\nAwEHoUQDQgAEfHdNuI1Yeh3/uOq2PlnW2vymloOVpwBYebbw4VVsna9xhnutIdQW\ndE8hkX8Yb0pIDasrDiwllVLzSvsWJAI0Kw==\n-----END EC PRIVATE KEY-----\n", + }, + { + kid: "3BW6un1EBi", + key: "-----BEGIN EC PRIVATE KEY-----\nMIGkAgEBBDA+3ZV17r8TsiMdw1cpbTSNbyEd5SMy3VS1Mk/kz6O2Ev/3QZut8GE2\nq3eGtLBoVQigBwYFK4EEACKhZANiAASs8Wxjk/uRimRKXnPr2/wDaXkN9wMDjYQK\nmZULb+0ZP1/cXmuXuri8hUGhQvIU8KWY9PkpV+LMPEdpE54mHPKSLjq5CDXoSZ/P\n9f7cdRaOZ000KQPZfIFR9ujJTtDN7Vs=\n-----END EC PRIVATE KEY-----\n", + }, + { + kid: "pkZ0pTKK0X", + key: "-----BEGIN EC PRIVATE KEY-----\nMHQCAQEEIHenfsXYPc5yzjZKUfvmydDR1YRwdsfZYvwHf/2wsYxooAcGBSuBBAAK\noUQDQgAEON1x7Vlu+nA0KvC5vYSOHhDUkfLYNZwYSLPFVT02h9E13yFFMIJegIBl\nAer+6PMZpPc8ycyeH9N+U9NAyliBhQ==\n-----END EC PRIVATE KEY-----\n", + }, + ], + }, + passwords: { + enabled: true, + schemes: [ + { + version: 1, + algorithm: "argon2id", + }, + ], + }, + policy: { + wasm_module: "/usr/local/share/mas-cli/policy.wasm", + client_registration_entrypoint: "client_registration/violation", + register_entrypoint: "register/violation", + authorization_grant_entrypoint: "authorization_grant/violation", + password_entrypoint: "password/violation", + email_entrypoint: "email/violation", + data: { + client_registration: { + allow_insecure_uris: true, + allow_missing_contacts: true, + }, + }, + }, + upstream_oauth2: { + providers: [], + }, + branding: { + service_name: null, + policy_uri: null, + tos_uri: null, + imprint: null, + logo_uri: null, + }, + experimental: { + access_token_ttl: 300, + compat_token_ttl: 300, + }, +}; + +export class MatrixAuthenticationServiceContainer extends GenericContainer { + private config: typeof DEFAULT_CONFIG; + + constructor() { + super("ghcr.io/matrix-org/matrix-authentication-service:0.8.0"); + + this.config = deepCopy(DEFAULT_CONFIG); + + this.withWaitStrategy(Wait.forHttp("/health", 8081)).withCommand(["server", "--config", "/config/config.yaml"]); + } + + public withConfig(config: object): this { + this.config = { + ...this.config, + ...config, + }; + return this; + } + + public override async start(): Promise { + const port = await getFreePort(); + + this.config.http.public_base = `http://localhost:${port}/`; + this.config.http.issuer = `http://localhost:${port}/`; + + this.withExposedPorts({ + container: 8080, + host: port, + }).withCopyContentToContainer([ + { + target: "/config/config.yaml", + content: YAML.stringify(this.config), + }, + ]); + + return super.start(); + } +} diff --git a/playwright/testcontainers/synapse.ts b/playwright/testcontainers/synapse.ts new file mode 100644 index 0000000000..c3cb0ab69f --- /dev/null +++ b/playwright/testcontainers/synapse.ts @@ -0,0 +1,323 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +import { AbstractStartedContainer, GenericContainer, StartedTestContainer, Wait } from "testcontainers"; +import { APIRequestContext } from "@playwright/test"; +import crypto from "node:crypto"; +import * as YAML from "yaml"; +import { deepCopy } from "matrix-js-sdk/src/utils"; +import { set } from "lodash"; + +import { getFreePort } from "../plugins/utils/port.ts"; +import { randB64Bytes } from "../plugins/utils/rand.ts"; +import { Credentials, HomeserverInstance } from "../plugins/homeserver"; + +const TAG = "develop@sha256:17cc0a301447430624afb860276e5c13270ddeb99a3f6d1c6d519a20b1a8f650"; + +const DEFAULT_CONFIG = { + server_name: "localhost", + public_baseurl: "", + pid_file: "/homeserver.pid", + web_client: false, + soft_file_limit: 0, + log_config: "/data/log.config", + listeners: [ + { + port: 8008, + tls: false, + bind_addresses: ["::"], + type: "http", + x_forwarded: true, + resources: [ + { + names: ["client"], + compress: false, + }, + ], + }, + ], + database: { + name: "sqlite3", + args: { + database: ":memory:", + }, + }, + rc_messages_per_second: 10000, + rc_message_burst_count: 10000, + rc_registration: { + per_second: 10000, + burst_count: 10000, + }, + rc_joins: { + local: { + per_second: 9999, + burst_count: 9999, + }, + remote: { + per_second: 9999, + burst_count: 9999, + }, + }, + rc_joins_per_room: { + per_second: 9999, + burst_count: 9999, + }, + rc_3pid_validation: { + per_second: 1000, + burst_count: 1000, + }, + rc_invites: { + per_room: { + per_second: 1000, + burst_count: 1000, + }, + per_user: { + per_second: 1000, + burst_count: 1000, + }, + }, + rc_login: { + address: { + per_second: 10000, + burst_count: 10000, + }, + account: { + per_second: 10000, + burst_count: 10000, + }, + failed_attempts: { + per_second: 10000, + burst_count: 10000, + }, + }, + media_store_path: "/media", + max_upload_size: "50M", + max_image_pixels: "32M", + dynamic_thumbnails: false, + enable_registration: true, + enable_registration_without_verification: true, + disable_msisdn_registration: false, + registrations_require_3pid: [], + registration_shared_secret: "secret", + enable_metrics: false, + report_stats: false, + macaroon_secret_key: "secret", + form_secret: "secret", + signing_key_path: "/data/localhost.signing.key", + trusted_key_servers: [], + password_config: { + enabled: true, + }, + ui_auth: { + session_timeout: "300s", + }, + background_updates: { + min_batch_size: 100000, + sleep_duration_ms: 100000, + }, + enable_authenticated_media: true, + email: undefined, + user_consent: undefined, + server_notices: undefined, + allow_guest_access: false, + experimental_features: {}, + oidc_providers: [], + serve_server_wellknown: true, +}; + +export type SynapseConfigOptions = Partial; + +export class SynapseContainer extends GenericContainer { + private config: typeof DEFAULT_CONFIG; + + constructor(private readonly request: APIRequestContext) { + super(`ghcr.io/element-hq/synapse:${TAG}`); + + this.config = deepCopy(DEFAULT_CONFIG); + this.config.registration_shared_secret = randB64Bytes(16); + this.config.macaroon_secret_key = randB64Bytes(16); + this.config.form_secret = randB64Bytes(16); + + const signingKey = randB64Bytes(32); + this.withWaitStrategy(Wait.forHttp("/health", 8008)).withCopyContentToContainer([ + { target: "/data/localhost.signing.key", content: `ed25519 x ${signingKey}` }, + { + target: "/data/log.config", + content: YAML.stringify({ + version: 1, + formatters: { + precise: { + format: "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s", + }, + }, + handlers: { + console: { + class: "logging.StreamHandler", + formatter: "precise", + }, + }, + loggers: { + "synapse.storage.SQL": { + level: "DEBUG", + }, + "twisted": { + handlers: ["console"], + propagate: false, + }, + }, + root: { + level: "DEBUG", + handlers: ["console"], + }, + disable_existing_loggers: false, + }), + }, + ]); + } + + public withConfigField(key: string, value: any): this { + set(this.config, key, value); + return this; + } + + public withConfig(config: Partial): this { + this.config = { + ...this.config, + ...config, + }; + return this; + } + + public override async start(): Promise { + const port = await getFreePort(); + + this.withExposedPorts({ + container: 8008, + host: port, + }) + .withConfig({ + public_baseurl: `http://localhost:${port}`, + }) + .withCopyContentToContainer([ + { + target: "/data/homeserver.yaml", + content: YAML.stringify(this.config), + }, + ]); + + return new StartedSynapseContainer( + await super.start(), + `http://localhost:${port}`, + this.config.registration_shared_secret, + this.request, + ); + } +} + +export class StartedSynapseContainer extends AbstractStartedContainer implements HomeserverInstance { + private adminToken?: string; + + constructor( + container: StartedTestContainer, + public readonly baseUrl: string, + private readonly registrationSharedSecret: string, + private readonly request: APIRequestContext, + ) { + super(container); + } + + private async registerUserInternal( + username: string, + password: string, + displayName?: string, + admin = false, + ): Promise { + const url = `${this.baseUrl}/_synapse/admin/v1/register`; + const { nonce } = await this.request.get(url).then((r) => r.json()); + const mac = crypto + .createHmac("sha1", this.registrationSharedSecret) + .update(`${nonce}\0${username}\0${password}\0${admin ? "" : "not"}admin`) + .digest("hex"); + const res = await this.request.post(url, { + data: { + nonce, + username, + password, + mac, + admin, + displayname: displayName, + }, + }); + + if (!res.ok()) { + throw await res.json(); + } + + const data = await res.json(); + return { + homeServer: data.home_server, + accessToken: data.access_token, + userId: data.user_id, + deviceId: data.device_id, + password, + displayName, + }; + } + + public registerUser(username: string, password: string, displayName?: string): Promise { + return this.registerUserInternal(username, password, displayName, false); + } + + public async loginUser(userId: string, password: string): Promise { + const url = `${this.baseUrl}/_matrix/client/v3/login`; + const res = await this.request.post(url, { + data: { + type: "m.login.password", + identifier: { + type: "m.id.user", + user: userId, + }, + password: password, + }, + }); + const json = await res.json(); + + return { + password, + accessToken: json.access_token, + userId: json.user_id, + deviceId: json.device_id, + homeServer: json.home_server, + }; + } + + public async setThreepid(userId: string, medium: string, address: string): Promise { + if (this.adminToken === undefined) { + const result = await this.registerUserInternal("admin", "totalyinsecureadminpassword", undefined, true); + this.adminToken = result.accessToken; + } + + const url = `${this.baseUrl}/_synapse/admin/v2/users/${userId}`; + const res = await this.request.put(url, { + data: { + threepids: [ + { + medium, + address, + }, + ], + }, + headers: { + Authorization: `Bearer ${this.adminToken}`, + }, + }); + + if (!res.ok()) { + throw await res.json(); + } + } +} diff --git a/yarn.lock b/yarn.lock index e72b11a5ed..a68b8cf97a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1158,6 +1158,11 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@balena/dockerignore@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" + integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1542,6 +1547,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@floating-ui/core@^1.6.0": version "1.6.8" resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.8.tgz#aa43561be075815879305965020f492cdb43da12" @@ -2076,6 +2086,11 @@ tslib "^2.6.2" webcrypto-core "^1.8.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@playwright/test@^1.40.1": version "1.49.1" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.1.tgz#55fa360658b3187bfb6371e2f8a64f50ef80c827" @@ -2657,6 +2672,13 @@ "@svgr/plugin-jsx" "8.1.0" "@svgr/plugin-svgo" "8.1.0" +"@testcontainers/postgresql@^10.16.0": + version "10.16.0" + resolved "https://registry.yarnpkg.com/@testcontainers/postgresql/-/postgresql-10.16.0.tgz#0437a9b426d64ea958e745a0e2ae19462b786f81" + integrity sha512-zWFQI+3QxlEELRvVv27i6zlVEPNUz9zKaSh7iWmFlCdfhcyr78daS0FG8FIfdQ79VK7YXA4jv+dTYXa2SwXu/w== + dependencies: + testcontainers "^10.16.0" + "@testing-library/dom@^10.4.0": version "10.4.0" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.0.tgz#82a9d9462f11d240ecadbf406607c6ceeeff43a8" @@ -2824,6 +2846,23 @@ resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz#dcef10a69d357fe9d43ac4ff2eca6b85dbf466af" integrity sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg== +"@types/docker-modem@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/docker-modem/-/docker-modem-3.0.6.tgz#1f9262fcf85425b158ca725699a03eb23cddbf87" + integrity sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg== + dependencies: + "@types/node" "*" + "@types/ssh2" "*" + +"@types/dockerode@^3.3.29": + version "3.3.33" + resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-3.3.33.tgz#67d9b4223caf41a0735695abe89c292e05d305c9" + integrity sha512-7av8lVOhkW7Xd11aZTSq5zhdpyNraldXwQR0pxUCiSNTvIzsP86KrFrmrZgxtrXD2Zrtzwt4H6OYLbATONWzWg== + dependencies: + "@types/docker-modem" "*" + "@types/node" "*" + "@types/ssh2" "*" + "@types/escape-html@^1.0.1": version "1.0.4" resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-1.0.4.tgz#dc7c166b76c7b03b27e32f80edf01d91eb5d9af2" @@ -3088,6 +3127,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@^18.11.18": + version "18.19.69" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.69.tgz#748d301818ba4b238854c53d290257a70aae7d01" + integrity sha512-ECPdY1nlaiO/Y6GUnwgtAAhLNaQ53AyIVz+eILxpEo5OvuqE6yWkqWBIb5dU0DqhKQtMeny+FBD3PK6lm7L5xQ== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" @@ -3232,6 +3278,28 @@ dependencies: "@types/node" "*" +"@types/ssh2-streams@*": + version "0.1.12" + resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz#e68795ba2bf01c76b93f9c9809e1f42f0eaaec5f" + integrity sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg== + dependencies: + "@types/node" "*" + +"@types/ssh2@*": + version "1.15.1" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.15.1.tgz#4db4b6864abca09eb299fe5354fa591add412223" + integrity sha512-ZIbEqKAsi5gj35y4P4vkJYly642wIbY6PqoN0xiyQGshKUGXR9WQjF/iF9mXBQ8uBKy3ezfsCkcoHKhd0BzuDA== + dependencies: + "@types/node" "^18.11.18" + +"@types/ssh2@^0.5.48": + version "0.5.52" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741" + integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg== + dependencies: + "@types/node" "*" + "@types/ssh2-streams" "*" + "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -3390,7 +3458,7 @@ ts-xor "^1.3.0" vaul "^1.0.0" -"@vector-im/matrix-wysiwyg-wasm@link:../../bindings/wysiwyg-wasm": +"@vector-im/matrix-wysiwyg-wasm@link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.38.0-af862ffd231dc0a6b8d6f2cb3601e68456c0ff24-integrity/node_modules/bindings/wysiwyg-wasm": version "0.0.0" uid "" @@ -3569,6 +3637,13 @@ abab@^2.0.6: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -3727,6 +3802,32 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +archiver-utils@^5.0.0, archiver-utils@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-5.0.2.tgz#63bc719d951803efc72cf961a56ef810760dd14d" + integrity sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA== + dependencies: + glob "^10.0.0" + graceful-fs "^4.2.0" + is-stream "^2.0.1" + lazystream "^1.0.0" + lodash "^4.17.15" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + +archiver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-7.0.1.tgz#c9d91c350362040b8927379c7aa69c0655122f61" + integrity sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ== + dependencies: + archiver-utils "^5.0.2" + async "^3.2.4" + buffer-crc32 "^1.0.0" + readable-stream "^4.0.0" + readdir-glob "^1.1.2" + tar-stream "^3.0.0" + zip-stream "^6.0.1" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -3862,6 +3963,13 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +asn1@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + asn1js@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" @@ -3881,7 +3989,12 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async@^3.2.3: +async-lock@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f" + integrity sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ== + +async@^3.2.3, async@^3.2.4: version "3.2.6" resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== @@ -3925,6 +4038,11 @@ axobject-query@^4.1.0: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== +b4a@^1.6.4: + version "1.6.7" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" + integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== + babel-jest@^29.0.0, babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -4035,6 +4153,39 @@ balanced-match@^2.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9" integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA== +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.0.tgz#305b511e262ffd8b9d5616b056464f8e1b3329cc" + integrity sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A== + +bare-fs@^2.1.1: + version "2.3.5" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-2.3.5.tgz#05daa8e8206aeb46d13c2fe25a2cd3797b0d284a" + integrity sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw== + dependencies: + bare-events "^2.0.0" + bare-path "^2.0.0" + bare-stream "^2.0.0" + +bare-os@^2.1.0: + version "2.4.4" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-2.4.4.tgz#01243392eb0a6e947177bb7c8a45123d45c9b1a9" + integrity sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ== + +bare-path@^2.0.0, bare-path@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-2.1.3.tgz#594104c829ef660e43b5589ec8daef7df6cedb3e" + integrity sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA== + dependencies: + bare-os "^2.1.0" + +bare-stream@^2.0.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.6.1.tgz#b3b9874fab05b662c9aea2706a12fb0698c46836" + integrity sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g== + dependencies: + streamx "^2.21.0" + base-x@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.0.tgz#6d835ceae379130e1a4cb846a70ac4746f28ea9b" @@ -4045,11 +4196,23 @@ base64-arraybuffer@^1.0.2: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc" integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== +bcrypt-pbkdf@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -4060,6 +4223,15 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + blob-polyfill@^9.0.0: version "9.0.20240710" resolved "https://registry.yarnpkg.com/blob-polyfill/-/blob-polyfill-9.0.20240710.tgz#2ef075a207311ea327704f04dc4a98cbefe4143b" @@ -4161,11 +4333,37 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-crc32@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" + integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buildcheck@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" + integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== + builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" @@ -4178,6 +4376,11 @@ bundle-name@^4.1.0: dependencies: run-applescript "^7.0.0" +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -4322,6 +4525,11 @@ chokidar@^4.0.0: dependencies: readdirp "^4.0.1" +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + chrome-trace-event@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" @@ -4518,6 +4726,17 @@ commonmark@^0.31.0: mdurl "~1.0.1" minimist "~1.2.8" +compress-commons@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-6.0.2.tgz#26d31251a66b9d6ba23a84064ecd3a6a71d2609e" + integrity sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg== + dependencies: + crc-32 "^1.2.0" + crc32-stream "^6.0.0" + is-stream "^2.0.1" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -4659,11 +4878,32 @@ counterpart@^0.18.6: pluralizers "^0.1.7" sprintf-js "^1.0.3" +cpu-features@~0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" + integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== + dependencies: + buildcheck "~0.0.6" + nan "^2.19.0" + crc-32@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-0.3.0.tgz#6a3d3687f5baec41f7e9b99fe1953a2e5d19775e" integrity sha512-kucVIjOmMc1f0tv53BJ/5WIX+MGLcKuoBhnGqQrgKJNqLByb/sVMWfW/Aw6hw0jgcqjJ2pi9E5y32zOIpaUlsA== +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-6.0.0.tgz#8529a3868f8b27abb915f6c3617c0fadedbf9430" + integrity sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g== + dependencies: + crc-32 "^1.2.0" + readable-stream "^4.0.0" + create-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" @@ -4972,7 +5212,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.3.1: +debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.5: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -5140,6 +5380,32 @@ dns-packet@^5.2.2: dependencies: "@leichtgewicht/ip-codec" "^2.0.1" +docker-compose@^0.24.8: + version "0.24.8" + resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.24.8.tgz#6c125e6b9e04cf68ced47e2596ef2bb93ee9694e" + integrity sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw== + dependencies: + yaml "^2.2.2" + +docker-modem@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.8.tgz#ef62c8bdff6e8a7d12f0160988c295ea8705e77a" + integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^1.11.0" + +dockerode@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629" + integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== + dependencies: + "@balena/dockerignore" "^1.0.2" + docker-modem "^3.0.0" + tar-fs "~2.0.1" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -5354,6 +5620,13 @@ encodeurl@~2.0.0: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + enhanced-resolve@^5.17.1: version "5.17.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" @@ -5832,6 +6105,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -5842,7 +6120,7 @@ eventemitter3@^5.0.1: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== -events@^3.2.0: +events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -5952,6 +6230,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -6216,6 +6499,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@^11.0.0: version "11.2.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" @@ -6320,6 +6608,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -6368,6 +6661,18 @@ glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@^10.0.0: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" @@ -6780,7 +7085,7 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.1.12: +ieee754@^1.1.12, ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -6839,7 +7144,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -7127,7 +7432,7 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: dependencies: call-bind "^1.0.7" -is-stream@^2.0.0: +is-stream@^2.0.0, is-stream@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== @@ -7280,6 +7585,15 @@ iterator.prototype@^1.1.3: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jackspeak@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" @@ -7940,6 +8254,13 @@ launch-editor@^2.6.1: picocolors "^1.0.0" shell-quote "^1.8.1" +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -8095,7 +8416,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8439,7 +8760,7 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1: +minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== @@ -8475,6 +8796,11 @@ minipass@^4.2.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp@0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -8535,6 +8861,11 @@ murmurhash-js@^1.0.0: resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51" integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw== +nan@^2.19.0, nan@^2.20.0: + version "2.22.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" + integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== + nanoid@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -8717,7 +9048,7 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -8953,7 +9284,7 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.6.1: +path-scurry@^1.11.1, path-scurry@^1.6.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== @@ -9780,6 +10111,22 @@ prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proper-lockfile@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +properties-reader@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/properties-reader/-/properties-reader-2.3.0.tgz#f3ab84224c9535a7a36e011ae489a79a13b472b2" + integrity sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw== + dependencies: + mkdirp "^1.0.4" + protocol-buffers-schema@^3.3.1: version "3.6.0" resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" @@ -9803,6 +10150,14 @@ psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +pump@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0, punycode@^2.1.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -9856,6 +10211,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + quickselect@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" @@ -10049,7 +10409,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.0.1, readable-stream@~2.3.6: +readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -10062,7 +10422,7 @@ readable-stream@^2.0.1, readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6: +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -10071,6 +10431,24 @@ readable-stream@^3.0.6: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.6.0.tgz#ce412dfb19c04efde1c5936d99c27f37a1ff94c9" + integrity sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readdir-glob@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== + dependencies: + minimatch "^5.1.0" + readdirp@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" @@ -10289,6 +10667,11 @@ restore-cursor@^5.0.0: onetime "^7.0.0" signal-exit "^4.1.0" +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + retry@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -10377,7 +10760,7 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -10597,7 +10980,7 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" -signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -10761,6 +11144,11 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" +split-ca@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== + sprintf-js@^1.0.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" @@ -10771,6 +11159,25 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +ssh-remote-port-forward@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz#72b0c5df8ec27ca300c75805cc6b266dee07e298" + integrity sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ== + dependencies: + "@types/ssh2" "^0.5.48" + ssh2 "^1.4.0" + +ssh2@^1.11.0, ssh2@^1.4.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.16.0.tgz#79221d40cbf4d03d07fe881149de0a9de928c9f0" + integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg== + dependencies: + asn1 "^0.2.6" + bcrypt-pbkdf "^1.0.2" + optionalDependencies: + cpu-features "~0.0.10" + nan "^2.20.0" + stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -10788,6 +11195,17 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +streamx@^2.15.0, streamx@^2.21.0: + version "2.21.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.21.1.tgz#f02979d8395b6b637d08a589fb514498bed55845" + integrity sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + string-argv@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" @@ -10900,7 +11318,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -11158,11 +11576,52 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +tar-fs@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.6.tgz#eaccd3a67d5672f09ca8e8f9c3d2b89fa173f217" + integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w== + dependencies: + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" + +tar-fs@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" + integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + tar-js@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/tar-js/-/tar-js-0.3.0.tgz#6949aabfb0ba18bb1562ae51a439fd0f30183a17" integrity sha512-9uqP2hJUZNKRkwPDe5nXxXdzo6w+BFBPq9x/tyi5/U/DneuSesO/HMb0y5TeWpfcv49YDJTs7SrrZeeu8ZHWDA== +tar-stream@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar-stream@^3.0.0, tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + temporal-polyfill@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/temporal-polyfill/-/temporal-polyfill-0.2.5.tgz#0796c40a50754c69ec0f9a2db3f6c582b9721aaf" @@ -11215,6 +11674,34 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +testcontainers@^10.16.0: + version "10.16.0" + resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-10.16.0.tgz#8a7e69ada5cd2c6cce1c6db72b3a3e8e412fcaf6" + integrity sha512-oxPLuOtrRWS11A+Yn0+zXB7GkmNarflWqmy6CQJk8KJ75LZs2/zlUXDpizTbPpCGtk4kE2EQYwFZjrE967F8Wg== + dependencies: + "@balena/dockerignore" "^1.0.2" + "@types/dockerode" "^3.3.29" + archiver "^7.0.1" + async-lock "^1.4.1" + byline "^5.0.0" + debug "^4.3.5" + docker-compose "^0.24.8" + dockerode "^3.3.5" + get-port "^5.1.1" + proper-lockfile "^4.1.2" + properties-reader "^2.3.0" + ssh-remote-port-forward "^1.0.4" + tar-fs "^3.0.6" + tmp "^0.2.3" + undici "^5.28.4" + +text-decoder@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" + integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== + dependencies: + b4a "^1.6.4" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -11248,6 +11735,11 @@ tinyqueue@^3.0.0: resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-3.0.0.tgz#101ea761ccc81f979e29200929e78f1556e3661e" integrity sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g== +tmp@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" + integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -11395,6 +11887,11 @@ tslib@^2.0.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2, tslib@^2.7.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== +tweetnacl@^0.14.3: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -11520,6 +12017,13 @@ undici-types@~6.20.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== +undici@^5.28.4: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + unhomoglyph@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3" @@ -12174,6 +12678,11 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.2: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + yaml@^2.3.3: version "2.6.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" @@ -12242,6 +12751,15 @@ yocto-queue@^1.0.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== +zip-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-6.0.1.tgz#e141b930ed60ccaf5d7fa9c8260e0d1748a2bbfb" + integrity sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA== + dependencies: + archiver-utils "^5.0.0" + compress-commons "^6.0.2" + readable-stream "^4.0.0" + zod-validation-error@^3.0.3: version "3.4.0" resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6"