sortParam($request, ['username', 'display_name', 'email', 'phone', 'enabled', 'last_logon', 'groups', 'groups_count'], 'username'); $dir = $request->string('dir', 'asc')->toString() === 'desc' ? 'desc' : 'asc'; $search = $request->string('q')->trim()->toString(); $groupFilter = $request->string('group')->trim()->toString(); $result = $this->ldapService->listUsers($sort, $dir, $search, $groupFilter); $users = $this->metadata->enrichUsers($result['rows']); if ($search !== '') { $ldapUsernames = array_column($users, 'username'); foreach ($this->metadata->findUsernamesByMetaSearch($search) as $username) { if (in_array($username, $ldapUsernames, true)) { continue; } try { $user = $this->ldapService->getUser($username); $users[] = array_merge($user, $this->metadata->getUserMeta($username)); } catch (InvalidArgumentException) { } } usort($users, fn ($a, $b) => strcmp($a['username'], $b['username'])); } if (in_array($sort, ['phone'], true)) { usort($users, function (array $a, array $b) use ($sort, $dir) { $cmp = ($a[$sort] ?? '') <=> ($b[$sort] ?? ''); return $dir === 'desc' ? -$cmp : $cmp; }); } return view('ldap-users.index', [ 'users' => $users, 'meta' => array_merge($result['meta'], ['total' => count($users)]), 'sort' => $sort, 'dir' => $dir, 'search' => $search, 'groupFilter' => $groupFilter, 'error' => $result['error'] ?? null, ]); } public function show(string $username): View|RedirectResponse { try { $user = $this->ldapService->getUser($username); $meta = $this->metadata->getUserMeta($username); $user = array_merge($user, $meta); $activity = $this->audit->listForUser($username, 50); return view('ldap-users.show', [ 'user' => $user, 'activityLogs' => $activity['rows'], 'activityError' => $activity['error'], ]); } catch (InvalidArgumentException $e) { return redirect()->route('ldap-users.index')->with('error', $e->getMessage()); } } public function edit(string $username): View|RedirectResponse { try { $user = $this->ldapService->getUser($username); $user = array_merge($user, $this->metadata->getUserMeta($username)); $groupsResult = $this->groupService->listGroups('name', 'asc'); $allGroups = $groupsResult['rows'] ?? []; $groupsByName = []; foreach ($allGroups as $group) { $groupsByName[$group['name']] = $group; } $currentGroups = []; foreach ($user['groups'] as $groupName) { $currentGroups[] = $groupsByName[$groupName] ?? [ 'name' => $groupName, 'route_key' => rawurlencode($groupName), 'protected' => true, ]; } $availableGroups = array_values(array_filter( $allGroups, fn (array $group) => ! $group['protected'] && ! in_array($group['name'], $user['groups'], true) )); return view('ldap-users.edit', [ 'user' => $user, 'currentGroups' => $currentGroups, 'availableGroups' => $availableGroups, ]); } catch (InvalidArgumentException $e) { return redirect()->route('ldap-users.index')->with('error', $e->getMessage()); } } public function update(Request $request, string $username): RedirectResponse { $data = $request->validate([ 'display_name' => ['required', 'string', 'max:255'], 'email' => ['nullable', 'email', 'max:255'], 'other_email' => ['nullable', 'email', 'max:255'], 'phone' => ['nullable', 'string', 'max:32'], 'admin_note' => ['nullable', 'string', 'max:5000'], ]); try { $this->ldapService->updateProfile($username, $data['display_name'], $data['email'] ?? null); $this->metadata->saveUserMeta($username, [ 'other_email' => $data['other_email'] ?? null, 'phone' => $data['phone'] ?? null, 'admin_note' => $data['admin_note'] ?? null, ]); $request->attributes->set('audit_metadata', [ 'username' => $username, 'display_name' => $data['display_name'], ]); return redirect()->route('ldap-users.show', $username)->with('success', "User [{$username}] updated."); } catch (InvalidArgumentException|RuntimeException $e) { return back()->withInput()->with('error', $e->getMessage()); } catch (Throwable) { return back()->withInput()->with('error', 'Failed to update user.'); } } public function toggleEnabled(Request $request, string $username): RedirectResponse { $request->validate(['confirm' => ['required', 'accepted']]); try { $user = $this->ldapService->getUser($username); $this->ldapService->setEnabled($username, ! $user['enabled']); $state = $user['enabled'] ? 'disabled' : 'enabled'; $request->attributes->set('audit_metadata', [ 'username' => $username, 'enabled' => ! $user['enabled'], ]); return redirect()->route('ldap-users.index')->with('success', "User [{$username}] is now {$state}."); } catch (InvalidArgumentException|RuntimeException $e) { return redirect()->route('ldap-users.index')->with('error', $e->getMessage()); } catch (Throwable) { return redirect()->route('ldap-users.index')->with('error', 'Failed to update account status.'); } } public function showPasswordForm(string $username): View|RedirectResponse { try { $user = $this->ldapService->getUser($username); if ($user['protected']) { return redirect()->route('ldap-users.index')->with('error', 'This account is protected.'); } return view('ldap-users.password', compact('user')); } catch (InvalidArgumentException $e) { return redirect()->route('ldap-users.index')->with('error', $e->getMessage()); } } public function addToGroup(Request $request, string $username): RedirectResponse { $data = $request->validate([ 'group' => ['required', 'string', 'max:255'], ]); try { $this->groupService->addMember($data['group'], $username); $request->attributes->set('audit_metadata', [ 'username' => $username, 'group' => $data['group'], ]); return redirect() ->route('ldap-users.edit', $username) ->with('success', "User [{$username}] added to group."); } catch (InvalidArgumentException|RuntimeException $e) { return back()->with('error', $e->getMessage()); } catch (Throwable) { return back()->with('error', 'Failed to add user to group.'); } } public function removeFromGroup(Request $request, string $username, string $group): RedirectResponse { $request->validate(['confirm' => ['required', 'accepted']]); try { $this->groupService->removeMember($group, $username); $request->attributes->set('audit_metadata', [ 'username' => $username, 'group' => $group, ]); return redirect() ->route('ldap-users.edit', $username) ->with('success', "User [{$username}] removed from group."); } catch (InvalidArgumentException|RuntimeException $e) { return back()->with('error', $e->getMessage()); } catch (Throwable) { return back()->with('error', 'Failed to remove user from group.'); } } public function resetPassword(Request $request, string $username): RedirectResponse { $data = $request->validate([ 'password' => ['required', 'string', 'min:8', 'max:128', 'confirmed'], 'confirm_action' => ['required', 'accepted'], 'confirm_username' => ['required', 'string'], ]); if (mb_strtolower($data['confirm_username']) !== mb_strtolower($username)) { return back()->withInput()->with('error', 'Username confirmation does not match.'); } try { $this->ldapService->resetPassword($username, $data['password']); $this->passwordReset->notifyPasswordChanged($username, session('ldap_username')); $request->attributes->set('audit_metadata', ['username' => $username]); return redirect()->route('ldap-users.index')->with('success', "Password for [{$username}] has been reset."); } catch (InvalidArgumentException|RuntimeException $e) { return back()->withInput()->with('error', $e->getMessage()); } catch (Throwable) { return back()->withInput()->with('error', 'Password reset failed.'); } } private function sortParam(Request $request, array $allowed, string $default): string { $sort = $request->string('sort', $default)->toString(); return in_array($sort, $allowed, true) ? $sort : $default; } }