204 lines
7.4 KiB
PHP
204 lines
7.4 KiB
PHP
<?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;
|
|
}
|
|
}
|