Roles and permissions Required
Check permissions instead of role names
Make code ask whether a user has a named permission, such as invoice.refund. Map roles to permissions in one place, and never compare role names in feature code.
How to do it
Write one list of permissions, each an action on a kind of record. Write one map from each role to its permissions, in code or in a table. Feature code calls one function, such as can(membership, 'invoice.refund'). A new or changed role is then one edit. An unknown role or permission gets no, as deny by default says. The interface may use the same function to hide buttons, but the server check is what counts.
Example
const ROLE_PERMISSIONS = {
owner: ['invoice.read', 'invoice.create', 'invoice.refund', 'member.manage', 'billing.manage'],
admin: ['invoice.read', 'invoice.create', 'invoice.refund', 'member.manage'],
member: ['invoice.read', 'invoice.create'],
viewer: ['invoice.read'],
};
export const can = (membership, permission) =>
ROLE_PERMISSIONS[membership?.role]?.includes(permission) ?? false;
ROLE_PERMISSIONS = {
"owner": {"invoice.read", "invoice.create", "invoice.refund", "member.manage", "billing.manage"},
"admin": {"invoice.read", "invoice.create", "invoice.refund", "member.manage"},
"member": {"invoice.read", "invoice.create"},
"viewer": {"invoice.read"},
}
def can(membership, permission: str) -> bool:
return membership is not None and permission in ROLE_PERMISSIONS.get(membership.role, set())
How to check
Search the code for role comparisons, such as role === 'admin' or role == "admin". Matches appear only in the policy module. A test confirms can answers no for an unknown role.
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/permissions-not-roles.