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) #

stable0.4.9

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) #

stable0.4.9hipaapci-dssgdprsoc2
{
  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.