*/ public function getActiveServices(User $user): Collection { $user->loadMissing(['roles.apps.protocol', 'roles.apps.provider', 'roles.permissions']); $restrictedPermissions = $this->getRestrictedPermissions($user); /** @var Collection $appsMap */ $appsMap = collect(); /** @var Collection $roles */ $roles = $user->roles; foreach ($roles as $role) { /** @var Collection $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> */ 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> $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 $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, )); } } }