# Permissions Audit

## Audit Session - 2026-05-29 11:38:13 +03:00

## Current Role Storage

| Item | Current Implementation |
|---|---|
| Role package | Spatie Laravel Permission |
| Roles found before test DB reset | `admin`, `supervisor`, `agent` |
| Permissions found before test DB reset | None; permissions table was empty |
| Role assignment | `model_has_roles` via UserResource role relationship |
| Permission customization | Not implemented |
| Main enforcement | Policies, Eloquent scopes, Filament resource methods, dashboard JavaScript |

## Permission Areas

| Permission Area | Current Implementation | Backend Protected? | Frontend Protected? | Problem | Recommendation |
|---|---|---|---|---|---|
| Admin full access | Policy `before()` returns true in several policies | Mostly yes | Dashboard allows admin | Admin is role name, but dashboard role selector shows Manager | Align labels and values: Admin/General Manager = `admin`. |
| Conversation list | `ConversationResource::getEloquentQuery()` filters supervisor/agent; `AssignedScope` also exists | Yes | Dashboard filters chat list and nav | Duplicate filtering exists in multiple places | Centralize rules and keep resource/controller consistent. |
| Conversation direct URL | `ConversationPolicy::view()` plus global scope | Yes | N/A | Good coverage for conversations | Keep tests and add browser/HTTP direct URL tests. |
| Message read/send | `MessageController` authorizes view/reply | Yes | Dashboard hides/shows actions | Reply sends through WhatsApp API and can fail at runtime if credentials missing | Add user-facing error states and credential health checks. |
| Agent transfer | `ConversationPolicy::transfer()` allows agent if assigned; endpoint does not call `transferTo` for agents | Partly | Dashboard currently hides transfer for agent | Backend allows more than expected behavior | Change policy so Employee cannot transfer unless business explicitly allows it. |
| Supervisor transfer | Policy validates current assignee and target user are in team | Yes | Dashboard says supervisor can transfer | Good concept | Ensure UI target list is filtered to team members, not all active agents. |
| Contacts | `ContactResource::canViewAny()` admin only | Yes | Dashboard says admin/supervisor | Role mismatch | Decide whether supervisors can see team contacts; then align resource and nav. |
| Assignment Log | `AssignmentPolicy::viewAny()` returns true; resource filters supervisor only | No for agent | Dashboard hides for agent | Agent can access assignment resource logic and count all rows | Add agent filter or block agent `viewAny`; add tests. |
| Team screen | `TeamResource::canViewAny()` admin only | Yes | Dashboard allows admin/supervisor | Role mismatch | Either allow supervisor own team read-only or remove supervisor nav access. |
| Employee/users screen | `UserResource::canViewAny()` admin/supervisor; `canCreate()` admin only | Mostly yes | Dashboard uses Permissions/Settings aliases | Supervisor can view team members, but no clear Team/Employees separation | Make Team/Employees screen explicit; do not use it as Permissions/Settings. |
| Reports | Dashboard maps to Webhook Events | Admin protected by WebhookEventPolicy | Dashboard allows supervisor | Wrong screen and role mismatch | Create real Reports page with admin/global and supervisor/team scopes. |
| Permissions screen | Not implemented; `/admin/users` alias | No real permission model | Dashboard says admin only | Misleading and incomplete | Create a real permissions matrix or remove item until implemented. |
| Settings screen | Not implemented; `/admin/users` alias | No | Dashboard says admin only | Misleading and incomplete | Create settings resource/page for WhatsApp credentials, routing, and system options. |
| Broadcast channels | `routes/channels.php` authorizes by `ConversationPolicy::view()` | Yes | UI does not subscribe to broadcast | Backend exists, frontend still polls | Use Reverb/Echo later after permission rules are stable. |

## Role Behavior Gap Analysis

