M15 – Quellenarten und Microsoft-Graph-Quelle #48

Closed
opened 2026-10-04 16:19:16 +00:00 by florian.wilde · 0 comments
Owner

Ziel

Ein Haus ohne lokales AD hat kein LDAP, auf das sich zeigen ließe. Für reine Entra-ID-Umgebungen
braucht es eine zweite Art von Verzeichnisquelle, die über Microsoft Graph liest — mit denselben
Regeln wie der LDAP-Abgleich: anlegen, aktualisieren, deaktivieren, nie löschen.

Dieser Meilenstein legt dazu die Weiche, die auch #029 braucht:
Verzeichnisquelle bekommt eine Art. Heute ist jede Quelle implizit LDAP.

Zu entscheiden

  • Ein Enum QuellenArt {LDAP GRAPH SCIM} auf Verzeichnisquelle — Empfehlung. Die Felder
    url, bindDn und basisDn sind dann je Art unterschiedlich belegt; was eine Art braucht,
    prüft der Service, nicht die Datenbank. Die Alternative wäre eine Tabelle je Art mit
    gemeinsamer Oberklasse — mehr Struktur, als drei Arten rechtfertigen.
  • Wie {{ou}} in Entra ID aussieht. Es gibt keinen DN und keine OU. Ehrlich ist, den
    Platzhalter bei einer Graph-Quelle leer zu lassen, statt etwas hineinzudeuten — und das
    in der Anleitung zu sagen. Wer OU-abhängige Befehle fährt, braucht ein lokales AD.

Aufgaben

  • QuellenArt als Enum, Migration mit LDAP für alle bestehenden Zeilen
  • Graph-Zugang: tenantId, clientId, clientSecret (verschlüsselt wie das Bind-Passwort, nie
    in einer Antwort), Token über Client-Credentials-Flow, Zwischenspeicher bis kurz vor Ablauf
  • Lesen über GET /v1.0/users mit $select, Seitenabruf über @odata.nextLink; der
    Vorgesetzte über $expand=manager($select=id)
  • Attributzuordnung mit Graph-Vorgaben (givenName, surname, displayName, mail,
    userPrincipalName, onPremisesSamAccountName, department, jobTitle, accountEnabled)
  • externeId ist die Entra-id (GUID). Sie ist stabil und übersteht eine Umbenennung
  • „Verbindung testen“ wie bei LDAP: die ersten zehn zugeordneten Treffer zeigen
  • Der Abgleich läuft durch denselben Dienst wie LDAP — nur das Holen der Einträge
    unterscheidet sich. Sonst driften die Regeln auseinander
  • Admin → Verzeichnis zeigt die Art je Quelle und bietet beim Anlegen die Wahl
  • Anleitung Kapitel 06 und docs/betrieb/: was in Entra ID einzurichten ist
    (App-Registrierung, User.Read.All, Directory.Read.All, Administratoreinwilligung)

Akzeptanzkriterien

  • Test gegen einen abspielenden HTTP-Doppelgänger mit aufgezeichneten Graph-Antworten: erster
    Abgleich legt an, zweiter aktualisiert ohne Dubletten, ein accountEnabled: false setzt
    aktiv = false
  • Test: der Seitenabruf folgt @odata.nextLink über mindestens drei Seiten
  • Test: clientSecret erscheint in keiner Antwort und in keinem Protokolleintrag
  • Test: eine Graph-Quelle lässt {{ou}} leer, statt einen Wert zu erfinden
  • Der LDAP-Weg verhält sich unverändert — alle Tests aus #016 bleiben grün
  • Offen und benannt: gegen einen echten Entra-Mandanten ist das nicht gelaufen; die
    Gegenprobe gehört in eine Prüfliste wie bei #024

Quelle im Repo: docs/issues/028-graph-quelle.md

