Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
100.00% |
10 / 10 |
|
100.00% |
7 / 7 |
CRAP | |
100.00% |
1 / 1 |
| CompanyRolePlayPolicy | |
100.00% |
10 / 10 |
|
100.00% |
7 / 7 |
13 | |
100.00% |
1 / 1 |
| view | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| create | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| update | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| delete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| manageSessions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
| sameCompany | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
| isCompanyAdmin | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Policies; |
| 4 | |
| 5 | use App\Http\Models\Auth\Role; |
| 6 | use App\Http\Models\Auth\User; |
| 7 | use App\Http\Models\CompanyRolePlayProject; |
| 8 | |
| 9 | /** |
| 10 | * Authorization policy for corporate {@see CompanyRolePlayProject} personas. |
| 11 | * |
| 12 | * The rules are deliberately company-scoped and role-aware: mutating a |
| 13 | * persona is restricted to company Global Admins (or Vengreso super-admins, |
| 14 | * who surface as Global Admins after a company masquerade). Cross-company |
| 15 | * access is never permitted, even for Vengreso admins — the effective-user |
| 16 | * check (`auth()->user()->company_id`) is sufficient because masquerade |
| 17 | * swaps the token. See Phase 1 brief decision #7. |
| 18 | */ |
| 19 | class CompanyRolePlayPolicy |
| 20 | { |
| 21 | /** |
| 22 | * Determine whether the user may read a specific corporate persona. |
| 23 | * |
| 24 | * Any user in the owning company can read; visibility (group/user |
| 25 | * assignment) is enforced separately at query time via |
| 26 | * {@see CompanyRolePlayProject::scopeAvailableToUser()}. |
| 27 | */ |
| 28 | public function view(User $user, CompanyRolePlayProject $persona): bool |
| 29 | { |
| 30 | return $this->sameCompany($user, $persona); |
| 31 | } |
| 32 | |
| 33 | /** |
| 34 | * Determine whether the user may create a corporate persona. |
| 35 | * |
| 36 | * Requires a Global Admin (or Vengreso Admin) role and a non-null |
| 37 | * `company_id` on the authenticated user. |
| 38 | */ |
| 39 | public function create(User $user): bool |
| 40 | { |
| 41 | return $this->isCompanyAdmin($user) && ! empty($user->company_id); |
| 42 | } |
| 43 | |
| 44 | /** |
| 45 | * Determine whether the user may update a specific corporate persona. |
| 46 | */ |
| 47 | public function update(User $user, CompanyRolePlayProject $persona): bool |
| 48 | { |
| 49 | return $this->sameCompany($user, $persona) && $this->isCompanyAdmin($user); |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Determine whether the user may delete a specific corporate persona. |
| 54 | */ |
| 55 | public function delete(User $user, CompanyRolePlayProject $persona): bool |
| 56 | { |
| 57 | return $this->sameCompany($user, $persona) && $this->isCompanyAdmin($user); |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Determine whether the user may browse aggregate sessions/analytics |
| 62 | * belonging to a specific corporate persona. |
| 63 | */ |
| 64 | public function manageSessions(User $user, CompanyRolePlayProject $persona): bool |
| 65 | { |
| 66 | return $this->sameCompany($user, $persona) && $this->isCompanyAdmin($user); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * Company match — the single invariant that gates every method. |
| 71 | */ |
| 72 | private function sameCompany(User $user, CompanyRolePlayProject $persona): bool |
| 73 | { |
| 74 | if (empty($user->company_id) || empty($persona->company_id)) { |
| 75 | return false; |
| 76 | } |
| 77 | |
| 78 | return (string) $user->company_id === (string) $persona->company_id; |
| 79 | } |
| 80 | |
| 81 | /** |
| 82 | * Whether the user has a role capable of mutating company-scoped resources. |
| 83 | */ |
| 84 | private function isCompanyAdmin(User $user): bool |
| 85 | { |
| 86 | $role = role($user->role); |
| 87 | |
| 88 | return in_array($role, [Role::GLOBAL_ADMIN, Role::VENGRESO_ADMIN], true); |
| 89 | } |
| 90 | } |