| Role | Expected Access | Current Access | Gap / Issue | Recommended Fix |
|---|---|---|---|---|
| Admin / General Manager | See/manage everything; assign/transfer anyone; manage employees, permissions, reports, settings | Can access conversations, contacts, teams, users, assignments, webhook events; can transfer/close | No real Reports, Permissions, Settings screens; dashboard label says Manager but backend role is admin | Build real admin screens and align labels. |
| Supervisor | See team employees, team conversations, team reports; assign/transfer inside team only | Conversations filtered to team; users filtered to team; assignment log filtered to team; some sidebar items allowed but resources block them | Contacts, Team, Reports nav allowed in dashboard but blocked or wrong target; transfer target options may show all active users in some places | Align nav with backend; filter transfer options; create team reports. |
| Employee / Agent | See only assigned conversations/contacts; reply only to assigned conversations; no team/permissions/global reports/settings | Conversations and messages are scoped; dashboard hides many items | Backend allows agent transfer own conversation; AssignmentResource can expose all assignments; no clear assigned contacts screen | Block assignment log for agents or filter own only; block agent transfer unless approved; add tests. |

## Direct Access Findings

Before the test database reset, resource logic was checked under admin, supervisor, and agent:

| Role | Conversation Count via Resource | Assignment Count via Resource | User Count via Resource | Notes |
|---|---:|---:|---:|---|
| admin | 5 | 7 | 6 | Full access expected. |
| supervisor | 1 | 3 | 4 | Team-scoped users/conversations/assignments. |
| agent | 0 | 7 | 6 | `canViewAny` blocks users, but AssignmentResource allowed all 7 rows. |

Critical: the agent assignment count indicates a real resource-layer permission gap.

## Backend Files Reviewed

- `app/Policies/ConversationPolicy.php`
- `app/Policies/AssignmentPolicy.php`
- `app/Policies/UserPolicy.php`
- `app/Policies/TeamPolicy.php`
- `app/Policies/ContactPolicy.php`
- `app/Policies/WebhookEventPolicy.php`
- `app/Models/Scopes/AssignedScope.php`
- `app/Filament/Resources/*Resource.php`
- `routes/api.php`
- `routes/channels.php`

## Phase 2 Permission Tasks

| Priority | Task |
|---|---|
| Critical | Fix `phpunit.xml` test database isolation before running tests. |
| Critical | Fix AssignmentResource so agents cannot see all assignment rows. |
| High | Decide whether agents can transfer conversations; expected behavior says no. |
| High | Align dashboard role values with backend roles. |
| High | Align sidebar role access with resource/backend access. |
| High | Create real Permissions screen or remove/rename the sidebar item until ready. |
| Medium | Add tests for direct Filament resource access by role. |
| Medium | Add transfer target filtering in all transfer UIs. |

## Phase 2 Update - 2026-05-29 12:09:31 +03:00

| Permission Area | Current Implementation | Backend Protected? | Frontend Protected? | Problem | Recommendation |
|---|---|---|---|---|---|
| Test DB isolation | `phpunit.xml` uses sqlite `:memory:`. | N/A | N/A | Local data was previously reset before Phase 2. | Keep this setting; avoid cached config before tests. |
| Assignment Log | Admin all, supervisor team-scoped, employee blocked. | Yes | Yes | No remaining employee access found in tests. | Keep tests for `/admin/assignments`. |
| Conversation transfer | Admin all active employees; supervisor team conversations to team employees; employee blocked. | Yes | Yes | Waiting List assignment workflow still incomplete. | Build explicit assignment flow in Phase 3. |
| Dashboard role display | Uses real backend role from `/api/workspace/data`. | N/A | Yes | Fake local role switcher removed. | Keep role changes server-side only. |
| Reports | Real placeholder route, admin/supervisor access. | Yes | Yes | No report data yet. | Implement scoped reports later. |
| Permissions | Real placeholder route, admin-only access. | Yes | Yes | No configurable matrix yet. | Decide if policy-only roles are enough or if per-employee overrides are required. |
| Settings | Real placeholder route, admin-only access. | Yes | Yes | No settings forms yet. | Implement WhatsApp/system settings after permission model is finalized. |

