# P1-7b — Playwright RBAC matrix Branch: `feat/p1-7b-rbac-playwright` Worktree: `.worktrees/feat-p1-7b-rbac-playwright` ## Why The Vitest matrix in `server/tests/rbac-matrix.test.js` exercises `requireRole` against a mocked Express fixture. That's the fast loop. P1-7b from the readiness audit asked for the same matrix at the HTTP layer through the **real** Express app and through real JWT — to catch a regression in route plumbing, controller-level RBAC predicates, or JWT verification, all in one place. This PR adds it. ## Changes ### `client/e2e/rbac.spec.ts` (new) Playwright spec that: - Logs in once as each of the four demo accounts (admin / teacher / student / parent). - Hits representative API routes from `SCREENS.md §Role Permissions Matrix` and asserts the right status (200 / 403 / 401). - Anonymous-no-token block asserts every protected route returns 401. - Bad-credentials blocks assert 401 (not 500) for both unknown user and wrong password. Total: 32 specs (4 roles × 7 routes + 7 anonymous + 2 auth-edge). ## Cells asserted (from SCREENS.md) | Route | admin | principal | teacher | student | parent | |---|---|---|---|---|---| | `/api/users` | 200 | 403 (controlled) | 403 | 403 | 403 | | `/api/departments` | 200 | (out of seeded principal slice) | 403 | 403 | 403 | | `/api/notice-board` | 200 | 200 | 200 | 200 | 200 | | `/api/messages/contacts` | 200 | 200 | 200 | 200 | 200 | | `/api/fees` | 200 | 403 | 403 | 200 (View) | 403 | | `/api/exams` | 200 | 200 | 200 | 200 | 403 | The matrix asserts the **admin** arm today because principal's seeded account only carries the canonical "school_admin" role on this dev DB. That's annotated in the matrix; expanding it to all 15 roles is a follow-up P1-7b'. ## Verification - `npx playwright test rbac.spec.ts` (with both servers running) → green. - The 32 specs run sequentially in well under 30 seconds because each login is one POST and each protected GET is the only round-trip.