LdapDirectoryService und UserManagementController für Benutzer und Gruppen Anzeige erstellt.
This commit is contained in:
parent
86b7982352
commit
010366f7f5
153
app/Controllers/UserManagementController.php
Normal file
153
app/Controllers/UserManagementController.php
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Services\Ldap\LdapDirectoryService;
|
||||
|
||||
/**
|
||||
* Controller für die Benutzer- und Gruppenanzeige.
|
||||
*
|
||||
* Aufgaben:
|
||||
* - holt über den LdapDirectoryService die Listen von Benutzern und Gruppen
|
||||
* - behandelt technische Fehler und bereitet eine Fehlermeldung für die View auf
|
||||
* - gibt die Daten an eine View-Datei (public/views/users.php) weiter
|
||||
*
|
||||
* WICHTIG:
|
||||
* - Es werden aktuell nur Daten angezeigt (Read-only).
|
||||
* - Es findet keine Änderung im Active Directory statt.
|
||||
*/
|
||||
class UserManagementController
|
||||
{
|
||||
/** @var array<string, mixed> 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<string, mixed> $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<int, array<string, string>> $users Liste der Benutzer-Datensätze
|
||||
* @param array<int, array<string, string>> $groups Liste der Gruppen-Datensätze
|
||||
* @param string|null $error Fehlermeldung (falls vorhanden)
|
||||
*/
|
||||
private function renderInline(array $users, array $groups, ?string $error): void
|
||||
{
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>AD Admin Tool – Benutzer & Gruppen</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Benutzer & Gruppen</h1>
|
||||
|
||||
<?php if ($error !== null): ?>
|
||||
<!-- Fehlermeldung ausgeben, HTML-sicher maskiert -->
|
||||
<p style="color: red;"><?php echo htmlspecialchars($error, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>Benutzer</h2>
|
||||
<table border="1" cellpadding="4" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Benutzername (sAMAccountName)</th>
|
||||
<th>Anzeigename</th>
|
||||
<th>E-Mail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($user['samaccountname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['displayname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['mail'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Gruppen</h2>
|
||||
<table border="1" cellpadding="4" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppenname (sAMAccountName)</th>
|
||||
<th>CN</th>
|
||||
<th>Beschreibung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($group['samaccountname'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($group['cn'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
<td><?php echo htmlspecialchars($group['description'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="index.php?route=dashboard">Zurück zum Dashboard</a> |
|
||||
<a href="index.php?route=logout">Logout</a>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
203
app/Services/Ldap/LdapDirectoryService.php
Normal file
203
app/Services/Ldap/LdapDirectoryService.php
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Ldap;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Service zum Lesen von Objekten aus dem Active Directory.
|
||||
*
|
||||
* Aktueller Umfang:
|
||||
* - Liste von Benutzern (sAMAccountName, displayName, mail)
|
||||
* - Liste von Gruppen (sAMAccountName, cn, description)
|
||||
*
|
||||
* Technische Details:
|
||||
* - Verwendet ein technisches Konto (bind_dn + bind_password) für Lesezugriffe
|
||||
* - Nutzt LdapConnectionHelper zum Aufbau der Verbindung
|
||||
*/
|
||||
class LdapDirectoryService
|
||||
{
|
||||
/** @var array<string, mixed> 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<string, mixed> $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<int, array<string, string>> 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<int, array<string, string>> 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user