TenantAtlas/apps/platform/app/Services/Auth/TenantDiagnosticsService.php
ahmido e64bae9cfc feat: cut over tenant core to managed environments (#335)
## Summary
- replace the legacy Tenant and TenantMembership core models with ManagedEnvironment and ManagedEnvironmentMembership
- propagate the managed environment naming and key changes across Filament resources, pages, controllers, jobs, models, and supporting runtime paths
- add feature 279 spec artifacts and focused managed-environment test coverage for model behavior, route binding, panel context, authorization, and legacy guardrails

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ManagedEnvironment/LegacyTenantCoreGuardTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentAuthorizationTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentPanelContextTest.php tests/Feature/ManagedEnvironment/ManagedEnvironmentRouteBindingTest.php tests/Unit/ManagedEnvironment/ManagedEnvironmentContextResolverTest.php tests/Unit/ManagedEnvironment/ManagedEnvironmentModelTest.php`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Notes
- branch pushed from commit `1123b122`
- browser smoke test file was added but not run in this pass

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #335
2026-05-07 06:38:14 +00:00

115 lines
3.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 ! ManagedEnvironmentMembership::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('role', 'owner')
->exists();
}
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;
}
$roles = $memberships->pluck('role')->all();
$roleToKeep = $this->highestRole($roles);
$membershipToKeep = $memberships->firstWhere('role', $roleToKeep) ?? $memberships->first();
if (! $membershipToKeep instanceof ManagedEnvironmentMembership) {
return;
}
$idsToDelete = $memberships
->reject(fn (ManagedEnvironmentMembership $m): bool => $m->getKey() === $membershipToKeep->getKey())
->pluck($membershipToKeep->getKeyName())
->all();
$membershipToKeep->forceFill([
'role' => $roleToKeep,
])->save();
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)),
'result_role' => $roleToKeep,
'source_roles' => $roles,
],
],
actorId: (int) $actor->getKey(),
actorEmail: $actor->email,
actorName: $actor->name,
status: 'success',
resourceType: 'tenant',
resourceId: (string) $tenant->getKey(),
);
});
}
/**
* @param array<int, string|null> $roles
*/
private function highestRole(array $roles): string
{
$priority = [
'owner' => 3,
'manager' => 2,
'readonly' => 1,
];
$bestRole = 'readonly';
$bestScore = 0;
foreach ($roles as $role) {
$score = $priority[$role] ?? 0;
if ($score > $bestScore) {
$bestScore = $score;
$bestRole = (string) $role;
}
}
return $bestRole;
}
}