Roles and permissions Recommended
Start with roles and add record rules only when needed
Start with a few roles per organisation. Add conditions on the record only for rules that need them, such as "only the author may edit a draft".
How to do it
Role-based access control (RBAC) gives each member one role. Four roles cover most products, such as owner, admin, member and viewer. Attribute-based access control (ABAC) adds conditions on the user, the record or the time. Relationship-based control, as in Google's Zanzibar paper, follows links such as "member of this folder's team". Keep every condition inside the policy module, never in route handlers. Move to a policy engine, such as OPA, Cedar, Casbin or OpenFGA, when the rules outgrow one file.
Example
const RECORD_RULES = {
'draft.edit': (user, membership, draft) => draft.authorId === user.id || can(membership, 'draft.edit_any'),
};
export const canOnRecord = (user, membership, permission, record) =>
can(membership, permission) && (RECORD_RULES[permission]?.(user, membership, record) ?? true);
RECORD_RULES = {
"draft.edit": lambda user, membership, draft: draft.author_id == user.id or can(membership, "draft.edit_any"),
}
def can_on_record(user, membership, permission: str, record) -> bool:
rule = RECORD_RULES.get(permission)
return can(membership, permission) and (rule is None or rule(user, membership, record))
How to check
Each organisation has at most 6 roles, or a written reason for more. A search finds conditions on record fields only inside the policy module.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers accounts playbook: https://agentwranglers.se/playbook/accounts/roles-and-permissions/rbac-then-abac.