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)
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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:
Verzeichnisquellebekommt eine Art. Heute ist jede Quelle implizit LDAP.Zu entscheiden
QuellenArt {LDAP GRAPH SCIM}aufVerzeichnisquelle— Empfehlung. Die Felderurl,bindDnundbasisDnsind 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.
{{ou}}in Entra ID aussieht. Es gibt keinen DN und keine OU. Ehrlich ist, denPlatzhalter 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
QuellenArtals Enum, Migration mitLDAPfür alle bestehenden ZeilentenantId,clientId,clientSecret(verschlüsselt wie das Bind-Passwort, niein einer Antwort), Token über Client-Credentials-Flow, Zwischenspeicher bis kurz vor Ablauf
GET /v1.0/usersmit$select, Seitenabruf über@odata.nextLink; derVorgesetzte über
$expand=manager($select=id)givenName,surname,displayName,mail,userPrincipalName,onPremisesSamAccountName,department,jobTitle,accountEnabled)externeIdist die Entra-id(GUID). Sie ist stabil und übersteht eine Umbenennungunterscheidet sich. Sonst driften die Regeln auseinander
docs/betrieb/: was in Entra ID einzurichten ist(App-Registrierung,
User.Read.All,Directory.Read.All, Administratoreinwilligung)Akzeptanzkriterien
Abgleich legt an, zweiter aktualisiert ohne Dubletten, ein
accountEnabled: falsesetztaktiv = false@odata.nextLinküber mindestens drei SeitenclientSecreterscheint in keiner Antwort und in keinem Protokolleintrag{{ou}}leer, statt einen Wert zu erfindenGegenprobe gehört in eine Prüfliste wie bei #024
Quelle im Repo:
docs/issues/028-graph-quelle.md