From 010366f7f5cf93f9fe2104336ce86db8c6c79865 Mon Sep 17 00:00:00 2001 From: blaerf Date: Fri, 21 Nov 2025 06:37:55 +0100 Subject: [PATCH] =?UTF-8?q?LdapDirectoryService=20und=20UserManagementCont?= =?UTF-8?q?roller=20f=C3=BCr=20Benutzer=20und=20Gruppen=20Anzeige=20erstel?= =?UTF-8?q?lt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/UserManagementController.php | 153 ++++++++++++++ app/Services/Ldap/LdapDirectoryService.php | 203 +++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 app/Controllers/UserManagementController.php create mode 100644 app/Services/Ldap/LdapDirectoryService.php diff --git a/app/Controllers/UserManagementController.php b/app/Controllers/UserManagementController.php new file mode 100644 index 0000000..8b20a81 --- /dev/null +++ b/app/Controllers/UserManagementController.php @@ -0,0 +1,153 @@ + Vollständige Anwendungskonfiguration (aus config.php) */ + private array $config; + + /** @var LdapDirectoryService Service für das Lesen von Benutzern und Gruppen aus dem LDAP/AD */ + private LdapDirectoryService $directoryService; + + /** + * @param array $config Vollständige Konfiguration aus config.php + */ + public function __construct(array $config) + { + // Komplette Konfiguration speichern (falls später weitere Werte benötigt werden). + $this->config = $config; + + // LDAP-Konfiguration aus der Gesamt-Konfiguration herausziehen. + $ldapConfig = $config['ldap'] ?? []; + + // Directory-Service initialisieren, der die eigentliche LDAP-Arbeit übernimmt. + $this->directoryService = new LdapDirectoryService($ldapConfig); + } + + /** + * Zeigt Benutzer- und Gruppenliste an. + * Wird typischerweise über die Route "users" (index.php?route=users) aufgerufen. + */ + public function show(): void + { + // Standardwerte für die View-Variablen vorbereiten. + $error = null; + $users = []; + $groups = []; + + try { + // Benutzer- und Gruppenlisten aus dem AD laden. + $users = $this->directoryService->getUsers(); + $groups = $this->directoryService->getGroups(); + } catch (\Throwable $exception) { + // Sämtliche technischen Fehler (z. B. Verbindungs- oder Konfigurationsprobleme) + // werden hier in eine für den Benutzer lesbare Fehlermeldung übersetzt. + $error = 'Fehler beim Laden von Benutzern/Gruppen: ' . $exception->getMessage(); + } + + // Pfad zur eigentlichen View-Datei bestimmen. + $viewPath = __DIR__ . '/../../public/views/users.php'; + + // Falls die View-Datei (noch) nicht existiert, Fallback-Ausgabe verwenden. + if (file_exists($viewPath) === false) { + $this->renderInline($users, $groups, $error); + return; + } + + // Variablen $users, $groups, $error stehen in der View zur Verfügung, + // weil sie im aktuellen Scope definiert sind. + require $viewPath; + } + + /** + * Fallback-Ausgabe, falls noch keine View-Datei existiert. + * + * @param array> $users Liste der Benutzer-Datensätze + * @param array> $groups Liste der Gruppen-Datensätze + * @param string|null $error Fehlermeldung (falls vorhanden) + */ + private function renderInline(array $users, array $groups, ?string $error): void + { + ?> + + + + + AD Admin Tool – Benutzer & Gruppen + + +

Benutzer & Gruppen

+ + + +

+ + +

Benutzer

+ + + + + + + + + + + + + + + + + +
Benutzername (sAMAccountName)AnzeigenameE-Mail
+ +

Gruppen

+ + + + + + + + + + + + + + + + + +
Gruppenname (sAMAccountName)CNBeschreibung
+ +

+ Zurück zum Dashboard | + Logout +

+ + + + LDAP-Konfiguration (inkl. base_dn, bind_dn, bind_password) */ + private array $config; + + /** @var LdapConnectionHelper Zentrale Hilfsklasse für den Aufbau von LDAP-Verbindungen */ + private LdapConnectionHelper $connectionHelper; + + /** + * @param array $ldapConfig Teilbereich "ldap" aus der config.php + */ + public function __construct(array $ldapConfig) + { + // Vollständige LDAP-Konfiguration speichern. + $this->config = $ldapConfig; + + // Gemeinsamen Verbindungs-Helper initialisieren. + // Dieser kümmert sich um ldap_connect und die grundlegenden Optionen. + $this->connectionHelper = new LdapConnectionHelper($ldapConfig); + } + + /** + * Stellt eine LDAP-Verbindung her und bindet sich mit dem technischen Konto. + * + * @return resource LDAP-Verbindungs-Handle + * + * @throws RuntimeException wenn Bind-Daten fehlen oder der Bind fehlschlägt + */ + private function connect() + { + // Technischen Bind-DN (Service-Account) aus der Konfiguration lesen. + $bindDn = (string)($this->config['bind_dn'] ?? ''); + $bindPassword = (string)($this->config['bind_password'] ?? ''); + + // Ohne Bind-DN oder Passwort kann kein technischer Bind durchgeführt werden. + if ($bindDn === '' || $bindPassword === '') { + throw new RuntimeException('LDAP-Binddaten für technisches Konto sind nicht konfiguriert.'); + } + + // Verbindung über den zentralen Helper erstellen (Server, Port, Optionen). + $connection = $this->connectionHelper->createConnection(); + + // Mit dem technischen Konto binden, um Lesezugriffe durchführen zu können. + // Das @-Zeichen unterdrückt PHP-Warnings bei Fehlversuchen. + if (@ldap_bind($connection, $bindDn, $bindPassword) !== true) { + // Bei fehlgeschlagenem Bind Verbindung wieder schließen. + ldap_unbind($connection); + throw new RuntimeException('LDAP-Bind mit technischem Konto ist fehlgeschlagen.'); + } + + // Erfolgreich gebundene Verbindung zurückgeben. + return $connection; + } + + /** + * Liefert eine Liste von Benutzern aus dem AD. + * + * @return array> Liste von Benutzer-Datensätzen + * [ + * [ + * 'samaccountname' => 'user1', + * 'displayname' => 'User Eins', + * 'mail' => 'user1@example.local', + * ], + * ... + * ] + * + * @throws RuntimeException bei Konfigurations- oder Verbindungsproblemen + */ + public function getUsers(): array + { + // Base-DN ist der Startpunkt der Suche im Verzeichnisbaum (z. B. DC=...,DC=...). + $baseDn = (string)($this->config['base_dn'] ?? ''); + if ($baseDn === '') { + throw new RuntimeException('LDAP base_dn für Benutzersuche ist nicht konfiguriert.'); + } + + // Verbindung mit technischem Bind herstellen. + $connection = $this->connect(); + + // Standard-Filter für Benutzer in AD: + // - objectClass=user + // - objectCategory=person + $filter = '(&(objectClass=user)(objectCategory=person))'; + + // Nur relevante Attribute auslesen, um Datenmenge klein zu halten. + $attributes = ['sAMAccountName', 'displayName', 'mail']; + + // LDAP-Suche unterhalb des Base-DN durchführen. + $search = @ldap_search($connection, $baseDn, $filter, $attributes); + if ($search === false) { + // Bei einem Fehler die Verbindung schließen und Exception werfen. + ldap_unbind($connection); + throw new RuntimeException('LDAP-Suche nach Benutzern ist fehlgeschlagen.'); + } + + // Suchergebnisse in ein PHP-Array umwandeln. + $entries = ldap_get_entries($connection, $search); + + // Verbindung wieder schließen, da sie nicht mehr benötigt wird. + ldap_unbind($connection); + + // Ergebnisliste für die View vorbereiten. + $users = []; + + // Wenn die Struktur unerwartet ist, leere Liste zurückgeben. + if (!is_array($entries) || !isset($entries['count'])) { + return $users; + } + + // Jeden einzelnen Eintrag aus dem Ergebnis verarbeiten. + for ($i = 0; $i < $entries['count']; $i++) { + $entry = $entries[$i]; + + // Attribute sind im Ergebnis verschachtelt, daher Zugriff über [...][0]. + $users[] = [ + 'samaccountname' => isset($entry['samaccountname'][0]) ? (string)$entry['samaccountname'][0] : '', + 'displayname' => isset($entry['displayname'][0]) ? (string)$entry['displayname'][0] : '', + 'mail' => isset($entry['mail'][0]) ? (string)$entry['mail'][0] : '', + ]; + } + + return $users; + } + + /** + * Liefert eine Liste von Gruppen aus dem AD. + * + * @return array> Liste von Gruppen-Datensätzen + * + * @throws RuntimeException bei Konfigurations- oder Verbindungsproblemen + */ + public function getGroups(): array + { + // Base-DN für die Gruppensuche aus der Konfiguration lesen. + $baseDn = (string)($this->config['base_dn'] ?? ''); + if ($baseDn === '') { + throw new RuntimeException('LDAP base_dn für Gruppensuche ist nicht konfiguriert.'); + } + + // Verbindung mit technischem Bind herstellen. + $connection = $this->connect(); + + // Filter für Gruppenobjekte im AD. + $filter = '(objectClass=group)'; + + // Attribute, die für die Darstellung relevant sind. + $attributes = ['sAMAccountName', 'cn', 'description']; + + // LDAP-Suche ausführen. + $search = @ldap_search($connection, $baseDn, $filter, $attributes); + if ($search === false) { + ldap_unbind($connection); + throw new RuntimeException('LDAP-Suche nach Gruppen ist fehlgeschlagen.'); + } + + // Ergebnisse holen. + $entries = ldap_get_entries($connection, $search); + + // Verbindung schließen. + ldap_unbind($connection); + + $groups = []; + + // Struktur prüfen, leere Liste zurückgeben, falls unerwartet. + if (!is_array($entries) || !isset($entries['count'])) { + return $groups; + } + + // Alle Einträge in ein flacheres Array-Format transformieren. + for ($i = 0; $i < $entries['count']; $i++) { + $entry = $entries[$i]; + + $groups[] = [ + 'samaccountname' => isset($entry['samaccountname'][0]) ? (string)$entry['samaccountname'][0] : '', + 'cn' => isset($entry['cn'][0]) ? (string)$entry['cn'][0] : '', + 'description' => isset($entry['description'][0]) ? (string)$entry['description'][0] : '', + ]; + } + + return $groups; + } +}