feat(gro-48): row-level data scoping for groomer role (RBAC Phase 2) #125

Closed
groombook-engineer[bot] wants to merge 3 commits from fleaflicker/gro48-rbac-row-level-clean into main

3 Commits

Author SHA1 Message Date
Flea Flicker 014f1abc8c fix(gro-48): add missing icalToken to schema and test fixtures
StaffRow now requires icalToken (from iCal calendar feed feature).
Adding icalToken: null to MANAGER mock in groomerIsolation.test.ts,
petPhotos.test.ts, rbac.test.ts, and buildStaff factory. Also adding
icalToken column to staff table schema to match origin/main.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-27 12:54:09 +00:00
Flea Flicker 252832d081 fix(gro-48): use staffRow?.role for consistency with clients.ts/pets.ts
QAP-125 noted appointments.ts uses staffRow.role (unsafe) while
clients.ts/pets.ts use staffRow?.role (safe). This inconsistency
can cause runtime errors when staff context is missing. Fixed by
using the safe optional-chain form consistently in both GET handlers.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-27 09:42:26 +00:00
Flea Flicker 3c21807b3d feat(gro-48): row-level data scoping for groomer role (RBAC Phase 2)
Filter query results at the route handler level when staff role is groomer:

- GET /api/appointments: WHERE staffId = groomer OR batherStaffId = groomer
- GET /api/appointments/🆔 403 if not assigned to groomer (as staff or bather)
- GET /api/clients: Clients with ≥1 appointment for this groomer (via exists subquery)
- GET /api/clients/🆔 403 if no appointment linkage
- GET /api/pets: Pets owned by groomer-linked clients (via exists subquery)
- GET /api/pets/:petId: 403 if no appointment linkage

Managers and receptionists: no change.

Added exists to @groombook/db exports (was missing from re-export).
Added groomerIsolation unit tests for role guard and filter logic.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-27 06:49:27 +00:00