Summary
Authorization logic for the two-tier permission system (super admin + project-scoped roles) is spread across User model methods, ProjectAccessService, AdminMiddleware, and inline controller checks. There is also a gap: the project-request approve/reject routes in routes/web.php only carry auth middleware and rely entirely on in-controller checks.
Consolidating into Laravel Policies creates a single, testable source of truth.
Implementation
1. Create app/Policies/ProjectPolicy.php
public function viewAny(User $user): bool // super admin or has any assigned project
public function view(User $user, Project $project): bool // super admin or assigned to project
public function manage(User $user, Project $project): bool // super admin or project admin
public function create(User $user): bool // super admin only
2. Create app/Policies/ProjectRequestPolicy.php
public function approve(User $user): bool // super admin only
public function reject(User $user): bool // super admin only
3. Register in AuthServiceProvider (or via model discovery in Laravel 11)
4. Update controllers to use $this->authorize() or Gate::authorize():
ProjectManagementController → authorize('manage', $project)
ProjectRequestController → authorize('approve', ProjectRequest::class)
- Remove the duplicate
isSuperAdmin() inline checks
5. Update routes/web.php — the project-request routes can optionally add the admin middleware as a belt-and-suspenders guard, since the Policy now enforces it explicitly.
Acceptance Criteria
Summary
Authorization logic for the two-tier permission system (super admin + project-scoped roles) is spread across
Usermodel methods,ProjectAccessService,AdminMiddleware, and inline controller checks. There is also a gap: the project-request approve/reject routes inroutes/web.phponly carryauthmiddleware and rely entirely on in-controller checks.Consolidating into Laravel Policies creates a single, testable source of truth.
Implementation
1. Create
app/Policies/ProjectPolicy.php2. Create
app/Policies/ProjectRequestPolicy.php3. Register in
AuthServiceProvider(or via model discovery in Laravel 11)4. Update controllers to use
$this->authorize()orGate::authorize():ProjectManagementController→authorize('manage', $project)ProjectRequestController→authorize('approve', ProjectRequest::class)isSuperAdmin()inline checks5. Update
routes/web.php— the project-request routes can optionally add theadminmiddleware as a belt-and-suspenders guard, since the Policy now enforces it explicitly.Acceptance Criteria
isSuperAdmin()/isAdminForProject()checks remain in controllers