feat(authz): add defineAuthz declaration registry
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import type { AuthzModule } from "./types.ts";
|
||||
|
||||
const PERMISSION_ID = /^[a-z0-9]+(?::[a-z0-9-]+)+$/;
|
||||
|
||||
/**
|
||||
* Validate and freeze one authorization declaration. Called from
|
||||
* `app/authz/<name>.ts` as the module's default export.
|
||||
*/
|
||||
export function defineAuthz(module: AuthzModule): AuthzModule {
|
||||
const permissions = module.permissions ?? {};
|
||||
const roles = module.roles ?? {};
|
||||
const policies = module.policies ?? {};
|
||||
const attributes = module.attributes ?? {};
|
||||
const bindings = module.bindings ?? {};
|
||||
|
||||
for (const id of Object.keys(permissions)) {
|
||||
if (id.includes("*")) {
|
||||
throw new Error(
|
||||
`WRN-AUTHZ-DECL: permission id '${id}' must not contain a wildcard; wildcards belong in roles.`,
|
||||
);
|
||||
}
|
||||
if (!PERMISSION_ID.test(id)) {
|
||||
throw new Error(
|
||||
`WRN-AUTHZ-DECL: permission id '${id}' must be lowercase colon-namespaced, e.g. 'post:read'.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [role, grants] of Object.entries(roles)) {
|
||||
for (const grant of grants) {
|
||||
if (typeof grant !== "string" || !grant.trim()) {
|
||||
throw new Error(
|
||||
`WRN-AUTHZ-DECL: role '${role}' grants an empty entry; expected a permission, 'ns:*', or 'role:<name>'.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [permission, names] of Object.entries(bindings)) {
|
||||
for (const name of names) {
|
||||
if (!(name in policies)) {
|
||||
throw new Error(
|
||||
`WRN-AUTHZ-DECL: binding for '${permission}' names policy '${name}', which is not declared in the same module.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Object.freeze({ permissions, roles, policies, attributes, bindings });
|
||||
}
|
||||
Reference in New Issue
Block a user