RBAC: The Bouncer Your API Deserves
π§ RBAC: The Bouncer Your API Deserves (ELI5 + Fun Edition π)
Think of your API like a hospital with many rooms π₯πͺ
- Everyone with a badge can enter the building (logged in β )
- But not everyone should enter every room (permissions β/β )
Right now, we only check "is this a real badge?" πͺͺ We do not check "can this person open this room?" π
Thatβs the RBAC mission: right person, right room, right action π§ β¨
β‘ TL;DR (One-Screen Version)
Before: User has role_id=2 and can do almost anything π¬
After: User has a role + allowed actions like:
order.deleteποΈpatient.patdiag.updateβοΈresult.viewπ
Every protected endpoint checks that list.
No permission = 403 Forbidden π«
π΅ The Current Situation (Why it hurts)
Current flow:
Client -> JWT -> AuthFilter -> Controller
What we check now:
- token exists β
- token not expired β
What we donβt check:
- what role means π€·
- what user can do π€·
So controllers are mostly "open by default" (yikes) π«
π― What We Need
From your real examples:
- Only Role A can delete
OrderTestποΈ - Only Role B can edit
PatDiagβοΈ - Most other normal actions stay available to authenticated users π
So we need action-level permissions (not just broad role names).
π§± Simple RBAC Model
We add 3 core tables:
-
rolesπ₯
Human names like Admin, Doctor, Technician. -
permissionsπ§Ύ
Small actions likeorder.delete,patient.view. -
role_permissionsπ
The mapping table: which role gets which permission.
And we make sure users.role_id actually points to roles.id.
π·οΈ Permission Naming (boring on purpose = good)
Use:
resource.action
Examples:
order.deletepatient.patdiag.updateresult.approveadmin.manage
Why this format?
- easy to read π
- easy to search π
- easy to debug π οΈ
π οΈ Build Plan (Dumb + Practical + No Drama)
Phase 1: Create RBAC tables ποΈ
- create
roles - create
permissions - create
role_permissions - add FK from
users.role_idtoroles.id - seed starter roles
Phase 2: Put permissions in JWT at login π
On login:
- load user role
- load role permissions
- add permission strings to JWT payload
Then request checks are fast (no DB hit every request) β‘
Phase 3: Add a PermissionFilter πͺ
Reusable filter behavior:
- read required permission from route
- admin bypass (if we keep that rule)
- check if permission exists in token
- if missing -> return 403
Phase 4: Protect routes/controllers π‘οΈ
- simple endpoint? use route filter directly
- mixed endpoint (does many things)? check permission inside controller before sensitive section
Phase 5: Add admin management endpoints ποΈ
Admin can:
- list permissions
- create roles
- assign permissions to roles
Gate this behind admin.manage π
π± Starter Permission Set
order.delete-> Role A onlypatient.patdiag.update-> Role B onlypatient.view-> all authenticatedresult.view-> all authenticatedadmin.manage-> admin only
Grow from here, one permission at a time πΏ
β οΈ Risks (Realistic)
-
Token gets bigger π
Usually fine unless you have huge permission lists. -
Audit takes time β³
Many controllers may need checks; do incremental rollout. -
Mixed endpoints are tricky π§©
Need clear rule: fail whole request vs fail only restricted part.
β Open Questions
- What are your actual role names today?
- Do admins bypass everything, or still need explicit permissions?
- For mixed updates, do we fail all or partially apply?
- Keep
resource.actionnaming, or add module prefix (lab.order.delete)? - Rollout style: full audit now or start with
OrderTest+PatDiagfirst? - If role/permission changes, how do we invalidate old JWTs?
π¦ File Manifest (implementation touchpoints)
app/Database/Migrations/...CreateRbacTables.php(new)app/Database/Seeds/RbacSeeder.php(new)app/Filters/PermissionFilter.php(new)app/Traits/PermissionTrait.php(new)app/Config/Filters.php(edit)app/Controllers/AuthController.php(edit)app/Config/Routes/08-order.php(edit)app/Config/Routes/01-patient.php(edit)app/Controllers/PatVisit/PatVisitController.php(edit)app/Controllers/Admin/PermissionController.php(new)app/Config/Routes/14-admin.php(new)
π Final ELI5
RBAC is just this:
"Being logged in means you entered the building.
Permissions decide which doors you can open." π
Thatβs it. Keep doors explicit, checks consistent, and 403s honest. ππ₯