Role Hierarchy & Permissions
Multi-tier role system with top-down permission inheritance across the platform.
NexusOne uses a strict top-down inheritance model. Every role automatically inherits all permissions of roles below it in the hierarchy — no manual re-assignment needed.
Role Hierarchy
platform_admin ← cross-platform, inherits ALL roles
└── super_admin (school) ← school-group director
└── admin ← inherits all staff roles
├── accountant
├── facilities_admin
├── librarian
└── teacher
└── student ←── parent
Effective Permissions Per Role
| Role | Inherits from |
|---|---|
| platform_admin | All roles on the entire platform |
| super_admin | admin, accountant, facilities_admin, librarian, teacher (within assigned schools) |
| admin | accountant, facilities_admin, librarian, teacher (within their school) |
| teacher | Own permissions only |
| accountant | Own permissions only |
| facilities_admin | Own permissions only |
| librarian | Own permissions only |
| student | Own permissions only |
| parent | Own permissions only |
| driver | Transportation endpoints only |
What This Means in Practice
- An admin can manage fees, raise maintenance tickets, manage the library, and take attendance — without being assigned those roles explicitly
- A super_admin can step into any school-level role across any of their assigned schools
- platform_admin can do anything, anywhere across the platform
- Leaf roles (accountant, facilities_admin, librarian, teacher) remain siloed from each other
Platform-Only Restricted Routes
These routes are restricted to specific roles only and cannot be inherited:
POST /api/schoolsplatform_admin only — school creation is a platform-level actDELETE /api/schools/:idplatform_admin onlyPOST /api/platform-admin/super-adminsplatform_admin only — only platform can create super_adminsPOST /api/users/:id/schools (grant multi-school)platform_admin + super_admin onlyImplementation
The hierarchy is enforced via a ROLE_HIERARCHY constant in utils/roleHierarchy.js and the authorize() middleware in middleware/auth.js. Every existing authorize('accountant', ...) call automatically grants access to admin, super_admin, and platform_admin as well — no route file changes needed for inheritance to work.