# Route and RBAC Test Matrix

Date: 2026-06-15

This matrix gives a testing architect a starting point for route, permission, CSRF, and smoke tests. Permissions are inferred from controller calls and `RbacService`. Where route behavior is demo-specific, test both current behavior and future production expectations.

## Global Route Rules

- Unknown route must return 404.
- Non-API POST without valid `_csrf` must return 403 with "Security check failed".
- Protected routes call `SessionAuth::requirePermission()`.
- `RuntimeException('Forbidden')` is rendered as 403 by `App`.
- `Request::path()` must strip `/FieldOps-Cloud/public`.
- `PhpRenderer` must prefix root-relative `href` and `action` values with `/FieldOps-Cloud/public` when served by XAMPP.

## Public and Demo Routes

| Route | Auth/RBAC expectation | Test notes |
| --- | --- | --- |
| `GET /` | Public/demo accessible | Landing loads |
| `GET /auth` | Public/demo accessible | Auth page loads |
| `GET /demo` | Public/demo accessible | Demo page loads |
| `GET /demo/company-admin` | Switches session to tenant owner | Redirects to `/dashboard`; user role owner |
| `GET /demo/platform-admin` | Switches session to platform admin | Redirects to `/platform-admin`; user role platform_admin |
| `GET /pricing` | Public/demo accessible | Currency fallback and plan rendering |

## Operations Routes

| Route | Required permission | Allowed role examples | Denied role examples | Critical tests |
| --- | --- | --- | --- | --- |
| `GET /dashboard` | `view_dashboard` | owner, manager, supervisor, team_member, trainee, demo_user, platform_admin | none of known roles | KPI and quick links |
| `GET /clients` | `view_clients` | owner, manager, supervisor, team_member, trainee, demo_user | platform_admin | Search/list |
| `GET /clients/view` | `view_clients` | owner, manager, supervisor, team_member, trainee, demo_user | platform_admin | 200 for known client, 404 for unknown |
| `GET /clients/edit` | `update_clients` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Edit form |
| `GET /clients/create` | `create_clients` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Create form |
| `POST /clients` | `create_clients` + CSRF | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Valid/invalid create |
| `POST /clients/update` | `update_clients` + CSRF | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Save edit, duplicate prevention |
| `GET /jobs` | `view_jobs` | owner, manager, supervisor, team_member, trainee, demo_user | platform_admin | Search/filter |
| `GET /jobs/view` | `view_jobs` | owner, manager, supervisor, team_member, trainee, demo_user | platform_admin | Detail/404 |
| `GET /jobs/create` | `create_jobs` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Form |
| `GET /jobs/new` | `create_jobs` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Alias |
| `POST /jobs` | `create_jobs` + CSRF | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Validation |
| `GET /schedule` | `view_schedule` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | Calendar renders |
| `POST /schedule/appointments` | `view_schedule` + CSRF | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | Booking/conflict |
| `GET /quotes` | `view_quotes` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | List/search/filter |
| `GET /quotes/view` | `view_quotes` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | Detail |
| `GET /quotes/edit` | `update_quotes` and draft quote | owner, manager | supervisor, team_member, trainee, platform_admin, demo_user | Draft-only edit |
| `GET /quotes/create` | `create_quotes` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Form |
| `GET /quotes/new` | `create_quotes` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Alias |
| `POST /quotes` | `create_quotes` + CSRF | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Create, totals |
| `POST /quotes/update` | `update_quotes` + CSRF and draft quote | owner, manager | supervisor, team_member, trainee, platform_admin, demo_user | Save draft edit |
| `GET /invoices` | `view_invoices` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | List/search/filter |
| `GET /invoices/view` | `view_invoices` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | Detail |
| `GET /invoices/edit` | `update_invoices` and unpaid invoice | owner, manager | supervisor, team_member, trainee, platform_admin, demo_user | Unpaid-only edit |
| `GET /invoices/create` | `create_invoices` | owner, manager, demo_user | supervisor, team_member, trainee, platform_admin | Form |
| `GET /invoices/new` | `create_invoices` | owner, manager, demo_user | supervisor, team_member, trainee, platform_admin | Alias |
| `POST /invoices` | `create_invoices` + CSRF | owner, manager, demo_user | supervisor, team_member, trainee, platform_admin | Create, totals |
| `POST /invoices/update` | `update_invoices` + CSRF and unpaid invoice | owner, manager | supervisor, team_member, trainee, platform_admin, demo_user | Save unpaid edit |
| `GET /ai-assistant` | `use_ai_assistant` | owner, manager, supervisor, team_member, demo_user | trainee, platform_admin | UI modes |
| `GET /import-data` | `import_csv` | owner, manager, demo_user | supervisor, team_member, trainee, platform_admin | Type/schema validation |
| `GET /import` | `import_csv` | owner, manager, demo_user | supervisor, team_member, trainee, platform_admin | Alias |
| `GET /import-data/sample` | `import_csv` | owner, manager, demo_user | supervisor, team_member, trainee, platform_admin | CSV sample |
| `GET /export-data` | `export_csv` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | UI |
| `GET /export` | `export_csv` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Alias |
| `GET /export-data/download` | `export_csv` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | CSV download |
| `GET /team` | `view_team` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | List |
| `GET /team/view` | `view_team` | owner, manager, supervisor, demo_user | team_member, trainee, platform_admin | Detail |
| `GET /team/edit` | `manage_team` | owner, manager | supervisor, demo_user, team_member, trainee, platform_admin | Edit form |
| `POST /team/update` | `manage_team` + CSRF | owner, manager | supervisor, demo_user, team_member, trainee, platform_admin | Save edit |
| `GET /reports` | `view_reports` | owner, manager, supervisor, demo_user, platform_admin | team_member, trainee | Report cards |
| `GET /settings` | `view_settings` | owner, manager, demo_user, platform_admin | supervisor, team_member, trainee | Settings |
| `POST /logout` | CSRF | session users | missing CSRF | Session destroyed |

