feat: RBAC middleware and role-based route guards (Phase 1) #89

Merged
groombook-engineer[bot] merged 2 commits from feat/rbac-middleware-gro-103 into main 2026-03-21 19:31:18 +00:00

2 Commits

Author SHA1 Message Date
Scrubs McBarkley 543c13f182 fix: correct TypeScript types in rbac.test.ts
Use StaffRow type for all staff fixture objects so groomer/receptionist
variants don't cause type errors. Simplify buildApp/buildWithStaff helper
signatures to MiddlewareHandler<AppEnv> / Context<AppEnv> — no more
Parameters<...> inference gymnastics.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-21 18:52:10 +00:00
Scrubs McBarkley 93a9ae4461 feat: add RBAC middleware with role-based route guards (GRO-103)
- New `apps/api/src/middleware/rbac.ts` with `resolveStaffMiddleware`
  (resolves staff from DB by OIDC sub, supports AUTH_DISABLED dev mode)
  and `requireRole(...roles)` factory for per-route role enforcement
- Wire `resolveStaffMiddleware` after `authMiddleware` on api basePath
- Route guards per permission matrix:
  - Manager only: /staff/*, /admin/*, /reports/*, /invoices/*, /impersonation/*
  - Manager + Receptionist only: /appointment-groups/*, /grooming-logs/*
  - Groomers read-only on /clients/*, /pets/*, /appointments/* (write requires manager/receptionist)
  - Services: all roles read, manager-only write
- Refactor impersonation router to use AppEnv and c.get("staff") instead
  of inline staff resolution; role check delegated to requireRole middleware
- Unit tests in rbac.test.ts covering resolveStaffMiddleware and requireRole
- Update impersonation.test.ts to inject staff directly via context

Closes #88 (Phase 1)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-21 15:50:45 +00:00