*/ #[NoDiscard] public function getAll(): PaginatedDataCollection { $users = User::query() ->with([ 'roles:id,name,priority', 'roles.apps:id,name', 'roles.permissions:id,name', 'restrictedPermissions', ]) ->orderBy('id') ->paginate(); return UserIndexData::collect($users, PaginatedDataCollection::class); } /** * Get the restricted permission IDs for a user. * * @return array */ public function getRestrictedPermissionIds(int $userId): array { if ($userId <= 0) { return []; } return RestrictedUserPermission::query() ->where('user_id', $userId) ->pluck('permission_id') ->toArray(); } /** * Assign roles to a user. */ public function assignRoles(int $userId, array $roleIds): void { if ($userId <= 0) { throw new InvalidArgumentException('Invalid id'); } DB::transaction(function () use ($userId, $roleIds): void { $user = User::query()->findOrFail($userId); $userPriority = auth()->user()?->roles()->min('priority') ?? 999999; // Get user's existing roles that have priority <= $userPriority (which are forbidden to the logged in user) $forbiddenRoleIds = $user->roles() ->where('priority', '<=', $userPriority) ->pluck('id') ->toArray(); // Merge them with the newly assigned roles so they are preserved $mergedRoleIds = array_values(array_unique(array_merge($forbiddenRoleIds, $roleIds))); $user->syncRoles($mergedRoleIds); }); } /** * Sync restricted permissions for a user. * * @param DataCollection $deactivatedPermissions * * @throws Throwable */ public function syncRestrictedPermissions(int $userId, DataCollection $deactivatedPermissions): void { if ($userId <= 0) { throw new InvalidArgumentException('Invalid user ID'); } $payload = $deactivatedPermissions->toCollection()->map(fn ($data) => [ 'user_id' => $userId, 'role_id' => $data->roleId, 'permission_id' => $data->permissionId, ])->toArray(); DB::transaction(function () use ($userId, $payload): void { RestrictedUserPermission::query() ->where('user_id', $userId) ->delete(); if (! empty($payload)) { RestrictedUserPermission::query()->insert($payload); } }); } /** * @throws InvalidArgumentException when id is invalid. * @throws Throwable when an error occurs during the deletion. */ public function delete(int $id): void { if ($id <= 0) { throw new InvalidArgumentException('Invalid id'); } DB::transaction(function () use ($id): void { $user = User::query()->findOrFail($id); $userPriority = auth()->user()?->roles()->min('priority') ?? 999999; $targetUserMinPriority = $user->roles()->min('priority') ?? 999999; if ($targetUserMinPriority <= $userPriority) { abort(403, 'Unauthorized to delete this user due to role priority hierarchy.'); } $user->delete(); }); } }