create(); $tenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'readonly', ]); $decision = app(ManagedEnvironmentAccessScopeResolver::class)->decision($user, $tenant, Capabilities::PROVIDER_VIEW); expect($decision->workspaceMember)->toBeTrue() ->and($decision->explicitScopeRowsPresent)->toBeFalse() ->and($decision->managedEnvironmentAllowed)->toBeTrue() ->and($decision->capabilityAllowed)->toBeTrue() ->and($decision->allowed())->toBeTrue(); }); it('narrows a workspace member to explicitly scoped environments only', function (): void { $workspace = Workspace::factory()->create(); $allowedTenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $deniedTenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'operator', ]); ManagedEnvironmentMembership::query()->create([ 'managed_environment_id' => (int) $allowedTenant->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'operator', 'source' => 'manual', ]); $resolver = app(ManagedEnvironmentAccessScopeResolver::class); $deniedDecision = $resolver->decision($user, $deniedTenant, Capabilities::PROVIDER_VIEW); expect($resolver->canAccess($user, $allowedTenant))->toBeTrue() ->and($resolver->canAccess($user, $deniedTenant))->toBeFalse() ->and($deniedDecision->failedBoundary)->toBe('managed_environment_scope') ->and($deniedDecision->shouldDenyAsNotFound())->toBeTrue(); }); it('ignores scope rows that belong to another workspace boundary', function (): void { $workspace = Workspace::factory()->create(); $tenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $foreignWorkspace = Workspace::factory()->create(); $foreignTenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $foreignWorkspace->getKey(), ]); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'readonly', ]); ManagedEnvironmentMembership::query()->create([ 'managed_environment_id' => (int) $foreignTenant->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'readonly', 'source' => 'manual', ]); $decision = app(ManagedEnvironmentAccessScopeResolver::class)->decision($user, $tenant); expect($decision->explicitScopeRowsPresent)->toBeFalse() ->and($decision->allowed())->toBeTrue(); }); it('removes environment scope rows and invalidates access when workspace membership ends', function (): void { $workspace = Workspace::factory()->create(); $tenant = ManagedEnvironment::factory()->active()->create([ 'workspace_id' => (int) $workspace->getKey(), ]); $actor = User::factory()->create(); $member = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $actor->getKey(), 'role' => 'owner', ]); $memberMembership = WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'operator', ]); $scope = ManagedEnvironmentMembership::query()->create([ 'managed_environment_id' => (int) $tenant->getKey(), 'user_id' => (int) $member->getKey(), 'role' => 'operator', 'source' => 'manual', ]); $resolver = app(ManagedEnvironmentAccessScopeResolver::class); expect($resolver->canAccess($member, $tenant))->toBeTrue(); app(WorkspaceMembershipManager::class)->removeMember($workspace, $actor, $memberMembership); expect($resolver->canAccess($member, $tenant))->toBeFalse() ->and(ManagedEnvironmentMembership::query()->whereKey($scope->getKey())->exists())->toBeFalse(); });