## Ziel Ein Haus ohne lokales AD hat kein LDAP, auf das sich zeigen ließe. Für reine Entra-ID-Umgebungen braucht es eine zweite Art von Verzeichnisquelle, die über Microsoft Graph liest — mit denselben Regeln wie der LDAP-Abgleich: anlegen, aktualisieren, deaktivieren, **nie löschen**. Dieser Meilenstein legt dazu die Weiche, die auch [#029](029-scim-endpunkt.md) braucht: `Verzeichnisquelle` bekommt eine **Art**. Heute ist jede Quelle implizit LDAP. ## Zu entscheiden - [ ] **Ein Enum `QuellenArt {LDAP GRAPH SCIM}` auf `Verzeichnisquelle`** — Empfehlung. Die Felder `url`, `bindDn` und `basisDn` sind dann je Art unterschiedlich belegt; was eine Art braucht, prüft der Service, nicht die Datenbank. Die Alternative wäre eine Tabelle je Art mit gemeinsamer Oberklasse — mehr Struktur, als drei Arten rechtfertigen. - [ ] **Wie `{{ou}}` in Entra ID aussieht.** Es gibt keinen DN und keine OU. Ehrlich ist, den Platzhalter bei einer Graph-Quelle **leer** zu lassen, statt etwas hineinzudeuten — und das in der Anleitung zu sagen. Wer OU-abhängige Befehle fährt, braucht ein lokales AD. ## Aufgaben - [ ] `QuellenArt` als Enum, Migration mit `LDAP` für alle bestehenden Zeilen - [ ] Graph-Zugang: `tenantId`, `clientId`, `clientSecret` (verschlüsselt wie das Bind-Passwort, nie in einer Antwort), Token über Client-Credentials-Flow, Zwischenspeicher bis kurz vor Ablauf - [ ] Lesen über `GET /v1.0/users` mit `$select`, Seitenabruf über `@odata.nextLink`; der Vorgesetzte über `$expand=manager($select=id)` - [ ] Attributzuordnung mit Graph-Vorgaben (`givenName`, `surname`, `displayName`, `mail`, `userPrincipalName`, `onPremisesSamAccountName`, `department`, `jobTitle`, `accountEnabled`) - [ ] `externeId` ist die Entra-`id` (GUID). Sie ist stabil und übersteht eine Umbenennung - [ ] „Verbindung testen“ wie bei LDAP: die ersten zehn zugeordneten Treffer zeigen - [ ] Der Abgleich läuft durch **denselben** Dienst wie LDAP — nur das Holen der Einträge unterscheidet sich. Sonst driften die Regeln auseinander - [ ] Admin → Verzeichnis zeigt die Art je Quelle und bietet beim Anlegen die Wahl - [ ] Anleitung Kapitel 06 und `docs/betrieb/`: was in Entra ID einzurichten ist (App-Registrierung, `User.Read.All`, `Directory.Read.All`, Administratoreinwilligung) ## Akzeptanzkriterien - [ ] Test gegen einen **abspielenden HTTP-Doppelgänger** mit aufgezeichneten Graph-Antworten: erster Abgleich legt an, zweiter aktualisiert ohne Dubletten, ein `accountEnabled: false` setzt `aktiv = false` - [ ] Test: der Seitenabruf folgt `@odata.nextLink` über mindestens drei Seiten - [ ] Test: `clientSecret` erscheint in keiner Antwort und in keinem Protokolleintrag - [ ] Test: eine Graph-Quelle lässt `{{ou}}` leer, statt einen Wert zu erfinden - [ ] Der LDAP-Weg verhält sich unverändert — alle Tests aus #016 bleiben grün - [ ] **Offen und benannt:** gegen einen echten Entra-Mandanten ist das nicht gelaufen; die Gegenprobe gehört in eine Prüfliste wie bei [#024](024-proxmox-echtlauf.md) --- Quelle im Repo: `docs/issues/028-graph-quelle.md`
florian.wilde added this to the v0.2.0 milestone 2026-10-04 16:19:16 +00:00
florian.wilde added the phase-2backendfrontend labels 2026-10-04 16:19:16 +00:00
Sign in to join this conversation.