$permission, 'guard_name' => 'admin']); } Permission::firstOrCreate(['name' => 'some-other.manager', 'guard_name' => 'admin']); } private function makeAdminWithPermissions(array $permissions): User { $role = Role::create(['name' => 'role-' . uniqid(), 'guard_name' => 'admin']); foreach ($permissions as $permission) { $role->givePermissionTo( Permission::firstOrCreate(['name' => $permission, 'guard_name' => 'admin']), ); } $admin = User::factory()->create(); $admin->assignRole($role); return $admin; } /** @dataProvider gatedRoutesProvider */ public function test_route_requires_expected_permission(string $method, string $uri, string $requiredPermission): void { $unauthorized = $this->makeAdminWithPermissions(['some-other.manager']); $this->actingAs($unauthorized, 'admin')->json($method, $uri)->assertStatus(403); $authorized = $this->makeAdminWithPermissions([$requiredPermission]); $response = $this->actingAs($authorized, 'admin')->json($method, $uri); $this->assertNotSame(403, $response->status(), "Expected {$uri} to be accessible with '{$requiredPermission}', got 403."); } public static function gatedRoutesProvider(): array { return [ 'settings index' => ['GET', '/api/v1/administrator/settings', 'setting.manager'], 'client-menu index' => ['GET', '/api/v1/administrator/client-menu', 'setting.manager'], 'login-blocks index' => ['GET', '/api/v1/administrator/login-blocks', 'setting.manager'], 'banks index' => ['GET', '/api/v1/administrator/banks', 'setting.manager'], 'domain-pricing index' => ['GET', '/api/v1/administrator/domain-pricing', 'setting.manager'], 'email-templates index' => ['GET', '/api/v1/administrator/email-templates', 'email-template.manager'], 'tickets index' => ['GET', '/api/v1/administrator/tickets', 'support.view'], 'tickets pending count' => ['GET', '/api/v1/administrator/tickets/pending-count', 'support.view'], 'activity-logs index' => ['GET', '/api/v1/administrator/activity-logs', 'system-log.manager'], 'email-logs index' => ['GET', '/api/v1/administrator/email-logs', 'system-log.manager'], 'post-categories index' => ['GET', '/api/v1/administrator/post-categories', 'post.view'], 'posts index' => ['GET', '/api/v1/administrator/posts', 'post.view'], 'media index' => ['GET', '/api/v1/administrator/media', 'media.view'], 'dashboard stats' => ['GET', '/api/v1/administrator/dashboard/stats', 'dashboard.view'], 'queue jobs recent' => ['GET', '/api/v1/administrator/queue/jobs/recent', 'queue.manager'], ]; } public function test_force_delete_post_requires_post_destroy_permission(): void { Permission::firstOrCreate(['name' => 'post.destroy', 'guard_name' => 'admin']); $unauthorized = $this->makeAdminWithPermissions(['post.view']); $this->actingAs($unauthorized, 'admin') ->deleteJson('/api/v1/administrator/posts/999/force-delete') ->assertStatus(403); $authorized = $this->makeAdminWithPermissions(['post.destroy']); $response = $this->actingAs($authorized, 'admin') ->deleteJson('/api/v1/administrator/posts/999/force-delete'); $this->assertNotSame(403, $response->status()); } public function test_dashboard_requires_dashboard_view_permission(): void { Permission::firstOrCreate(['name' => 'dashboard.view', 'guard_name' => 'admin']); $unauthorized = $this->makeAdminWithPermissions(['post.view']); $this->actingAs($unauthorized, 'admin') ->getJson('/api/v1/administrator/dashboard/stats') ->assertStatus(403); $authorized = $this->makeAdminWithPermissions(['dashboard.view']); $response = $this->actingAs($authorized, 'admin')->getJson('/api/v1/administrator/dashboard/stats'); $this->assertNotSame(403, $response->status()); } public function test_full_administrator_bypasses_all_permission_gates(): void { Role::firstOrCreate(['name' => 'Full Administrator', 'guard_name' => 'admin']); $admin = User::factory()->create(); $admin->assignRole('Full Administrator'); foreach (self::gatedRoutesProvider() as [$method, $uri]) { $response = $this->actingAs($admin, 'admin')->json($method, $uri); $this->assertNotSame(403, $response->status(), "Full Administrator should not be blocked from {$uri}."); } } }