# Route, RBAC, and Security Matrix

This matrix is a test design aid. Claude should verify each route in code and expand this into exhaustive cases.

## Global Rules

- Active routes are in `public/index.php`.
- `App::handle()` rejects all non-API POST requests without valid `_csrf`.
- API routes under `/api/` bypass central CSRF and must be tested separately.
- Controllers should enforce permissions with `SessionAuth::requirePermission()`.
- 403 behavior should not leak sensitive detail.
- Route inventory should remain at 0 dead links/buttons.

## Public and Demo Routes

| Route | Expected behavior | Tests |
| --- | --- | --- |
| `GET /` | Public landing | 200, no auth, no PHP warnings |
| `GET /pricing` | Public pricing | Currency/discount rendering |
| `GET /privacy`, `/terms`, `/support` | App-store/public readiness pages | 200, footer links valid |
| `GET /account/delete-request` | Public deletion request form | CSRF present |
| `POST /account/delete-request` | Stores request stub | CSRF, invalid email, accepted session stub |
| `GET /auth` | Login/signup UI | Forms visible |
| `POST /login` | Login action | Valid/invalid credentials |
| `POST /signup` | Signup action | Creates owner session |
| `GET /demo`, `/demo/company-admin`, `/demo/platform-admin`, `/demo/role` | Demo entry/switching | Local only where applicable; production disabled behavior |

## Tenant Operations Routes

| Route group | Permission | Allowed roles | Denied roles | Key tests |
| --- | --- | --- | --- | --- |
| `/dashboard` | `view_dashboard` | owner, manager, supervisor, team_member, trainee, demo_user | unauthenticated in production-like mode | Dashboard stats and nav |
| `/jobs`, `/jobs/view` | `view_jobs` | owner, manager, supervisor, team_member, trainee, demo_user | platform_admin unless support session | Assignment scope for team_member |
| `/jobs/create`, `/jobs/new`, `POST /jobs` | `create_jobs` | owner, manager, supervisor | team_member, trainee, demo_user, platform_admin | Validation, CSRF |
| `/jobs/edit`, `POST /jobs/update` | `update_jobs` | owner, manager, supervisor, assigned team_member | trainee, demo_user, platform_admin | Assigned-job scope |
| `/recurring-jobs` and posts | likely job/tenant operations permissions | owner/manager/supervisor per implementation | restricted roles | Create, generate, pause/resume |
| `/dispatch` and posts | `manage_dispatch` | owner, manager, supervisor | team_member, trainee, demo_user, platform_admin | Crew and assignment conflicts |
| `/schedule`, `POST /schedule/appointments` | `view_schedule` plus booking permissions per controller | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | Calendar slot click, booking, conflict |
| `/quotes`, `/quotes/view` | `view_quotes` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | Drill-in |
| `/quotes/create`, `/quotes/new`, `POST /quotes` | `create_quotes` | owner, manager, supervisor | team_member, trainee, demo_user, platform_admin | Create validation |
| `/quotes/edit`, `POST /quotes/update` | `update_quotes` | owner, manager | supervisor, team_member, trainee, demo_user, platform_admin | Draft-only editing |
| `/quotes/convert-to-invoice` | quote/invoice create/update permissions | owner, manager | restricted roles | Conversion correctness |
| `/invoices`, `/invoices/view` | `view_invoices` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | Drill-in |
| `/invoices/create`, `/invoices/new`, `POST /invoices` | `create_invoices` | owner, manager | supervisor, team_member, trainee, demo_user, platform_admin | Create validation |
| `/invoices/edit`, `/invoices/update` | `update_invoices` | owner, manager | supervisor, team_member, trainee, demo_user, platform_admin | Unpaid/partial only |
| `/invoices/payment` | invoice update/payment capability | owner, manager | restricted roles | Partial/full payments |
| `/clients`, `/clients/view` | `view_clients` | owner, manager, supervisor, team_member, trainee, demo_user | platform_admin | Drill-in |
| `/clients/create`, `POST /clients` | `create_clients` | owner, manager, supervisor | team_member, trainee, demo_user, platform_admin | Validation |
| `/clients/edit`, `/clients/update` | `update_clients` | owner, manager, supervisor | team_member, trainee, demo_user, platform_admin | Save edits |
| `/team`, `/team/view` | `view_team` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Drill-in |
| `/team/create`, `/team/update` | `manage_team` | owner, manager | supervisor, team_member, trainee, demo_user, platform_admin | Role validation |
| `/reports` | `view_reports` | owner, manager, supervisor, demo_user, platform_admin | team_member, trainee | Counts |
| `/settings` | `view_settings` | owner, manager, demo_user, platform_admin | supervisor, team_member, trainee | Logout form |

