import { spawn } from "node:child_process"; import { argon2 } from "node:crypto"; import { chmodSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs"; import { createServer as createHttpServer } from "node:http"; import { request as httpsRequest } from "node:https"; import { createServer } from "node:net"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { startFakeOidcProvider } from "../../../backend/test/fixtures/oidc-provider.mjs"; const __dir = dirname(fileURLToPath(import.meta.url)); const repositoryRoot = resolve(__dir, "../../.."); const frontendRoot = join(repositoryRoot, "frontend"); const backendRoot = join(repositoryRoot, "backend"); const harnessRoot = join(repositoryRoot, "harness"); const thtRoot = join(repositoryRoot, "tools", "tht"); const fakePi = join(frontendRoot, "e2e", "fixtures", "fake-pi.mjs"); const fakeTht = join(frontendRoot, "e2e", "fixtures", "fake-tht.mjs"); const FIXTURE_CLIENT_ID = "thothii-e2e-client"; const DEFAULT_FIXTURE_CLIENT_SECRET = "e2e-client-secret-not-a-production-secret"; const DEFAULT_FIXTURE_API_TOKEN = "e2e-group-catalog-token-not-a-production-secret"; const WRONG_CLIENT_ID = "thothii-e2e-unregistered-client"; const WRONG_CLIENT_SECRET = "e2e-wrong-client-secret-not-production"; const WRONG_API_TOKEN = "e2e-wrong-api-token-not-production"; const OIDC_CREDENTIAL_VARIANTS = new Set([ "correct", "wrong-client-id", "wrong-client-secret", "wrong-api-token", ]); function safeError(code) { return new Error(code); } function resolveFixtureCredentials() { const runtimeCredential = process.env.THT_TASK15_SENTINEL; if (runtimeCredential === undefined) { return Object.freeze({ clientSecret: DEFAULT_FIXTURE_CLIENT_SECRET, apiToken: DEFAULT_FIXTURE_API_TOKEN, runtime: false, }); } if (runtimeCredential.length < 24 || runtimeCredential.length > 512 || /[\r\n\0]/u.test(runtimeCredential)) { throw safeError("e2e_runtime_fixture_credential_invalid"); } return Object.freeze({ clientSecret: runtimeCredential, apiToken: runtimeCredential, runtime: true }); } function buildAuthenticationStorageBridge(output) { const result = spawn("go", ["build", "-o", output, "./cmd/tht"], { cwd: thtRoot, stdio: "ignore", }); return new Promise((resolveBuild, rejectBuild) => { result.once("error", () => rejectBuild(safeError("e2e_auth_storage_build_failed"))); result.once("exit", (code) => code === 0 ? resolveBuild() : rejectBuild(safeError("e2e_auth_storage_build_failed"))); }); } function secureDirectory(path) { mkdirSync(path, { recursive: true, mode: 0o700 }); chmodSync(path, 0o700); } function runFixtureCommand(command, args, cwd) { const child = spawn(command, args, { cwd, stdio: "ignore" }); return new Promise((resolveCommand, rejectCommand) => { child.once("error", () => rejectCommand(safeError("e2e_workspace_fixture_command_failed"))); child.once("exit", (code) => code === 0 ? resolveCommand() : rejectCommand(safeError("e2e_workspace_fixture_command_failed"))); }); } const F1_WORKSPACE_ID = "fixture-workspace"; const F1_WORKSPACE_DESCRIPTOR = `workspace: schema_version: 4 id: fixture-workspace name: Fixture workspace language: en dwh: engine: postgres database: fixture schema: fixture supported_transports: [postgres_direct] `; async function prepareF1Workspace(root, memoryOnly = false) { const source = join(root, "workspace-source"); const remote = join(root, "workspace-remote.git"); secureDirectory(source); secureDirectory(join(source, F1_WORKSPACE_ID)); writeSecure(join(source, "thoth-workspaces.yaml"), [ "schema_version: 1", "workspaces:", ` - id: ${F1_WORKSPACE_ID}`, " name: Fixture workspace", "", ].join("\n")); writeSecure(join(source, F1_WORKSPACE_ID, "workspace.yaml"), memoryOnly ? F1_WORKSPACE_DESCRIPTOR.split("dwh:")[0] : F1_WORKSPACE_DESCRIPTOR); await runFixtureCommand("git", ["init", "--bare", "--initial-branch=main", remote], root); chmodSync(remote, 0o700); await runFixtureCommand("git", ["init", "--initial-branch=main"], source); await runFixtureCommand("git", ["config", "user.name", "ThothII E2E Fixture"], source); await runFixtureCommand("git", ["config", "user.email", "thothii-e2e@example.invalid"], source); await runFixtureCommand("git", ["add", "-A"], source); await runFixtureCommand("git", ["commit", "-m", "Create deterministic fixture workspace"], source); await runFixtureCommand("git", ["remote", "add", "origin", remote], source); await runFixtureCommand("git", ["push", "origin", "main"], source); return { id: F1_WORKSPACE_ID, remote }; } function writeSecure(path, value) { writeFileSync(path, value, { encoding: "utf8", mode: 0o600 }); chmodSync(path, 0o600); } async function testPasswordHash(password) { const salt = Buffer.from("thothii-e2e-salt"); const message = Buffer.from(password, "utf8"); let digest; try { digest = await new Promise((resolveDigest, rejectDigest) => { argon2("argon2id", { message, nonce: salt, memory: 65_536, passes: 3, parallelism: 1, tagLength: 32, }, (error, derived) => error || !derived ? rejectDigest(error ?? safeError("e2e_password_hash_failed")) : resolveDigest(derived)); }); return `$argon2id$v=19$m=65536,t=3,p=1$${salt.toString("base64").replaceAll("=", "")}$${digest.toString("base64").replaceAll("=", "")}`; } finally { message.fill(0); salt.fill(0); digest?.fill(0); } } function pause(milliseconds) { return new Promise((resolvePause) => setTimeout(resolvePause, milliseconds)); } function providerJson(url, caFile, options = {}) { return new Promise((resolveResponse, rejectResponse) => { const body = options.body ?? ""; const request = httpsRequest(url, { method: options.method ?? "GET", ca: readFileSync(caFile), headers: { accept: "application/json", ...(body.length === 0 ? {} : { "content-length": String(Buffer.byteLength(body)), "content-type": "application/x-www-form-urlencoded", }), ...options.headers, }, }, (response) => { const chunks = []; let size = 0; response.on("data", (chunk) => { size += chunk.length; if (size > 64 * 1024) request.destroy(safeError("e2e_provider_response_too_large")); else chunks.push(chunk); }); response.once("error", () => rejectResponse(safeError("e2e_provider_response_failed"))); response.once("end", () => { try { resolveResponse({ status: response.statusCode ?? 0, body: JSON.parse(Buffer.concat(chunks).toString("utf8")) }); } catch { rejectResponse(safeError("e2e_provider_response_invalid")); } }); }); request.once("error", () => rejectResponse(safeError("e2e_provider_request_failed"))); request.end(body); }); } async function freeLoopbackPort() { const server = createServer(); await new Promise((resolveListen, rejectListen) => { server.once("error", rejectListen); server.listen({ host: "127.0.0.1", port: 0 }, resolveListen); }); const address = server.address(); await new Promise((resolveClose) => server.close(resolveClose)); if (!address || typeof address === "string") throw safeError("e2e_loopback_port_unavailable"); return address.port; } const F1_QDRANT_INDEXES = Object.freeze([ "content_hash", "document_id", "kind", "record_key", "record_kind", "vector_generation", "workspace_id", "workspace_revision", ]); async function startFakeQdrant() { const payloadSchema = Object.fromEntries(F1_QDRANT_INDEXES.map((field) => [field, { data_type: "keyword" }])); const server = createHttpServer((request, response) => { const path = new URL(request.url ?? "/", "http://loopback.invalid").pathname; if (request.method !== "GET" || path !== `/collections/${F1_WORKSPACE_ID}`) { response.writeHead(404).end(); return; } response.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ result: { config: { params: { vectors: { size: 1024, distance: "Cosine" } } }, payload_schema: payloadSchema, }, })); }); await new Promise((resolveListen, rejectListen) => { server.once("error", rejectListen); server.listen({ host: "127.0.0.1", port: 0 }, resolveListen); }); const address = server.address(); if (!address || typeof address === "string") { await new Promise((resolveClose) => server.close(resolveClose)); throw safeError("e2e_qdrant_loopback_port_unavailable"); } return { baseUrl: `http://127.0.0.1:${address.port}/`, close: () => new Promise((resolveClose) => server.close(resolveClose)), }; } function managedProcess(command, args, options) { const child = spawn(command, args, { cwd: options.cwd, env: options.env, stdio: ["ignore", "pipe", "pipe"], }); let exited = false; let diagnostic = ""; const captureDiagnostic = (chunk) => { const sanitized = String(chunk) .replace(/https?:\/\/[^\s)]+/g, "[url]") .replace(/(?:THT_[A-Z_]+|PI_[A-Z_]+|AUTH_MODE)=\S+/g, "$1=[redacted]") .replace(/[A-Za-z0-9_-]{43,}/g, "[redacted]"); diagnostic = `${diagnostic}${sanitized}`.slice(-800); }; child.once("exit", () => { exited = true; }); child.stdout?.on("data", captureDiagnostic); child.stderr?.on("data", captureDiagnostic); return { child, exited: () => exited, diagnostic: () => diagnostic.replace(/\s+/g, " ").trim(), async close() { if (exited) return; child.kill("SIGTERM"); for (let attempt = 0; attempt < 50 && !exited; attempt += 1) await pause(20); if (!exited) child.kill("SIGKILL"); for (let attempt = 0; attempt < 50 && !exited; attempt += 1) await pause(20); }, }; } function oneShotJson(command, args, options) { return new Promise((resolveCommand) => { const child = spawn(command, args, { cwd: options.cwd, env: options.env, stdio: ["ignore", "pipe", "ignore"], }); let output = ""; let outputValid = true; let settled = false; const finish = (status) => { if (settled) return; settled = true; clearTimeout(timer); let report; try { report = outputValid ? JSON.parse(output) : undefined; } catch { report = undefined; } resolveCommand({ status: Number.isInteger(status) ? status : 1, report: report && typeof report === "object" ? report : { ready: false, mode: "none", checks: [{ code: "fixture_command_invalid" }] }, }); }; child.stdout?.on("data", (chunk) => { if (!outputValid) return; output += String(chunk); if (Buffer.byteLength(output) > 64 * 1024) { output = ""; outputValid = false; child.kill("SIGKILL"); } }); child.once("error", () => finish(1)); child.once("exit", (status) => finish(status)); const timer = setTimeout(() => child.kill("SIGKILL"), 35_000); }); } async function waitForOk(url, processHandle) { for (let attempt = 0; attempt < 300; attempt += 1) { if (processHandle.exited()) { const detail = processHandle.diagnostic(); throw safeError(detail ? `e2e_service_stopped_during_startup:${detail}` : "e2e_service_stopped_during_startup"); } try { const response = await fetch(url, { redirect: "error" }); if (response.ok) return; } catch { // The process is expected to race its listener setup. } await pause(100); } throw safeError("e2e_service_startup_timeout"); } function cleanBackendEnvironment(overrides) { const env = { ...process.env }; for (const name of [ "AUTH_MODE", "THT_AUTH_CONFIG_FILE", "THT_AUTH_STATE_ROOT", "THT_SECRETS_FILE", "NODE_EXTRA_CA_CERTS", "SETTINGS_FILE", "THT_MAINTENANCE_FILE", "THT_WORKSPACE_REGISTRY_ROOT", "THT_WORKSPACE_SECRET_STORE_ROOT", "THT_WORKSPACE_SECRET_RUNTIME_ROOT", "THT_WORKSPACE_GIT_REMOTE", "THT_WORKSPACE_GIT_BRANCH", "THT_WORKSPACE_SECRET_ROOTS", "THT_WORKSPACE_INSTALLATION_ID", "THT_LEGACY_WORKSPACE_MODE", "THT_DWH_PRECHECK", "THT_INTERNAL_QDRANT_URL", "THT_CONFIG", "THT_TASK15_SENTINEL", ]) delete env[name]; for (const name of Object.keys(env)) { if (name.startsWith("THT_WS_")) delete env[name]; } return { ...env, ...overrides }; } export async function createAuthenticationStack({ withF1Workspace = false, memoryRuntime = {} } = {}) { const credentials = resolveFixtureCredentials(); const root = mkdtempSync(join(realpathSync(tmpdir()), "thothii-auth-e2e-")); secureDirectory(root); const stateRoot = join(root, "auth-state"); const registryRoot = join(root, "workspace-registry"); const workspaceSecretRoot = join(root, "workspace-secrets"); const workspaceRuntimeRoot = join(root, "workspace-runtime"); const fixtureSecretRoot = join(root, "fixture-runtime-secrets"); const piAgentRoot = join(root, "pi-agent"); const providerRoot = join(root, "provider"); const authConfigFile = join(root, "auth.yaml"); const usersFile = join(root, "users.yaml"); const secretsFile = join(root, "test.secrets"); const settingsFile = join(root, "settings.json"); const maintenanceFile = join(root, "maintenance.json"); const authStorageBinary = join(root, "tht-auth-storage"); const fixtureDwhPasswordFile = join(fixtureSecretRoot, "fixture-dwh-password"); const fixtureDwhCaFile = join(fixtureSecretRoot, "fixture-dwh-ca.pem"); for (const path of [stateRoot, registryRoot, workspaceSecretRoot, workspaceRuntimeRoot, fixtureSecretRoot, piAgentRoot, providerRoot]) secureDirectory(path); for (const child of ["sessions", "oidc"]) secureDirectory(join(stateRoot, child)); writeSecure(join(piAgentRoot, "settings.json"), JSON.stringify({ enabledModels: ["zai/glm-5.2"], })); writeSecure(join(piAgentRoot, "auth.json"), JSON.stringify({ zai: { type: "api_key", key: "e2e-model-key-not-a-production-secret" }, })); const [frontendPort, backendPort] = await Promise.all([freeLoopbackPort(), freeLoopbackPort()]); const publicUrl = `http://127.0.0.1:${frontendPort}`; const backendUrl = `http://127.0.0.1:${backendPort}`; const workspace = withF1Workspace ? await prepareF1Workspace(root, Object.keys(memoryRuntime).length > 0) : undefined; const provider = await startFakeOidcProvider({ directory: providerRoot, registration: { clientId: FIXTURE_CLIENT_ID, clientSecret: credentials.clientSecret, redirectUri: new URL("/api/auth/oidc/callback", publicUrl).href, }, apiToken: credentials.apiToken, }); await buildAuthenticationStorageBridge(authStorageBinary); const localPassword = "e2e-local-password"; const passwordHash = await testPasswordHash(localPassword); const accounts = Object.freeze({ ordinary: Object.freeze({ username: "ordinary", password: localPassword }), admin: Object.freeze({ username: "administrator", password: localPassword }), }); writeSecure(usersFile, JSON.stringify({ version: 1, users: [ { id: "11111111-1111-4111-8111-111111111111", username: accounts.ordinary.username, displayName: "Fixture ordinary", passwordHash, roles: ["user"], enabled: true, authRevision: 1, }, { id: "22222222-2222-4222-8222-222222222222", username: accounts.admin.username, displayName: "Fixture administrator", passwordHash, roles: ["admin"], enabled: true, authRevision: 1, }, ], })); function writeOidcSecrets(variant) { if (!OIDC_CREDENTIAL_VARIANTS.has(variant)) throw safeError("e2e_oidc_credential_variant_invalid"); const selectedClientSecret = variant === "wrong-client-secret" ? WRONG_CLIENT_SECRET : credentials.clientSecret; const selectedApiToken = variant === "wrong-api-token" ? WRONG_API_TOKEN : credentials.apiToken; writeSecure(secretsFile, [ `THT_OIDC_CLIENT_SECRET=${selectedClientSecret}`, `THT_AUTHENTIK_API_TOKEN=${selectedApiToken}`, "", ].join("\n")); } writeOidcSecrets("correct"); if (workspace) { writeSecure(fixtureDwhPasswordFile, "fixture-password-not-a-secret\n"); writeSecure(fixtureDwhCaFile, "fixture-ca-not-a-production-certificate\n"); writeSecure(settingsFile, JSON.stringify({ workspace: workspace.id, provider: "zai", model: "glm-5.2", thinking: "medium", })); } let mode = undefined; let backend = undefined; let qdrant = undefined; const frontend = managedProcess(join(frontendRoot, "node_modules", ".bin", "vite"), [ "--host", "127.0.0.1", "--port", String(frontendPort), "--strictPort", ], { cwd: frontendRoot, env: { ...process.env, THT_FRONTEND_API_UPSTREAM: backendUrl, }, }); try { await waitForOk(publicUrl, frontend); qdrant = workspace ? await startFakeQdrant() : undefined; const localConfig = () => ({ version: 1, mode: "local", publicUrl, session: { regularTtlSeconds: 600, regularIdleSeconds: 600, rememberTtlSeconds: 2_592_000, rememberIdleSeconds: 604_800, }, local: { usersFile: "users.yaml" }, }); const oidcConfig = (variant = "correct") => ({ version: 1, mode: "oidc", publicUrl, session: { regularTtlSeconds: 600, regularIdleSeconds: 600, oidcTtlSeconds: 60, }, oidc: { issuer: provider.issuer, clientId: variant === "wrong-client-id" ? WRONG_CLIENT_ID : FIXTURE_CLIENT_ID, clientSecretRef: "THT_OIDC_CLIENT_SECRET", scopes: ["openid", "profile", "groups"], groupsClaim: "groups", }, groupCatalog: { driver: "authentik", baseUrl: provider.baseUrl, apiTokenRef: "THT_AUTHENTIK_API_TOKEN", }, authorization: { groupRoles: { "fixture-users": ["user"], "fixture-admin": ["admin"], }, }, }); function backendEnvironment() { return cleanBackendEnvironment({ NODE_ENV: "test", HOST: "127.0.0.1", PORT: String(backendPort), PI_BIN: fakePi, PI_CODING_AGENT_DIR: piAgentRoot, THT_BIN: fakeTht, THT_AUTH_STORAGE_BIN: authStorageBinary, THT_HARNESS_DIR: harnessRoot, THT_AUTH_CONFIG_FILE: authConfigFile, THT_AUTH_STATE_ROOT: stateRoot, THT_SECRETS_FILE: secretsFile, NODE_EXTRA_CA_CERTS: provider.caFile, SETTINGS_FILE: settingsFile, THT_MAINTENANCE_FILE: maintenanceFile, THT_WORKSPACE_REGISTRY_ROOT: registryRoot, THT_WORKSPACE_SECRET_STORE_ROOT: workspaceSecretRoot, THT_WORKSPACE_SECRET_RUNTIME_ROOT: workspaceRuntimeRoot, THT_WORKSPACE_INSTALLATION_ID: "e2e", THT_DATA_ROOT: join(root, "data"), ...(qdrant ? { THT_INTERNAL_QDRANT_URL: qdrant.baseUrl } : {}), ...(workspace ? { THT_WORKSPACE_GIT_REMOTE: workspace.remote, THT_WORKSPACE_GIT_BRANCH: "main", THT_WORKSPACE_SECRET_ROOTS: fixtureSecretRoot, THT_WS_FIXTURE_WORKSPACE_DWH_TRANSPORT: "postgres_direct", THT_WS_FIXTURE_WORKSPACE_DWH_HOST: "127.0.0.1", THT_WS_FIXTURE_WORKSPACE_DWH_PORT: "5432", THT_WS_FIXTURE_WORKSPACE_DWH_USER: "fixture", THT_WS_FIXTURE_WORKSPACE_DWH_PASSWORD_FILE: fixtureDwhPasswordFile, THT_WS_FIXTURE_WORKSPACE_DWH_TLS_CA_FILE: fixtureDwhCaFile, } : {}), ...memoryRuntime, }); } async function startBackend() { if (mode === undefined) throw safeError("e2e_auth_mode_not_configured"); backend = managedProcess(join(backendRoot, "node_modules", ".bin", "tsx"), ["src/server.ts"], { cwd: backendRoot, env: backendEnvironment(), }); await waitForOk(`${backendUrl}/health`, backend); } async function restartBackend() { await backend?.close(); backend = undefined; await startBackend(); } return { publicUrl, fixtureWorkspaceRoot() { if (!workspace) throw safeError("e2e_workspace_fixture_missing"); return join(registryRoot, "repo", workspace.id); }, localAccount(account) { const found = accounts[account]; if (!found) throw safeError("e2e_local_account_unknown"); return found; }, lastAuthorization() { return provider.lastAuthorization(); }, async providerSurface() { const discovery = await providerJson(`${provider.issuer}.well-known/openid-configuration`, provider.caFile); const jwks = await providerJson(`${provider.issuer}jwks`, provider.caFile); const device = await providerJson(`${provider.issuer}device_authorization`, provider.caFile, { method: "POST", body: new URLSearchParams({ client_id: FIXTURE_CLIENT_ID, client_secret: credentials.clientSecret, }).toString(), }); const deviceCode = typeof device.body?.device_code === "string" ? device.body.device_code : ""; const deviceToken = deviceCode.length === 0 ? { status: 0, body: {} } : await providerJson(`${provider.issuer}token`, provider.caFile, { method: "POST", body: new URLSearchParams({ grant_type: "urn:ietf:params:oauth:grant-type:device_code", client_id: FIXTURE_CLIENT_ID, client_secret: credentials.clientSecret, device_code: deviceCode, }).toString(), }); const groups = await providerJson(`${provider.baseUrl}/api/v3/core/groups/?name=fixture-users`, provider.caFile, { headers: { authorization: `Bearer ${credentials.apiToken}` }, }); return { discovery: discovery.status === 200 && discovery.body?.issuer === provider.issuer, jwks: jwks.status === 200 && jwks.body?.keys?.[0]?.alg === "RS256" && jwks.body?.keys?.[0]?.use === "sig", deviceAuthorization: device.status === 200 && typeof device.body?.device_code === "string" && typeof device.body?.verification_uri === "string", deviceToken: deviceToken.status === 200 && typeof deviceToken.body?.id_token === "string", groupList: groups.status === 200 && groups.body?.results?.[0]?.name === "fixture-users", runtimeCredential: credentials.runtime, }; }, setOidcIdentity(identity) { provider.setIdentity(identity); }, async useLocalMode() { writeSecure(authConfigFile, JSON.stringify(localConfig())); mode = "local"; await restartBackend(); }, async useOidcMode(identity, variant = "correct") { if (!OIDC_CREDENTIAL_VARIANTS.has(variant)) throw safeError("e2e_oidc_credential_variant_invalid"); provider.setIdentity(identity); writeOidcSecrets(variant); writeSecure(authConfigFile, JSON.stringify(oidcConfig(variant))); mode = "oidc"; await restartBackend(); }, async authDiagnostics() { if (mode !== "oidc") throw safeError("e2e_oidc_mode_required"); return oneShotJson( join(backendRoot, "node_modules", ".bin", "tsx"), ["src/auth/diagnostic-command.ts", "--json"], { cwd: backendRoot, env: backendEnvironment() }, ); }, restartBackend, async close() { await backend?.close(); await frontend.close(); await provider.close(); await qdrant?.close(); rmSync(root, { recursive: true, force: true }); }, }; } catch (error) { await backend?.close(); await frontend.close(); await provider.close(); await qdrant?.close(); rmSync(root, { recursive: true, force: true }); throw error; } }