LDAP-Benutzer-Export (schnelle DB-Abfragen)¶
Live-Abfragen gegen ein LDAP-Verzeichnis sind bei vielen Nutzern spürbar langsam. Der Benutzer-Export spiegelt Benutzer, Gruppen und Mitgliedschaften periodisch in Datenbank-Tabellen — Abfragen laufen dann in Millisekunden über die DB statt über LDAP.
Wann lohnt sich das?
Immer, wenn dieselben Verzeichnis-Daten häufig gelesen werden (Picker, Auswahllisten, Reports) und ein kurzer Nachlauf von wenigen Minuten/Stunden akzeptabel ist. Für sicherheitskritische Live-Prüfungen (z.B. Login-Bind) bleibt der direkte LDAP-Zugriff.
Zieltabellen¶
| Tabelle | Inhalt |
|---|---|
exported_user |
eine Zeile je Person: Benutzerkennung, Anrede, Vor-/Nachname, E-Mail, Telefon/Mobil, Personalnummer, Titel, Abteilung, Standort, groups_csv (denormalisierte Gruppenliste), extra_json u.a. |
exported_group |
Gruppen (cn, Beschreibung, E-Mail, Mitgliederzahl) |
exported_user_group |
n:m-Mitgliedschaften (für echte Joins/Filter) |
user_export_run |
Lauf-Protokoll/Statistik je Sync |
Jede Zeile trägt active (Soft-Delete), last_seen_at, attr_hash, Zeitstempel
und den stabilen Schlüssel entry_uuid.
So funktioniert der Sync¶
Full-Sync mit Mark-and-Sweep (angelehnt an bewährte Verfahren wie Keycloaks User-Federation):
- Paged Search (RFC 2696) über den Konnektor liest alle Benutzer + Gruppen.
- Stabiler Schlüssel
entryUUID(RFC 4530) — übersteht Umbenennen/Verschieben im Verzeichnis, ohne Duplikate oder Waisen zu erzeugen. - Attribut-Hash erkennt Änderungen: unveränderte Sätze werden nur „berührt" (kein unnötiger Schreibvorgang) → die Statistik zeigt neu/geändert/unverändert.
- Soft-Delete: im Verzeichnis nicht mehr gefundene Sätze werden auf
active=0gesetzt (mitdeleted_at), nicht gelöscht — für Audit und Wiederauftauchen. - Ausfall-Schutz: liefert der Lauf 0 Treffer oder bricht die Trefferzahl stark ein (Standard > 50 %), wird der Sweep ausgesetzt — die Tabelle wird nie durch einen LDAP-Ausfall geleert.
Gelöscht + neu angelegt = neue Identität
Wird ein Verzeichnis-Eintrag gelöscht und neu angelegt, vergibt der Server
eine neue entryUUID. Der Export führt dann einen neuen aktiven Satz;
der alte bleibt als active=0 erhalten (Historie).
Auslösen¶
Manuell: unter System-Administration → Schnittstellen & Verteilung → LDAP-Konnektoren den Konnektor öffnen → Bereich „Benutzer-Export" → Jetzt synchronisieren. Darunter erscheinen die Statistik des Laufs und eine kurze Lauf-Historie.
Geplant (empfohlen): direkt im Panel unter Zeitplan — Manuell /
Intervall / Cron wählen (bei Intervall: Zahl + Einheit; bei Cron: Ausdruck +
„nächster Lauf"-Vorschau). Das legt im Hintergrund einen verwalteten
Zeitplaner-Job „LDAP-Export: <Konnektor>" an, der
ldap.sync("<Konnektor>") ausführt — er taucht auch in der Zeitplaner-
Verwaltung auf. Bewährt: ein nächtlicher Full-Sync; die Läufe sind
idempotent und dank Attribut-Hash günstig.
Alternativ per Skript (z.B. in einem eigenen Job): ldap.sync(name) gibt eine
Statistik zurück (status, usersScanned, added, updated, unchanged,
removed, groups, memberships, errors, skipped, durationMs,
sweepSkipped, message); gleichbedeutend ldap.exportUsers(name).
Schnell lesen¶
Die Tabellen liegen in der KCForms-Datenbank — direkt per
db oder als Lookup-/Picker-Quelle nutzbar:
// aktive Softwareentwickler in Stockholm (Millisekunden statt LDAP-Sekunden)
var rows = db.query(
"SELECT login_name, display_name, email, phone, employee_number " +
"FROM exported_user WHERE department = ${abt} AND office = ${ort} AND active = 1 " +
"ORDER BY last_name",
{ abt: "Softwareentwicklung", ort: "Stockholm" });
// Mitglieder einer Gruppe
var team = db.query(
"SELECT u.display_name, u.email FROM exported_user_group ug " +
"JOIN exported_user u ON u.connector_id = ug.connector_id AND u.entry_uuid = ug.user_entry_uuid " +
"WHERE ug.group_cn = ${gruppe} AND ug.active = 1", { gruppe: "Abteilung Beratung" });
Immer auf active = 1 filtern, wenn nur aktuelle Einträge gewünscht sind.
Konfiguration¶
Alles wird im Bereich „Benutzer-Export" des Konnektors gepflegt — gegliedert wie folgt:
- Synchronisation aktiv — Master-Schalter ganz oben. Ist er aus, werden
die Detailfelder ausgeblendet und kein Sync läuft mehr (weder manuell noch
per Zeitplan oder Skript
ldap.sync(...)) — die gepflegte Attribut-Zuordnung bleibt aber gespeichert und ist beim Wiedereinschalten sofort wieder da. Zum Übernehmen Speichern. (Deaktivieren pausiert auch den Zeitplaner-Job.) - Quelle & Umfang — Benutzer-Basis (Base-DN) & -Filter, Seitengröße, Schalter „Gruppen + Mitgliedschaften mitsynchronisieren" (+ Gruppen-Basis/-Filter).
- Attribut-Zuordnung — Tabelle „Spalte ← LDAP-Attribut" (Benutzerkennung,
Anrede, Vor-/Nachname, E-Mail, Telefon, Personalnummer, Titel, Abteilung,
Standort, Vorgesetzter, Sprache …), je Feld mit Autocomplete; der Button
„Beispiel-Attribute laden" liest einen echten Eintrag und schlägt dessen
Attribute vor. Zusätzliche Attribute landen in
extra_json. - Zeitplan — Manuell / Intervall / Cron (siehe oben).
- Löschverhalten & Robustheit — Schwelle für den Ausfall-Schutz (Sweep aussetzen bei Treffer-Einbruch).
-
Benachrichtigung — optionale Zusammenfassungs-Mail nach jedem Lauf (Empfänger als Komma-Liste). Mit „Nur bei Änderungen oder Problemen mailen" bleibt es bei ereignislosen Läufen still. Leer = keine Mail. Versand über den Mail-Ausgang; der Empfänger muss die Empfänger-Whitelist erfüllen.
Die Zusammenfassung nennt Fehler und übersprungene Einträge explizit: Betreff-Zusatz
⚠ N Fehler/M übersprungen, im Text eine eigene Zeile plus die ersten Fehler-Beispiele (Hinweis: …). Einzelne beschädigte Verzeichniseinträge (unlesbar oder ohneentryUUID) brechen den Lauf nicht ab — sie werden gezählt, übersprungen und in der Mail gemeldet; der übrige Export läuft normal durch. Ein harter Abbruch (z.B. LDAP nicht erreichbar) erscheint alsStatus: failedmit— FEHLERim Betreff.
Leere Felder = Defaults (Basis = Konnektor-Base-DN, Filter
(objectClass=inetOrgPerson) bzw. (objectClass=groupOfNames), Standard-Mapping
gängiger inetOrgPerson-Attribute). Zwei Speicher-Aktionen: Speichern und
Speichern & jetzt synchronisieren.
Anrede
Eine Anrede kennt Standard-LDAP i.d.R. nicht. Der Export füllt sie nur,
wenn ein Attribut über salutationAttr benannt ist und am Eintrag existiert —
sonst bleibt sie leer (keine Rate-Heuristik).
Dubletten vermeiden & bereinigen¶
In exported_user steht jede Person genau einmal je Konnektor (Schlüssel
connector_id + entry_uuid). „Doppelte" Zeilen entstehen daher nur aus zwei
Gründen — für beide gibt es eine Mechanik:
- Zwei Konnektoren exportieren dieselbe Basis-DN → jede Person steht je Konnektor einmal. Der Überlappungs-Wächter warnt beim Öffnen/Speichern des Panels, wenn ein anderer aktiver Export dieselbe Benutzer-Basis abzieht. Lösung: bei einem der Konnektoren Synchronisation aus (Master-Schalter) und dessen Daten mit „Export dieses Konnektors leeren" entfernen.
- Soft-Delete-Tombstones (
active=0) — nach Rename/Neuanlage im LDAP (neueentryUUID) bleibt der alte Satz als deaktivierte Historie stehen. „Verwaiste Sätze entfernen" löscht dieseactive=0-Zeilen endgültig; die aktiven bleiben.
Beide Aktionen sitzen im Panel unter Bereinigung. Beim normalen Lesen ohnehin
WHERE active=1 und einen connector_id filtern.
Betrieb & Sicherheit¶
- Read-only Service-Account im Konnektor hinterlegen (der Export bindet nur lesend); nach Möglichkeit LDAPS.
- Läufe sind idempotent; Teilfehler brechen nicht die ganze Tabelle ab (bei einem Fehler wird der Sweep übersprungen).
- Die Statistik je Lauf steht in
user_export_runund im Admin-Panel — auffälligesweep_skipped-Läufe oder Treffer-Einbrüche sind das Frühwarnsignal.
Siehe auch¶
- Zeitplaner — den Sync einplanen
- LDAP-Scripting — Live-Zugriff (
ldap.connect,ldap.sync) db-Modul — die Export-Tabellen abfragen