docs: correct module authorization comments
This commit is contained in:
@@ -17,9 +17,11 @@ function sanitizedDenial(c: Context, status: 403 | 503, code: string): Response
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a Hono middleware that enforces `permissionId`, resolved exclusively from Core's own
|
* Builds a Hono middleware that enforces `permissionId`, resolved exclusively from the injected,
|
||||||
* trusted, server-side permission registry — a route never supplies its own baseline, and no
|
* trusted, server-side `PermissionRegistry` — in production, the complete platform registry
|
||||||
* request input can influence which baseline is checked. The principal is resolved exclusively
|
* `startApi` composes from Core's own declarations plus every installed module's, never a
|
||||||
|
* Core-only fallback. A route never supplies its own baseline, and no request input can influence
|
||||||
|
* which baseline is checked. The principal is resolved exclusively
|
||||||
* through the injected identity runtime (`resolvePrincipal`), the same one `/api/v1/principal`
|
* through the injected identity runtime (`resolvePrincipal`), the same one `/api/v1/principal`
|
||||||
* already uses; nothing here ever trusts a client-supplied claim about identity, ownership, or
|
* already uses; nothing here ever trusts a client-supplied claim about identity, ownership, or
|
||||||
* grants. A permission id that is not registered fails closed (403) exactly like a denied
|
* grants. A permission id that is not registered fails closed (403) exactly like a denied
|
||||||
|
|||||||
@@ -891,9 +891,11 @@ describe.sequential("live API — platform owner and delegated grant management"
|
|||||||
const ownerEmail = "[email protected]";
|
const ownerEmail = "[email protected]";
|
||||||
const managerEmail = "[email protected]";
|
const managerEmail = "[email protected]";
|
||||||
const password = "correct horse battery staple";
|
const password = "correct horse battery staple";
|
||||||
// Module permission registration does not exist yet, so `core.authorization.grants.manage` —
|
// This scenario intentionally proves out the explicit-grant lifecycle (create, list, use,
|
||||||
// the one permission `CORE_PERMISSION_REGISTRY` actually declares — stands in for "some
|
// revoke) against Core's own grant-management permission rather than a module's — a module
|
||||||
// registered permission" throughout this scenario's explicit-grant lifecycle.
|
// permission would exercise the exact same route and evaluator code, adding coverage breadth
|
||||||
|
// without coverage depth. The "live API — installed module permission end-to-end" scenario
|
||||||
|
// below is where a real installed module's declared permission is proven end-to-end instead.
|
||||||
const testPermission = "core.authorization.grants.manage";
|
const testPermission = "core.authorization.grants.manage";
|
||||||
// Well-formed (passes `PermissionId` shape validation) but never registered — used to prove the
|
// Well-formed (passes `PermissionId` shape validation) but never registered — used to prove the
|
||||||
// live route rejects it even for a caller who otherwise has full grant-management authority.
|
// live route rejects it even for a caller who otherwise has full grant-management authority.
|
||||||
|
|||||||
@@ -25,8 +25,13 @@ const subjectUserId = parseEkklesiaUserId("subject_1");
|
|||||||
// (or, for listing, still return) an unregistered permission id rather than assuming registration.
|
// (or, for listing, still return) an unregistered permission id rather than assuming registration.
|
||||||
const permission = parsePermissionId("calendar.events.read");
|
const permission = parsePermissionId("calendar.events.read");
|
||||||
// A permission `CORE_PERMISSION_REGISTRY` actually declares, so tests that must reach
|
// A permission `CORE_PERMISSION_REGISTRY` actually declares, so tests that must reach
|
||||||
// `store.createGrant`/`store.revokeGrant` can do so without also depending on module permission
|
// `store.createGrant`/`store.revokeGrant` can do so. This suite exercises `createGrantsRoutes` in
|
||||||
// registration, which does not exist yet.
|
// isolation and intentionally injects `CORE_PERMISSION_REGISTRY` directly rather than a composed
|
||||||
|
// platform registry — the routes have no notion of "Core" versus "module" permissions, they only
|
||||||
|
// ever consult whichever registry they are given. Coverage proving a module-declared permission
|
||||||
|
// works through the exact same route code lives in
|
||||||
|
// `apps/api/tests/authorization-middleware.test.ts` (unit) and the "installed module permission
|
||||||
|
// end-to-end" scenario in `apps/api/tests/api-http.integration.ts` (live).
|
||||||
const registeredPermission = CORE_AUTHORIZATION_GRANTS_MANAGE;
|
const registeredPermission = CORE_AUTHORIZATION_GRANTS_MANAGE;
|
||||||
const grantId = parseGrantId("11111111-1111-1111-1111-111111111111");
|
const grantId = parseGrantId("11111111-1111-1111-1111-111111111111");
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,10 @@ export class PermissionRegistryError extends Error {
|
|||||||
* Builds an immutable permission registry from a fixed list of declarations, rejecting any
|
* Builds an immutable permission registry from a fixed list of declarations, rejecting any
|
||||||
* duplicate permission id outright at composition time. Deliberately the smallest mechanism that
|
* duplicate permission id outright at composition time. Deliberately the smallest mechanism that
|
||||||
* distinguishes "registered" from "well-formed": no module discovery, no dynamic registration, no
|
* distinguishes "registered" from "well-formed": no module discovery, no dynamic registration, no
|
||||||
* network or filesystem I/O — a future module registration mechanism can merge its own
|
* network or filesystem I/O. The module integration layer's composer
|
||||||
* declarations into a combined list and still call this same constructor, inheriting duplicate
|
* (`composePlatformPermissionRegistry`, `core/src/modules/permissions.ts`) merges Core's own
|
||||||
* detection for free instead of reimplementing it.
|
* declarations with every discovered module's into one combined list and calls this same
|
||||||
|
* constructor, inheriting duplicate detection for free rather than reimplementing it.
|
||||||
*/
|
*/
|
||||||
export function createPermissionRegistry(
|
export function createPermissionRegistry(
|
||||||
declarations: readonly PermissionDeclaration[],
|
declarations: readonly PermissionDeclaration[],
|
||||||
@@ -98,11 +99,12 @@ export const CORE_PERMISSION_DECLARATIONS: readonly PermissionDeclaration[] = Ob
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core's own permission registry — the trusted source of truth `apps/api`'s authorization
|
* Core's own permission registry, containing exactly the Core-owned permissions declared above.
|
||||||
* enforcement consults so an HTTP route can never supply its own baseline. Contains exactly the
|
* Production `apps/api` does not consult this constant directly: it enforces requests against the
|
||||||
* one Core-owned permission this slice needs; a future module registration mechanism is
|
* composed platform registry (`composePlatformPermissionRegistry`,
|
||||||
* deliberately out of scope here and will compose its own declarations through
|
* `core/src/modules/permissions.ts`), which merges these same declarations with every installed
|
||||||
* `createPermissionRegistry` rather than through this constant.
|
* module's. `CORE_PERMISSION_REGISTRY` remains for Core-only callers and tests that need Core's
|
||||||
|
* permissions in isolation, without composing in any module.
|
||||||
*/
|
*/
|
||||||
export const CORE_PERMISSION_REGISTRY: PermissionRegistry = createPermissionRegistry(
|
export const CORE_PERMISSION_REGISTRY: PermissionRegistry = createPermissionRegistry(
|
||||||
CORE_PERMISSION_DECLARATIONS,
|
CORE_PERMISSION_DECLARATIONS,
|
||||||
|
|||||||
Reference in New Issue
Block a user