## AI, Import, Export, Integrations

| Route | Permission | Critical security tests |
| --- | --- | --- |
| `GET /ai-assistant` | `use_ai_assistant` | Trainee denied, platform_admin denied unless permission changes |
| `POST /ai-assistant/generate` | `use_ai_assistant` plus CSRF | Invalid mode 422, redaction, no mutation, no external call |
| `GET /import-data`, `/import` | `import_csv` | Restricted roles denied |
| `POST /import-data/preview` | `import_csv` plus CSRF | Header validation, file/text validation |
| `POST /import-data/commit` | `import_csv` plus CSRF | Must require valid preview |
| `POST /import-data/undo` | `import_csv` plus CSRF | Must scope to last import |
| `GET /export-data`, `/export` | `export_csv` | Basic live export blocked |
| `GET /export-data/download` | `export_csv` | CSV formula safety, tenant scope |
| `GET /settings/accounting` | `view_accounting_integrations` | Provider data visible only to allowed roles |
| `GET /settings/calendar` | `view_calendar_integrations` | Allowed roles only |
| `GET /settings/llm` | `view_llm_settings` | Secrets not displayed |

## SaaS and Platform Admin Routes

| Route | Required permission | Key tests |
| --- | --- | --- |
| `GET /workspace` | tenant workspace permission | Feature gating |
| `GET /subscription` | tenant subscription/billing permission | Access state |
| `GET /settings/pricing` | `manage_platform_pricing` | Platform admin only |
| `POST /settings/pricing` | `manage_platform_pricing` plus CSRF | Currency, discount, trial validation |
| `POST /settings/pricing/reset` | `manage_platform_pricing` plus CSRF | Reset confirmation/result |
| `GET /platform-admin` | `view_platform_admin` | Tenant roles denied |
| `GET /platform-admin/tenants` | platform admin | Tenant table |
| `GET /platform-admin/tenants/view` | platform admin | Tabs and tenant context |
| `POST /platform-admin/tenants/action` | platform admin plus CSRF | Confirmation and audit |
| `POST /platform-admin/tenants/login` | `impersonate_tenants` plus CSRF | Reason required, time limit, audit |
| `GET/POST /platform-admin/tenants/return` | support session/platform admin | Restore admin |
| `POST /platform-admin/billing/override` | `manage_platform_billing` plus CSRF | Amount/reason validation |
| `POST /platform-admin/accounting/connect` | platform admin plus CSRF | Sandbox provider validation |
| `POST /platform-admin/demo/reset` | platform admin plus CSRF | Explicit only, audit |
| `GET /platform-admin/health` | platform admin | Status cards |
| `GET /platform-admin/security` | platform admin | Security checklist |
| `GET /platform-admin/mobile-offline` | platform admin | Placeholder status |

## API Routes

| Route | CSRF | Auth model | Tests |
| --- | --- | --- | --- |
| `POST /api/portal/quote` | Central CSRF bypassed | Signed token payload | Valid approve/decline, invalid token, expired token, tampering |
| `POST /api/offline-sync` | Central CSRF bypassed | Offline payload/user context per controller | Assigned job accepted, unassigned job rejected, invalid payload |

## Negative Routes

Add explicit tests for:

- `GET /admin`: should be 404 unless intentionally introduced.
- `GET /platform-admin/unknown`: should be 404.
- `GET /quotes/bad`: should be 404.
- `POST` any non-API route without CSRF: should be 403.
- Tenant roles attempting `/platform-admin`.
- Platform admin attempting tenant routes without support session.
- Trainee attempting `/ai-assistant`, `/schedule`, `/quotes`, `/invoices`, `/team`, `/import-data`.

