Permissions
RBAC / ABAC / scope-based access control — declare roles, resolve user permissions, gate routes via middleware. Composes with b.apiKey (scopes flow from req.apiKey) and b.middleware.attachUser (roles flow from req.user). Wildcard scope syntax mirrors the project-wide convention: "users:*" (trailing-greedy), "*:read" (per-segment), "*" (root-greedy).
The middleware factory runs three layers per request: RBAC scope match, optional MFA freshness gate (per-route or per-role), and ABAC predicate evaluation when a policy is registered for the requested scope. Failures emit permissions.check.deny / permissions.mfa.required / permissions.policy.deny audit events with the actor 5 W's so a compliance reviewer can reconstruct exactly which layer refused the request.
b.permissions.match(granted, required) #
Project-wide scope wildcard matcher. Returns true when granted (a scope held by the actor, possibly containing *) covers required (the scope a route demands, always concrete). Trailing * is greedy across remaining segments; mid-string * matches a single segment.
b.permissions.match("users:*", "users:read"); // → true
b.permissions.match("users:*", "users:read:detail"); // → true
b.permissions.match("*:read", "users:read"); // → true
b.permissions.match("users:*:read", "users:42:read"); // → true
b.permissions.match("users:read", "users:write"); // → false
b.permissions.match("users:read", "users:read:audit"); // → false (no implicit sub-resource grant)
b.permissions.create(opts) #
{
roles: object, // role-name → spec (required, ≥1 entry)
resolver: function, // req → actor; default reads req.apiKey / req.user
audit: b.audit, // optional audit sink
auditFailures: boolean, // default true
auditSuccess: boolean, // default true (compliance trail)
denyStatus: number, // default 403
missingActorStatus: number, // default 401
responder: function, // (req, res, status, info) — custom error responder
}
Build a permissions registry from a role table. Returns a handle exposing require(scope, mwOpts?) / requireAll(scopes, mwOpts?) / requireAny(scopes, mwOpts?) middleware factories, check(actor, scope) / checkAll / checkAny synchronous predicates, policy(scope, predicate) for ABAC layering, expand(roleNames) to resolve inherited permissions, and dbRoleFor(reqOrActor) for declarative DB role binding. Role tables are validated at create-time — unknown extends targets, cycles, and bad scope shapes throw before the first request.
var perms = b.permissions.create({
roles: {
admin: { extends: ["editor"], permissions: ["users:delete"] },
editor: ["users:read", "users:write", "posts:*"],
viewer: ["*:read"],
},
audit: b.audit,
});
// Synchronous check (e.g. inside a handler that already has the actor):
perms.check({ roles: ["editor"] }, "posts:write"); // → true
perms.check({ scopes: ["users:read"] }, "users:delete"); // → false
// Middleware on a route — actor is read from req.user / req.apiKey:
router.delete("/users/:id",
attachUser,
perms.require("users:delete", { requireMfa: true, mfaWindowMs: b.constants.TIME.minutes(5) }),
deleteHandler);
// ABAC predicate stacked on top of RBAC:
perms.policy("orders:write", async function (actor, ctx) {
return ctx.order.tenantId === actor.tenantId;
});
Last updated 2026-08-08T16:39:49.652Z by seeder.