ADR-003 · RBAC with global resource.action permissions
Status: Accepted
Context
Schools have varied staff structures — admins, class teachers, finance staff, and delegated sub-admins with narrow remits. Access needs to be configurable per school without shipping code per school, and it must compose with two other axes: tenancy (which school) and plan (which modules the school bought).
Decision
Use role-based access control with globally-defined permissions:
- Permissions are strings shaped
resource.action(student.read,attendance.mark,finance.manage). They are global — seeded once, identical across tenants. - Roles and role→permission assignments are tenant-scoped, so each school customises who can do what:
User → UserRole → Role → RolePermission → Permission. - Enforced server-side with stacked decorators:
@auth_required→@tenant_required→@require_permission('...'). UI checks (hasPermission,<PermissionGate>, tab filters) are cosmetic mirrors. - Default roles (School Admin, Teacher, Student, Finance Staff, Class Teacher) are seeded per tenant and fully editable.
Two deliberate refinements:
- Delete-sensitive modules (students, teachers, finance) grant granular toggles instead of
<resource>.manage, becausehas_permissiontreats.manageas implying.delete. - Branch scoping for sub-admins is layered on top (
core/branch_scope.py), anchored onClass.school_unit_id, and fails closed.
Consequences
Positive
- Per-tenant flexibility with no per-tenant code.
- Global permission strings keep the vocabulary consistent and greppable.
- Composes cleanly with plan gates (
@require_plan_feature) — the two are independent.
Negative / cost
- Permission strings must be governed; ad-hoc strings fragment the model.
- The
.manage⇒.deleteimplication is a sharp edge that must be remembered when granting delegated access (hence granular toggles for sensitive modules). - Branch scoping adds a second enforcement layer that every affected query must respect.
Alternatives considered
- Hard-coded roles — simplest, but can’t express per-school variation.
- Attribute-based access control (ABAC) — more expressive, far more complex than pilot needs justify.