TenantAtlas/tests/Feature/Baselines/BaselineProfileAuthorizationTest.php

157 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Resources\BaselineProfileResource;
use App\Models\BaselineProfile;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
describe('BaselineProfile RBAC — 404 vs 403 semantics', function () {
it('denies non-members accessing the list page', function (): void {
$user = User::factory()->create();
$workspace = Workspace::factory()->create();
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
$response = $this->actingAs($user)
->get(BaselineProfileResource::getUrl(panel: 'admin'));
expect($response->status())->toBeIn([403, 404, 302], 'Non-members should not get HTTP 200');
});
it('returns 404 for members accessing a profile from another workspace', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$otherWorkspace = Workspace::factory()->create();
$profile = BaselineProfile::factory()->create([
'workspace_id' => (int) $otherWorkspace->getKey(),
]);
$this->actingAs($user)
->get(BaselineProfileResource::getUrl('view', ['record' => $profile], panel: 'admin'))
->assertNotFound();
});
it('returns 200 for readonly members accessing list page', function (): void {
[$user] = createUserWithTenant(role: 'readonly');
$this->actingAs($user)
->get(BaselineProfileResource::getUrl(panel: 'admin'))
->assertOk();
});
it('returns 403 for members with mocked missing capability on list page', function (): void {
$workspace = Workspace::factory()->create();
$user = User::factory()->create();
WorkspaceMembership::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'user_id' => (int) $user->getKey(),
'role' => 'readonly',
]);
$resolver = \Mockery::mock(WorkspaceCapabilityResolver::class);
$resolver->shouldReceive('isMember')->andReturnTrue();
$resolver->shouldReceive('can')->andReturnFalse();
app()->instance(WorkspaceCapabilityResolver::class, $resolver);
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
$this->actingAs($user)
->get(BaselineProfileResource::getUrl(panel: 'admin'))
->assertForbidden();
});
it('returns 403 for readonly members accessing create page', function (): void {
[$user] = createUserWithTenant(role: 'readonly');
$this->actingAs($user)
->get(BaselineProfileResource::getUrl('create', panel: 'admin'))
->assertForbidden();
});
it('returns 200 for owner members accessing create page', function (): void {
[$user] = createUserWithTenant(role: 'owner');
$this->actingAs($user)
->get(BaselineProfileResource::getUrl('create', panel: 'admin'))
->assertOk();
});
it('returns 404 for members accessing profile from another workspace', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$otherWorkspace = Workspace::factory()->create();
$profile = BaselineProfile::factory()->create([
'workspace_id' => (int) $otherWorkspace->getKey(),
]);
$this->actingAs($user)
->get(BaselineProfileResource::getUrl('view', ['record' => $profile], panel: 'admin'))
->assertNotFound();
});
it('returns 403 for readonly members accessing edit page', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'readonly');
$workspace = Workspace::query()->whereKey($tenant->workspace_id)->first();
$profile = BaselineProfile::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
]);
$this->actingAs($user)
->get(BaselineProfileResource::getUrl('edit', ['record' => $profile], panel: 'admin'))
->assertForbidden();
});
it('returns 200 for owner members accessing edit page', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$workspace = Workspace::query()->whereKey($tenant->workspace_id)->first();
$profile = BaselineProfile::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
]);
$this->actingAs($user)
->get(BaselineProfileResource::getUrl('edit', ['record' => $profile], panel: 'admin'))
->assertOk();
});
});
describe('BaselineProfile static authorization methods', function () {
it('canViewAny returns false for non-members', function (): void {
$user = User::factory()->create();
$workspace = Workspace::factory()->create();
$this->actingAs($user);
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
expect(BaselineProfileResource::canViewAny())->toBeFalse();
});
it('canViewAny returns true for members', function (): void {
[$user] = createUserWithTenant(role: 'readonly');
$this->actingAs($user);
expect(BaselineProfileResource::canViewAny())->toBeTrue();
});
it('canCreate returns true for managers and false for readonly', function (): void {
[$owner] = createUserWithTenant(role: 'owner');
$this->actingAs($owner);
expect(BaselineProfileResource::canCreate())->toBeTrue();
[$readonly] = createUserWithTenant(role: 'readonly');
$this->actingAs($readonly);
expect(BaselineProfileResource::canCreate())->toBeFalse();
});
});