Zum Inhalt

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):

  1. Paged Search (RFC 2696) über den Konnektor liest alle Benutzer + Gruppen.
  2. Stabiler Schlüssel entryUUID (RFC 4530) — übersteht Umbenennen/Verschieben im Verzeichnis, ohne Duplikate oder Waisen zu erzeugen.
  3. Attribut-Hash erkennt Änderungen: unveränderte Sätze werden nur „berührt" (kein unnötiger Schreibvorgang) → die Statistik zeigt neu/geändert/unverändert.
  4. Soft-Delete: im Verzeichnis nicht mehr gefundene Sätze werden auf active=0 gesetzt (mit deleted_at), nicht gelöscht — für Audit und Wiederauftauchen.
  5. 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("&lt;Konnektor&gt;") 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 ohne entryUUID) 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 als Status: failed mit — FEHLER im 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 (neue entryUUID) bleibt der alte Satz als deaktivierte Historie stehen. „Verwaiste Sätze entfernen" löscht diese active=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_run und im Admin-Panel — auffällige sweep_skipped-Läufe oder Treffer-Einbrüche sind das Frühwarnsignal.

Siehe auch