TenantAtlas/apps/platform/tests/Feature/System/SupportAccessRecoveryBoundaryTest.php
ahmido 1e0f21365b PR: 276-support-access-governance → platform-dev (#332)
Automated PR created via MCP by Copilot on user request: "pr gegen platform-dev".

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #332
2026-05-05 21:54:26 +00:00

145 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\System\Pages\Dashboard;
use App\Filament\System\Pages\RepairWorkspaceOwners;
use App\Models\AuditLog;
use App\Models\PlatformUser;
use App\Models\SupportAccessGrant;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Support\Auth\PlatformCapabilities;
use App\Support\Auth\WorkspaceRole;
use Filament\Facades\Filament;
use Livewire\Livewire;
beforeEach(function (): void {
Filament::setCurrentPanel('system');
Filament::bootCurrentPanel();
Tenant::factory()->create([
'tenant_id' => null,
'external_id' => 'platform',
'name' => 'Platform',
]);
config()->set('tenantpilot.break_glass.enabled', true);
config()->set('tenantpilot.break_glass.ttl_minutes', 15);
});
function spec276_recovery_platform_user(): PlatformUser
{
return PlatformUser::factory()->create([
'capabilities' => [
PlatformCapabilities::ACCESS_SYSTEM_PANEL,
PlatformCapabilities::CONSOLE_VIEW,
PlatformCapabilities::USE_BREAK_GLASS,
],
]);
}
function spec276_ownerless_workspace_with_target(): array
{
$workspace = Workspace::factory()->create();
$targetUser = User::factory()->create();
WorkspaceMembership::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'user_id' => (int) $targetUser->getKey(),
'role' => WorkspaceRole::Operator->value,
]);
return [$workspace, $targetUser];
}
it('blocks owner repair when break-glass is active but recovery-scoped support access is missing', function (): void {
$platformUser = spec276_recovery_platform_user();
[$workspace, $targetUser] = spec276_ownerless_workspace_with_target();
$this->actingAs($platformUser, 'platform');
Livewire::test(Dashboard::class)
->callAction('enter_break_glass', data: [
'reason' => 'Recover workspace ownership',
]);
Livewire::test(RepairWorkspaceOwners::class)
->callAction('assign_owner', data: [
'workspace_id' => (int) $workspace->getKey(),
'target_user_id' => (int) $targetUser->getKey(),
'reason' => 'Fix ownerless workspace',
])
->assertHasErrors();
expect(WorkspaceMembership::query()
->where('workspace_id', (int) $workspace->getKey())
->where('user_id', (int) $targetUser->getKey())
->value('role'))->toBe(WorkspaceRole::Operator->value);
});
it('does not allow audit-view support access to satisfy the owner-repair boundary', function (): void {
$platformUser = spec276_recovery_platform_user();
[$workspace, $targetUser] = spec276_ownerless_workspace_with_target();
SupportAccessGrant::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'requested_by_platform_user_id' => (int) $platformUser->getKey(),
'scope' => SupportAccessGrant::SCOPE_AUDIT_VIEW,
]);
$this->actingAs($platformUser, 'platform');
Livewire::test(Dashboard::class)
->callAction('enter_break_glass', data: [
'reason' => 'Recover workspace ownership',
]);
Livewire::test(RepairWorkspaceOwners::class)
->callAction('assign_owner', data: [
'workspace_id' => (int) $workspace->getKey(),
'target_user_id' => (int) $targetUser->getKey(),
'reason' => 'Fix ownerless workspace',
])
->assertHasErrors();
});
it('allows owner repair only when break-glass and active workspace-recovery support access are both present', function (): void {
$platformUser = spec276_recovery_platform_user();
[$workspace, $targetUser] = spec276_ownerless_workspace_with_target();
$supportGrant = SupportAccessGrant::factory()->activeRecovery()->create([
'workspace_id' => (int) $workspace->getKey(),
'requested_by_platform_user_id' => (int) $platformUser->getKey(),
]);
$this->actingAs($platformUser, 'platform');
Livewire::test(Dashboard::class)
->callAction('enter_break_glass', data: [
'reason' => 'Recover workspace ownership',
]);
Livewire::test(RepairWorkspaceOwners::class)
->callAction('assign_owner', data: [
'workspace_id' => (int) $workspace->getKey(),
'target_user_id' => (int) $targetUser->getKey(),
'reason' => 'Fix ownerless workspace',
])
->assertHasNoActionErrors()
->assertNotified('Owner assigned');
expect(WorkspaceMembership::query()
->where('workspace_id', (int) $workspace->getKey())
->where('user_id', (int) $targetUser->getKey())
->value('role'))->toBe(WorkspaceRole::Owner->value);
expect(AuditLog::query()
->where('workspace_id', (int) $workspace->getKey())
->where('action', 'workspace_membership.break_glass.assign_owner')
->whereJsonContains('metadata->support_access_grant_id', (int) $supportGrant->getKey())
->exists())->toBeTrue();
});