Important nuance: `demo_user` can create quotes/invoices but does not have `update_quotes`, `update_invoices`, or `manage_team`; this means demo_user can see create paths but cannot edit locked update routes. Test the actual role selected in UI before treating this as a bug.

## Integration Settings Routes

| Route | Required permission | Critical tests |
| --- | --- | --- |
| `GET /settings/accounting` | `view_accounting_integrations` | All 10 providers render; SMEPlus marked owner confirmation |
| `GET /settings/calendar` | `view_calendar_integrations` | Google and Outlook render; two-way sync wording |
| `GET /settings/llm` | `view_llm_settings` | Provider list, secret safety rules |

## SaaS Tenant Routes

| Route | Required permission | Critical tests |
| --- | --- | --- |
| `GET /workspace` | `manage_tenant` | Tenant workspace, plan, onboarding, usage |
| `GET /subscription` | `manage_billing` | Usage, plan, billing/trial display |

## Pricing Admin Routes

| Route | Required permission | Critical tests |
| --- | --- | --- |
| `GET /settings/pricing` | `manage_platform_pricing` | Form renders only for platform_admin |
| `POST /settings/pricing` | `manage_platform_pricing` + CSRF | Save valid settings, reject invalid settings |
| `POST /settings/pricing/reset` | `manage_platform_pricing` + CSRF | Reset defaults |

## Platform Admin Routes

| Route | Required permission | Critical tests |
| --- | --- | --- |
| `GET /platform-admin` | `view_platform_admin` | Main console loads; tenant roles 403 |
| `GET /platform-admin/tenants` | `view_platform_admin` | Tenant list |
| `GET /platform-admin/tenants/view` | `view_platform_admin` | Tenant detail tabs |
| `POST /platform-admin/tenants/login` | `impersonate_tenants` + CSRF | Requires reason, duration, tenant; audits support login |
| `GET /platform-admin/tenants/return` | support session | Restores original platform admin |
| `POST /platform-admin/tenants/return` | support session + CSRF | Restores original platform admin |
| `POST /platform-admin/tenants/action` | `manage_platform_admin` + CSRF | Requires confirmation; validates action-specific fields; audits |
| `POST /platform-admin/billing/override` | `manage_platform_billing` + CSRF | Override/pause/resume billing |
| `POST /platform-admin/accounting/connect` | `manage_platform_billing` + CSRF | Sandbox-only provider link |
| `POST /platform-admin/demo/reset` | `manage_platform_admin` + CSRF | Explicit audited reset |
| `GET /platform-admin/health` | `view_platform_admin` | Health page |
| `GET /platform-admin/security` | `view_platform_admin` | Security admin page |
| `GET /platform-admin/mobile-offline` | `view_platform_admin` | Mobile/offline readiness |

## Negative Route Tests To Add

- Tenant owner opens `/platform-admin`: expect 403.
- Manager opens `/settings/pricing`: expect 403.
- Demo user opens `/team/edit`: expect 403.
- Team member opens `/quotes/create`: expect 403.
- Supervisor opens `/invoices/create`: expect 403.
- Trainee opens `/schedule`: expect 403.
- POST any form without `_csrf`: expect 403.
- POST with tampered `_csrf`: expect 403.
- Unknown routes: `/admin`, `/api/clients`, `/platform-admin/unknown`, `/quotes/bad`: expect 404 unless implemented later.

