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:
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
type AuthorizationDrizzleDatabase,
|
||||
createAuthorizationDrizzleView,
|
||||
} from "@ekklesia/core/authorization";
|
||||
import { createValidatedPostgresPool, type PostgresPoolOptions } from "@ekklesia/core/database";
|
||||
import { createIdentityDrizzleView, type IdentityDrizzleDatabase } from "@ekklesia/core/identity";
|
||||
|
||||
export interface ApiDatabaseHandle {
|
||||
readonly authorization: AuthorizationDrizzleDatabase;
|
||||
readonly identity: IdentityDrizzleDatabase;
|
||||
/** Closes the one shared pool exactly once, regardless of how many typed views were created
|
||||
* over it. Safe to call from both a startup-failure cleanup path and normal shutdown. */
|
||||
readonly close: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the one physical PostgreSQL pool this process uses for both Core's identity and
|
||||
* authorization schemas: one validated, timeout-bounded `pg.Pool` (`createValidatedPostgresPool`,
|
||||
* `@ekklesia/core/database`), wrapped by two independent typed Drizzle views
|
||||
* (`createIdentityDrizzleView`, `createAuthorizationDrizzleView`). Neither Core domain owns or
|
||||
* closes the pool in this composition — `close()` here is the only thing that ever calls
|
||||
* `pool.end()`. `createIdentityDrizzleDatabase`/`createAuthorizationDrizzleDatabase` remain
|
||||
* available, unaffected, and independently testable for callers that only need one domain.
|
||||
*/
|
||||
export function createApiDatabase(
|
||||
databaseUrl: string,
|
||||
options: PostgresPoolOptions = {},
|
||||
): ApiDatabaseHandle {
|
||||
const { close, pool } = createValidatedPostgresPool(databaseUrl, options);
|
||||
|
||||
return Object.freeze({
|
||||
authorization: createAuthorizationDrizzleView(pool),
|
||||
close,
|
||||
identity: createIdentityDrizzleView(pool),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user