singleloginsystem/app/Services/UserAppAccessService.php
2026-06-10 13:18:46 +00:00

143 lines
4.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services;
use App\Data\ConnectedApp\ConnectedAppData;
use App\Data\Ui\Dashboard\ServiceAppDto;
use App\Enums\ConnectedAppPermissonEnum;
use App\Helpers\AppPermission;
use App\Models\{ConnectedApp, RestrictedUserPermission, Role, User};
use Carbon\Carbon;
use Illuminate\Support\Collection;
class UserAppAccessService
{
/**
* Get all active services for the user as a collection of DTOs.
*
* @return Collection<int, ServiceAppDto>
*/
public function getActiveServices(User $user): Collection
{
$user->loadMissing(['roles.apps.protocol', 'roles.apps.provider', 'roles.permissions']);
$restrictedPermissions = $this->getRestrictedPermissions($user);
/** @var Collection<int, ServiceAppDto> $appsMap */
$appsMap = collect();
/** @var Collection<int, Role> $roles */
$roles = $user->roles;
foreach ($roles as $role) {
/** @var Collection<int, ConnectedApp> $apps */
$apps = $role->apps;
foreach ($apps as $app) {
if ($this->isAppRestrictedForRole($role, $app, $restrictedPermissions)) {
continue;
}
/** @var mixed $pivot */
$pivot = $app->pivot;
$durationStr = (string) $pivot->duration;
$daysRemaining = $this->getDaysRemainingIfActive($durationStr);
// If null is returned, the app has expired.
if (null === $daysRemaining) {
continue;
}
$this->addOrUpdateAppInMap($appsMap, $app, $durationStr, $daysRemaining);
}
}
return $appsMap->values();
}
/**
* Retrieves a map of restricted permissions for the user, grouped by role ID.
*
* * @return array<int, array<int, int>>
*/
private function getRestrictedPermissions(User $user): array
{
return RestrictedUserPermission::query()
->where('user_id', $user->id)
->get()
->groupBy('role_id')
->map(fn ($item) => $item->pluck('permission_id')->all())
->all();
}
/**
* Checks if the app is restricted for the given role.
*
* * @param array<int, array<int, int>> $restrictedPermissions
*/
private function isAppRestrictedForRole(Role $role, ConnectedApp $app, array $restrictedPermissions): bool
{
$permissionName = AppPermission::name(ConnectedAppPermissonEnum::Use, ConnectedAppData::from($app));
$permission = $role->permissions->firstWhere('name', $permissionName);
if (! $permission) {
return false;
}
return isset($restrictedPermissions[$role->id])
&& in_array($permission->id, $restrictedPermissions[$role->id], true);
}
/**
* Calculates the days remaining. Returns null if the duration has expired.
*/
private function getDaysRemainingIfActive(string $durationStr): ?int
{
$duration = Carbon::parse($durationStr);
$now = now();
if ($duration->isPast()) {
return null;
}
$daysRemaining = (int) $now->diffInDays($duration, false);
if (0 === $daysRemaining && $now->lt($duration)) {
return 1;
}
return $daysRemaining;
}
/**
* Adds the app to the map or updates it if the new duration is greater than the existing one.
*
* * @param Collection<int, ServiceAppDto> $appsMap
*/
private function addOrUpdateAppInMap(Collection $appsMap, ConnectedApp $app, string $durationStr, int $daysRemaining): void
{
/** @var ServiceAppDto|null $existing */
$existing = $appsMap->get($app->id);
$duration = Carbon::parse($durationStr);
$protocolName = $app->protocol?->name ? mb_strtolower($app->protocol->name) : null;
$providerSlug = $app->provider?->slug ? mb_strtolower($app->provider->slug) : null;
if (! $existing || Carbon::parse($existing->duration)->lt($duration)) {
$appsMap->put($app->id, new ServiceAppDto(
id: $app->id,
name: $app->name,
slug: $app->slug,
duration: $durationStr,
days_remaining: $daysRemaining,
is_warning: $daysRemaining <= 3,
protocol_name: $protocolName,
provider_slug: $providerSlug,
));
}
}
}