157 lines
5.7 KiB
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();
|
|
});
|
|
});
|