Implements platform feature branch `285-workspace-rbac-environment-access`. Summary: - switch managed environment authorization to workspace-first role resolution with explicit environment-scope narrowing - rewire Filament pages, resources, policies, and user tenant access helpers to the shared access-scope resolver - add Spec 285 coverage across unit, feature, and browser tests plus full spec artifacts Validation: - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Auth/WorkspaceFirstCapabilityResolverTest.php tests/Unit/Auth/ManagedEnvironmentAccessScopeResolverTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Auth/WorkspaceFirstManagedEnvironmentAccessTest.php tests/Feature/Filament/ManagedEnvironmentAccessScopeManagementTest.php tests/Feature/Filament/WorkspaceMembershipRoleManagementTest.php tests/Feature/Rbac/GovernanceArtifactsWorkspaceFirstAuthorizationTest.php tests/Feature/Rbac/OperationRunWorkspaceFirstAuthorizationTest.php tests/Feature/Rbac/ProviderConnectionWorkspaceFirstPolicyTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Verification/ProviderExecutionReauthorizationTest.php tests/Feature/ProviderConnections/ProviderConnectionHealthCheckStartSurfaceTest.php tests/Feature/Tenants/TenantProviderBackedActionStartTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Audit/TenantMembershipAuditLogTest.php tests/Feature/Filament/TenantMembersTest.php tests/Feature/TenantRBAC/TenantMembershipCrudTest.php tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` Target branch: `platform-dev`. Follow-up integration path after merge: - `platform-dev` -> `dev`. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #344
79 lines
2.7 KiB
PHP
79 lines
2.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Auth;
|
|
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\ManagedEnvironmentMembership;
|
|
use App\Models\User;
|
|
use App\Services\Intune\AuditLogger;
|
|
use App\Support\Audit\AuditActionId;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class TenantDiagnosticsService
|
|
{
|
|
public function __construct(public AuditLogger $auditLogger) {}
|
|
|
|
public function tenantHasNoOwners(ManagedEnvironment $tenant): bool
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public function userHasDuplicateMemberships(ManagedEnvironment $tenant, User $user): bool
|
|
{
|
|
return ManagedEnvironmentMembership::query()
|
|
->where('managed_environment_id', (int) $tenant->getKey())
|
|
->where('user_id', (int) $user->getKey())
|
|
->count() > 1;
|
|
}
|
|
|
|
public function mergeDuplicateMembershipsForUser(ManagedEnvironment $tenant, User $actor, User $member): void
|
|
{
|
|
DB::transaction(function () use ($tenant, $actor, $member): void {
|
|
$memberships = ManagedEnvironmentMembership::query()
|
|
->where('managed_environment_id', (int) $tenant->getKey())
|
|
->where('user_id', (int) $member->getKey())
|
|
->orderBy('created_at')
|
|
->get();
|
|
|
|
if ($memberships->count() <= 1) {
|
|
return;
|
|
}
|
|
|
|
$membershipToKeep = $memberships->first();
|
|
if (! $membershipToKeep instanceof ManagedEnvironmentMembership) {
|
|
return;
|
|
}
|
|
|
|
$idsToDelete = $memberships
|
|
->reject(fn (ManagedEnvironmentMembership $m): bool => $m->getKey() === $membershipToKeep->getKey())
|
|
->pluck($membershipToKeep->getKeyName())
|
|
->all();
|
|
|
|
ManagedEnvironmentMembership::query()
|
|
->whereIn($membershipToKeep->getKeyName(), $idsToDelete)
|
|
->delete();
|
|
|
|
$this->auditLogger->log(
|
|
tenant: $tenant,
|
|
action: AuditActionId::TenantMembershipDuplicatesMerged->value,
|
|
context: [
|
|
'metadata' => [
|
|
'member_user_id' => (int) $member->getKey(),
|
|
'kept_membership_id' => (string) $membershipToKeep->getKey(),
|
|
'deleted_membership_ids' => array_values(array_map('strval', $idsToDelete)),
|
|
],
|
|
],
|
|
actorId: (int) $actor->getKey(),
|
|
actorEmail: $actor->email,
|
|
actorName: $actor->name,
|
|
status: 'success',
|
|
resourceType: 'tenant',
|
|
resourceId: (string) $tenant->getKey(),
|
|
);
|
|
});
|
|
}
|
|
|
|
}
|