Files
portal-api/tests/Feature/Api/Admin/PermissionGatingTest.php

117 lines
5.4 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Feature\Api\Admin;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
class PermissionGatingTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
// CheckPermission throws when the checked permission row doesn't exist at all —
// seed every permission this test touches so unauthorized checks 403 instead of 500.
foreach (self::gatedRoutesProvider() as [, , $permission]) {
Permission::firstOrCreate(['name' => $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}.");
}
}
}