Skip to content

Roles & Inheritance

A role models who is acting (a job/title). You define a role the first time you grant() or deny() on it — or declare it up front with setup().

import { AccessControl } from 'accesscontrol';
const ac = new AccessControl();
ac.grant('user')
.createOwn('video')
.readAny('video')
.grant('admin') // switch role, keep the chain
.extend('user') // inherit user's grants
.deleteAny('video');

A role inherits others with .extend(). Grants are additive: the child gets its own grants plus everything its parents grant.

ac.grant('user').readAny('post', ['*']);
ac.grant('moderator').extend('user');
ac.can('moderator').readAny('post').granted; // true (inherited)

You can extend multiple roles, and chains are flattened:

ac.grant('support').readAny('ticket', ['*']);
ac.grant('agent').extend(['user', 'support']);
ac.grant('lead').extend('agent'); // lead → agent → user, support

An explicit deny always wins — even over an inherited grant. This lets a child role carve a field (or action) back out:

ac.grant('user').readAny('post', ['*']);
ac.grant('moderator').extend('user');
ac.deny('moderator').readAny('post', ['secret']); // carve a field back
ac.can('moderator').readAny('post').attributes; // ['*', '!secret']
ac.hasRole('admin'); // boolean (single or array)
ac.getRoles(); // ['user', 'admin']
ac.getInheritedRolesOf('admin'); // ['user']
ac.removeRoles('admin'); // also strips it from other roles' $extend

Pass an array to check “at least one of these roles allows it”. Attributes are the union across the roles:

ac.can(['user', 'support']).readAny('ticket').granted;