## Phase 3A Waiting List Permission Update - 2026-05-29 12:30:00 +03:00

| Permission Area | Current Implementation | Backend Protected? | Frontend Protected? | Problem | Recommendation |
|---|---|---|---|---|---|
| Waiting List page | `WaitingList::canAccess()` allows admin and supervisor only. | Yes | Yes | Employee access is now blocked. | Keep route tests for all roles. |
| Waiting List API index | `GET /api/waiting-list` uses `viewWaitingList`. | Yes | N/A | None found. | Keep API scoped through `WaitingListService`. |
| Waiting List assign | `POST /api/waiting-list/{conversation}/assign` validates pending/unassigned state, active employee target, and role scope. | Yes | N/A | None found. | Keep employees blocked by direct API tests. |
| Admin assignment | Admin can assign any pending/unassigned conversation to any active employee. | Yes | Yes | Supervisors are not valid assignment targets in Phase 3A. | Revisit only if business wants supervisors to handle chats. |
| Supervisor assignment | Supervisor can assign only team-scoped pending conversations to employees under their team. | Yes | Yes | Global unscoped waiting conversations are admin-only. | Define future routing rules if global rows should be claimable by supervisors. |
| Employee restriction | Employee cannot view or assign waiting conversations. | Yes | Yes | None found. | Keep as default business rule. |

## Phase 3B Employee Profile Permission Update - 2026-05-29 12:45:48 +03:00

| Permission Area | Current Implementation | Backend Protected? | Frontend Protected? | Problem | Recommendation |
|---|---|---|---|---|---|
| Employee profile view | Admin all; supervisor team members only; employee blocked. | Yes | Yes | No My Profile route. | Add self-profile later only if required. |
| Employee edit | Admin only. | Yes | Yes | Supervisor cannot edit roles/team/status. | Keep unless business explicitly delegates editing. |
| Employee delete | Admin only and blocked when user owns conversations. | Yes | Yes | None found in Phase 3B. | Add transfer-before-delete flow later if needed. |
| Assignment history on profile | Profile is reachable only through allowed employee scope. | Yes | Yes | `assigned_from` is inferred from prior assignment rows. | Add dedicated audit fields only if compliance requires exact transfer-from history. |

## Phase 3C Reports Permission Update - 2026-05-29

| Permission Area | Current Implementation | Backend Protected? | Frontend Protected? | Problem | Recommendation |
|---|---|---|---|---|---|
| Reports page | `Reports::canAccess()` allows admin and supervisor only. | Yes | Yes | Employee access is now blocked. | Keep employee blocked — personal reports not implemented. |
| Admin report scope | Admin sees all conversations, employees, teams, and assignments. | Yes | Yes | None found. | Admin can filter by team and employee. |
| Supervisor report scope | Supervisor sees only own team's conversations, employees, and assignments. | Yes | Yes | Team filter is ignored for supervisor (always team-scoped). | Supervisor cannot filter other teams. |
| Report data isolation | Queries use `Conversation::system()` bypassing `AssignedScope` but apply explicit team/user scoping. | Yes | N/A | Reports correctly bypass AssignedScope without leaking data. | Keep system() usage for reports; role scoping is explicit. |
| Team filter | Admin: filters by team; Supervisor: ignored (always team-scoped). | Yes | Yes | No supervisor filter needed for current scope. | Revisit if supervisors manage multiple teams. |

## Phase 3D Settings Permission Update - 2026-05-29

| Permission Area | Current Implementation | Backend Protected? | Frontend Protected? | Problem | Recommendation |
|---|---|---|---|---|---|
| Settings page | `Settings::canAccess()` allows admin only. | Yes | Yes | Supervisor and employee are blocked. | Keep admin-only — settings contain sensitive configuration status. |
| Config visibility | Admin can see masked secrets and health status. | N/A | N/A | Full tokens are never exposed; only masked values shown. | Keep `maskSecret()` method for all sensitive values. |
