Decisions (ADRs)ADR-003 · RBAC permissions

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, because has_permission treats .manage as implying .delete.
  • Branch scoping for sub-admins is layered on top (core/branch_scope.py), anchored on Class.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.delete implication 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.