feat: add authorization bootstrap and HTTP enforcement

Wires Core's identity and authorization domains into apps/api over one
shared, validated PostgreSQL pool; adds an explicit, single-use,
concurrency-safe platform-owner bootstrap CLI; adds a smallest-possible
immutable permission registry with one Core-owned restricted permission
(core.authorization.grants.manage); and adds reusable Hono authorization
enforcement plus a narrowly scoped /api/v1/grants HTTP API for managing
explicit grants only, gated by an exact trusted Origin on every mutation.
This commit is contained in:
ekklesia
2026-07-23 11:16:32 -05:00
parent 72e082ff2f
commit 1363608552
33 changed files with 2962 additions and 63 deletions
@@ -0,0 +1,364 @@
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 { parseEkklesiaUserId } from "@ekklesia/contracts";
import { Client, type QueryResultRow } from "pg";
import { afterAll, describe, expect, it } from "vitest";
import {
bootstrapPlatformOwner,
createAuthorizationDrizzleDatabase,
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<string>();
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<Client> {
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<Row extends QueryResultRow>(
client: Client,
text: string,
values: unknown[] = [],
): Promise<readonly Row[]> {
try {
const result = await client.query<Row>(text, values);
return result.rows;
} catch {
throw harnessError("An integration database administration operation failed.");
}
}
async function safeEnd(client: Client): Promise<void> {
try {
await client.end();
} catch {
throw harnessError("An integration database connection could not be closed.");
}
}
async function createDatabase(databaseName: string): Promise<void> {
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<void> {
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<void> {
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<void> {
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<number> {
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<typeof createAuthorizationDrizzleDatabase>["db"];
let close: () => Promise<void>;
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, "[email protected]");
await seedUser(admin, secondUserId, "[email protected]");
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 historical row alone is enough to have consumed it", 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, "[email protected]");
await seedUser(admin, raceUserB, "[email protected]");
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);
}
});
});