import { randomBytes } from "node:crypto"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { createUserActor, parseEkklesiaUserId, parsePermissionId } from "@ekklesia/contracts"; import { Client, type QueryResultRow } from "pg"; import { afterAll, describe, expect, it } from "vitest"; import { bootstrapPlatformOwner, createAuthorizationDrizzleDatabase, createAuthorizationStore, PlatformOwnerBootstrapError, } from "../src/authorization/index.js"; import { applyModuleMigrations, createPostgresMigrationDatabase, discoverAllMigrations, } from "../src/migrations/index.js"; const integrationDatabaseUrlEnvironment = "EKKLESIA_MIGRATION_TEST_DATABASE_URL"; const coreMigrationsDirectory = path.join( path.dirname(fileURLToPath(import.meta.url)), "..", "drizzle", ); const trackedDatabases = new Set(); let databaseCounter = 0; class IntegrationHarnessError extends Error { constructor(message: string) { super(message); this.name = "IntegrationHarnessError"; this.stack = `${this.name}: ${this.message}`; } } function harnessError(message: string): IntegrationHarnessError { return new IntegrationHarnessError(message); } function baseDatabaseUrl(): string { const value = process.env[integrationDatabaseUrlEnvironment]; if (typeof value !== "string" || value.trim() === "") { throw harnessError("The bootstrap integration database is not configured."); } try { const parsed = new URL(value); if (parsed.protocol !== "postgres:" && parsed.protocol !== "postgresql:") { throw new TypeError("Unsupported protocol."); } } catch { throw harnessError("The bootstrap integration database configuration is invalid."); } return value; } function quoteDatabaseName(databaseName: string): string { if (!/^[a-z][a-z0-9_]{0,62}$/u.test(databaseName)) { throw harnessError("The generated integration database name is invalid."); } return `"${databaseName}"`; } function makeDatabaseName(label: string): string { const safeLabel = label .toLowerCase() .replaceAll(/[^a-z0-9]+/gu, "_") .replaceAll(/^_+|_+$/gu, "") .slice(0, 14); databaseCounter += 1; return `ekk_bootstrap_${process.pid}_${databaseCounter}_${safeLabel || "scenario"}_${randomBytes(4).toString("hex")}`; } function databaseUrl(databaseName: string): string { const parsed = new URL(baseDatabaseUrl()); parsed.pathname = `/${databaseName}`; parsed.searchParams.delete("application_name"); return parsed.toString(); } async function openAdministrativeClient(connectionString: string): Promise { const client = new Client({ connectionString }); client.on("error", () => undefined); try { await client.connect(); return client; } catch { try { await client.end(); } catch { // The public harness failure remains static when connection cleanup also fails. } throw harnessError("The bootstrap integration database connection failed."); } } async function safeQuery( client: Client, text: string, values: unknown[] = [], ): Promise { try { const result = await client.query(text, values); return result.rows; } catch { throw harnessError("An integration database administration operation failed."); } } async function safeEnd(client: Client): Promise { try { await client.end(); } catch { throw harnessError("An integration database connection could not be closed."); } } async function createDatabase(databaseName: string): Promise { const admin = await openAdministrativeClient(baseDatabaseUrl()); try { await safeQuery(admin, `CREATE DATABASE ${quoteDatabaseName(databaseName)} TEMPLATE template0`); } finally { await safeEnd(admin); } } async function dropDatabase(databaseName: string): Promise { const admin = await openAdministrativeClient(baseDatabaseUrl()); try { await safeQuery( admin, "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid <> pg_backend_pid()", [databaseName], ); await safeQuery( admin, `DROP DATABASE IF EXISTS ${quoteDatabaseName(databaseName)} WITH (FORCE)`, ); trackedDatabases.delete(databaseName); } finally { await safeEnd(admin); } } async function createDisposableDatabase(label: string): Promise<{ name: string; url: string }> { const name = makeDatabaseName(label); await createDatabase(name); trackedDatabases.add(name); return { name, url: databaseUrl(name) }; } async function applyCoreMigrations(url: string): Promise { const emptyModulesDirectory = await mkdtemp( path.join(tmpdir(), "ekklesia-bootstrap-no-modules-"), ); try { const aggregate = await discoverAllMigrations({ coreMigrationsDirectory, moduleDiscovery: { moduleSdkVersion: "0.1.0", modulesDirectory: emptyModulesDirectory, platformVersion: "0.1.0", }, }); await applyModuleMigrations({ aggregate, database: createPostgresMigrationDatabase(url) }); } finally { await rm(emptyModulesDirectory, { recursive: true, force: true }); } } async function seedUser(client: Client, id: string, email: string): Promise { await safeQuery( client, `insert into "ekklesia_core_user" (id, name, email, email_verified, created_at, updated_at) values ($1, $2, $3, false, now(), now())`, [id, "Integration Test User", email], ); } async function ownerRowCount(client: Client): Promise { const rows = await safeQuery<{ count: string }>( client, `select count(*)::text as count from "ekklesia_core_platform_owner"`, ); return Number(rows[0]?.count ?? "0"); } afterAll(async () => { for (const databaseName of [...trackedDatabases]) { await dropDatabase(databaseName).catch(() => undefined); } }); describe.sequential("live platform-owner bootstrap", () => { let database: { name: string; url: string }; let admin: Client; let db: ReturnType["db"]; let close: () => Promise; const firstUserId = parseEkklesiaUserId("bootstrap_user_one"); const secondUserId = parseEkklesiaUserId("bootstrap_user_two"); it("bootstraps successfully for an existing user and records exactly one audit entry with the fixed bootstrap actor identity", async () => { database = await createDisposableDatabase("success"); await applyCoreMigrations(database.url); admin = await openAdministrativeClient(database.url); await seedUser(admin, firstUserId, "bootstrap-one@example.test"); await seedUser(admin, secondUserId, "bootstrap-two@example.test"); const handle = createAuthorizationDrizzleDatabase(database.url); db = handle.db; close = handle.close; await bootstrapPlatformOwner(db, { userId: firstUserId }); const owners = await safeQuery<{ user_id: string }>( admin, `select user_id from "ekklesia_core_platform_owner"`, ); expect(owners).toEqual([{ user_id: firstUserId }]); const audit = await safeQuery<{ action: string; actor_id: string | null; actor_type: string; subject_user_id: string; }>( admin, `select action, actor_id, actor_type, subject_user_id from "ekklesia_core_authorization_audit"`, ); expect(audit).toEqual([ { action: "platform_owner.grant", actor_id: "authorization-bootstrap-cli", actor_type: "system", subject_user_id: firstUserId, }, ]); }); it("rejects a second bootstrap attempt outright, even for a different user, and leaves no new row or audit entry", async () => { let captured: unknown; try { await bootstrapPlatformOwner(db, { userId: secondUserId }); } catch (error) { captured = error; } expect(captured).toBeInstanceOf(PlatformOwnerBootstrapError); expect(captured).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED" }); expect(await ownerRowCount(admin)).toBe(1); const audit = await safeQuery(admin, `select id from "ekklesia_core_authorization_audit"`); expect(audit).toHaveLength(1); }); it("still refuses bootstrap after the existing owner is revoked — the durable consumed record, not the owner row, is the authority", async () => { await safeQuery( admin, `update "ekklesia_core_platform_owner" set revoked_at = now(), revoked_by_actor_type = 'system' where user_id = $1`, [firstUserId], ); let captured: unknown; try { await bootstrapPlatformOwner(db, { userId: secondUserId }); } catch (error) { captured = error; } expect(captured).toBeInstanceOf(PlatformOwnerBootstrapError); expect(captured).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED" }); expect(await ownerRowCount(admin)).toBe(1); }); it("shuts down cleanly", async () => { await close(); await safeEnd(admin); await dropDatabase(database.name); }); }); describe.sequential("live platform-owner bootstrap — nonexistent user", () => { it("rolls back entirely for a user that does not exist, leaving no owner row and no audit entry", async () => { const database = await createDisposableDatabase("missing_user"); await applyCoreMigrations(database.url); const { close, db } = createAuthorizationDrizzleDatabase(database.url); try { const missingUserId = parseEkklesiaUserId("bootstrap_user_never_seeded"); let captured: unknown; try { await bootstrapPlatformOwner(db, { userId: missingUserId }); } catch (error) { captured = error; } expect(captured).toBeInstanceOf(PlatformOwnerBootstrapError); expect(captured).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_USER_NOT_FOUND" }); const admin = await openAdministrativeClient(database.url); try { expect(await ownerRowCount(admin)).toBe(0); const audit = await safeQuery(admin, `select id from "ekklesia_core_authorization_audit"`); expect(audit).toHaveLength(0); } finally { await safeEnd(admin); } } finally { await close(); await dropDatabase(database.name); } }); }); describe.sequential("live platform-owner bootstrap — concurrency", () => { it("produces exactly one owner when two bootstrap attempts race for two different users", async () => { const database = await createDisposableDatabase("race"); await applyCoreMigrations(database.url); const admin = await openAdministrativeClient(database.url); const raceUserA = parseEkklesiaUserId("bootstrap_race_user_a"); const raceUserB = parseEkklesiaUserId("bootstrap_race_user_b"); await seedUser(admin, raceUserA, "bootstrap-race-a@example.test"); await seedUser(admin, raceUserB, "bootstrap-race-b@example.test"); const { close, db } = createAuthorizationDrizzleDatabase(database.url); try { const results = await Promise.allSettled([ bootstrapPlatformOwner(db, { userId: raceUserA }), bootstrapPlatformOwner(db, { userId: raceUserB }), ]); const fulfilled = results.filter((result) => result.status === "fulfilled"); const rejected = results.filter((result) => result.status === "rejected"); expect(fulfilled).toHaveLength(1); expect(rejected).toHaveLength(1); expect((rejected[0] as PromiseRejectedResult).reason).toBeInstanceOf( PlatformOwnerBootstrapError, ); expect((rejected[0] as PromiseRejectedResult).reason).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED", }); const owners = await safeQuery<{ user_id: string }>( admin, `select user_id from "ekklesia_core_platform_owner"`, ); expect(owners).toHaveLength(1); expect([raceUserA, raceUserB]).toContain(owners[0]?.user_id); const audit = await safeQuery( admin, `select id from "ekklesia_core_authorization_audit" where action = 'platform_owner.grant'`, ); expect(audit).toHaveLength(1); } finally { await close(); await safeEnd(admin); await dropDatabase(database.name); } }); }); /** * Pass 3: bootstrap's single-use authority and authorization security history must both survive * user deletion, which is exactly what the pre-Pass-3 cascading foreign keys destroyed. * * The administrative client here is **test-only provisioning and inspection infrastructure**: it * seeds users, performs the user deletion under test, and reads back the resulting state. It is * never an API pool or a runtime connection, and every client and disposable database it opens is * closed or dropped in `finally`. */ describe.sequential("live platform-owner bootstrap — durability across user deletion", () => { async function consumedCount(client: Client): Promise { const rows = await safeQuery<{ count: string }>( client, `select count(*)::text as count from "ekklesia_core_platform_owner_bootstrap"`, ); return Number(rows[0]?.count ?? "0"); } it("records exactly one consumed row, one owner row, and one audit row in one transaction", async () => { const database = await createDisposableDatabase("durable"); await applyCoreMigrations(database.url); const admin = await openAdministrativeClient(database.url); const { close, db } = createAuthorizationDrizzleDatabase(database.url); try { const ownerId = parseEkklesiaUserId("durable_owner"); const laterId = parseEkklesiaUserId("durable_later"); await seedUser(admin, ownerId, "durable-owner@example.test"); await seedUser(admin, laterId, "durable-later@example.test"); expect(await consumedCount(admin)).toBe(0); await bootstrapPlatformOwner(db, { userId: ownerId }); const consumed = await safeQuery<{ consumed_for_user_id: string | null; id: string }>( admin, `select id, consumed_for_user_id from "ekklesia_core_platform_owner_bootstrap"`, ); expect(consumed).toHaveLength(1); expect(consumed[0]?.id).toBe("singleton"); expect(consumed[0]?.consumed_for_user_id).toBe(ownerId); expect(await ownerRowCount(admin)).toBe(1); expect( await safeQuery( admin, `select id from "ekklesia_core_authorization_audit" where action = 'platform_owner.grant'`, ), ).toHaveLength(1); // Deleting the bootstrapped owner's user row removes the *live* owner row, exactly as // before — but no longer the consumed sentinel or the security history describing it. await safeQuery(admin, `delete from "ekklesia_core_user" where id = $1`, [ownerId]); expect(await ownerRowCount(admin)).toBe(0); expect(await consumedCount(admin)).toBe(1); const survivingAudit = await safeQuery<{ action: string; subject_user_id: string }>( admin, `select action, subject_user_id from "ekklesia_core_authorization_audit"`, ); expect(survivingAudit).toHaveLength(1); expect(survivingAudit[0]).toMatchObject({ action: "platform_owner.grant", subject_user_id: ownerId, }); // And bootstrap is still consumed — the operation that establishes a platform's first // administrator cannot be reopened by deleting a user. let captured: unknown; try { await bootstrapPlatformOwner(db, { userId: laterId }); } catch (error) { captured = error; } expect(captured).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED" }); expect(await ownerRowCount(admin)).toBe(0); } finally { await close(); await safeEnd(admin); await dropDatabase(database.name); } }); it("retains a grant subject's create and revoke audit rows after that user is deleted", async () => { const database = await createDisposableDatabase("audit_survives"); await applyCoreMigrations(database.url); const admin = await openAdministrativeClient(database.url); const { close, db } = createAuthorizationDrizzleDatabase(database.url); try { const subjectId = parseEkklesiaUserId("audit_subject"); const managerId = parseEkklesiaUserId("audit_manager"); await seedUser(admin, subjectId, "audit-subject@example.test"); await seedUser(admin, managerId, "audit-manager@example.test"); const store = createAuthorizationStore(db); const grant = await store.createGrant({ actor: createUserActor(managerId), permission: parsePermissionId("calendar.events.read"), sourceKind: "explicit", userId: subjectId, }); await store.revokeGrant({ actor: createUserActor(managerId), grantId: grant.id }); const before = await safeQuery<{ action: string }>( admin, `select action from "ekklesia_core_authorization_audit" where subject_user_id = $1 order by created_at`, [subjectId], ); expect(before.map((row) => row.action)).toEqual(["grant.create", "grant.revoke"]); await safeQuery(admin, `delete from "ekklesia_core_user" where id = $1`, [subjectId]); // The live grant row still cascades with the user, as intended — the security *history* // does not. expect( await safeQuery(admin, `select id from "ekklesia_core_grant" where user_id = $1`, [ subjectId, ]), ).toHaveLength(0); const after = await safeQuery<{ action: string; subject_user_id: string }>( admin, `select action, subject_user_id from "ekklesia_core_authorization_audit" where subject_user_id = $1 order by created_at`, [subjectId], ); expect(after.map((row) => row.action)).toEqual(["grant.create", "grant.revoke"]); // The retained subject is a plain identifier snapshot; no profile data was ever copied here. expect(Object.keys(after[0] as object).sort()).toEqual(["action", "subject_user_id"]); } finally { await close(); await safeEnd(admin); await dropDatabase(database.name); } }); it("does not consume bootstrap when the transaction fails after the sentinel was claimed", async () => { const database = await createDisposableDatabase("failed_claim"); await applyCoreMigrations(database.url); const admin = await openAdministrativeClient(database.url); const { close, db } = createAuthorizationDrizzleDatabase(database.url); try { const ownerId = parseEkklesiaUserId("failed_claim_owner"); await seedUser(admin, ownerId, "failed-claim@example.test"); // A temporary trigger makes the audit insert — the last statement of the bootstrap // transaction, after the sentinel and owner row were already written — violate the audit // action CHECK constraint. await safeQuery( admin, `create function ekklesia_test_break_bootstrap() returns trigger as $$ begin new.action := 'not.a.real.action'; return new; end; $$ language plpgsql`, ); await safeQuery( admin, `create trigger ekklesia_test_break_bootstrap_trigger before insert on "ekklesia_core_authorization_audit" for each row execute function ekklesia_test_break_bootstrap()`, ); try { let captured: unknown; try { await bootstrapPlatformOwner(db, { userId: ownerId }); } catch (error) { captured = error; } expect(captured).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_FAILED" }); // The sentinel, the owner row, and the audit row all rolled back together, so the one // legitimate first use is still available. expect(await consumedCount(admin)).toBe(0); expect(await ownerRowCount(admin)).toBe(0); } finally { await safeQuery( admin, `drop trigger if exists ekklesia_test_break_bootstrap_trigger on "ekklesia_core_authorization_audit"`, ); await safeQuery(admin, `drop function if exists ekklesia_test_break_bootstrap()`); } // With the failure removed, bootstrap still works — it was never burned. await bootstrapPlatformOwner(db, { userId: ownerId }); expect(await consumedCount(admin)).toBe(1); expect(await ownerRowCount(admin)).toBe(1); } finally { await close(); await safeEnd(admin); await dropDatabase(database.name); } }); }); /** * Upgrade path: an installation created before this migration must not gain a fresh first use. * These scenarios apply only 0000/0001, plant representative pre-migration state through the * test-only administrative client, then apply 0002 and assert the backfill's effect. */ describe.sequential("live Core migration 0002 — bootstrap-consumed backfill", () => { async function applyThrough(url: string, upTo: number): Promise { const emptyModulesDirectory = await mkdtemp( path.join(tmpdir(), "ekklesia-upgrade-no-modules-"), ); try { const aggregate = await discoverAllMigrations({ coreMigrationsDirectory, moduleDiscovery: { moduleSdkVersion: "0.1.0", modulesDirectory: emptyModulesDirectory, platformVersion: "0.1.0", }, }); const selected = { ...aggregate, migrations: aggregate.migrations.slice(0, upTo), }; await applyModuleMigrations({ aggregate: selected, database: createPostgresMigrationDatabase(url), }); } finally { await rm(emptyModulesDirectory, { recursive: true, force: true }); } } async function consumedRows(client: Client) { return await safeQuery<{ consumed_for_user_id: string | null; id: string }>( client, `select id, consumed_for_user_id from "ekklesia_core_platform_owner_bootstrap"`, ); } it("backfills consumed state for an installation that already has an owner, including a revoked one", async () => { for (const [label, revoke] of [ ["active_owner", false], ["revoked_owner", true], ] as const) { const database = await createDisposableDatabase(label); const admin = await openAdministrativeClient(database.url); try { await applyThrough(database.url, 2); const ownerId = parseEkklesiaUserId(`upgrade_owner_${label}`); const nextId = parseEkklesiaUserId(`upgrade_next_${label}`); await seedUser(admin, ownerId, `upgrade-${label}@example.test`); await seedUser(admin, nextId, `upgrade-next-${label}@example.test`); await safeQuery( admin, `insert into "ekklesia_core_platform_owner" (user_id, granted_by_actor_type, granted_by_actor_id) values ($1, 'system', 'authorization-bootstrap-cli')`, [ownerId], ); await safeQuery( admin, `insert into "ekklesia_core_authorization_audit" (action, actor_type, actor_id, subject_user_id) values ('platform_owner.grant', 'system', 'authorization-bootstrap-cli', $1)`, [ownerId], ); if (revoke) { await safeQuery( admin, `update "ekklesia_core_platform_owner" set revoked_at = now(), revoked_by_actor_type = 'system' where user_id = $1`, [ownerId], ); } await applyThrough(database.url, 3); const consumed = await consumedRows(admin); expect(consumed, label).toHaveLength(1); expect(consumed[0]?.id).toBe("singleton"); // A backfill cannot know which of several historical owner rows the original single use // targeted, so it deliberately records no subject rather than fabricating one. expect(consumed[0]?.consumed_for_user_id).toBeNull(); // Every pre-existing audit row survives the migration untouched. expect( await safeQuery(admin, `select id from "ekklesia_core_authorization_audit"`), ).toHaveLength(1); const { close, db } = createAuthorizationDrizzleDatabase(database.url); try { let captured: unknown; try { await bootstrapPlatformOwner(db, { userId: nextId }); } catch (error) { captured = error; } expect(captured, label).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED", }); } finally { await close(); } } finally { await safeEnd(admin); await dropDatabase(database.name); } } }); it("leaves bootstrap available exactly once for an installation that never had an owner", async () => { const database = await createDisposableDatabase("never_owned"); const admin = await openAdministrativeClient(database.url); try { await applyThrough(database.url, 2); const userId = parseEkklesiaUserId("never_owned_user"); await seedUser(admin, userId, "never-owned@example.test"); await applyThrough(database.url, 3); // A legitimately never-bootstrapped installation must retain its one first use. expect(await consumedRows(admin)).toHaveLength(0); const { close, db } = createAuthorizationDrizzleDatabase(database.url); try { await bootstrapPlatformOwner(db, { userId }); expect(await consumedRows(admin)).toHaveLength(1); let captured: unknown; try { await bootstrapPlatformOwner(db, { userId }); } catch (error) { captured = error; } expect(captured).toMatchObject({ code: "PLATFORM_OWNER_BOOTSTRAP_ALREADY_CONSUMED" }); } finally { await close(); } } finally { await safeEnd(admin); await dropDatabase(database.name); } }); });