feat(verzeichnis): SCIM-Endpunkt als dritte Quellenart (#029)
Bisher holt Laufzettel Personen aus dem Verzeichnis. Das setzt Lesezugriff und einen Netzweg dorthin voraus, und beides ist nicht ueberall gegeben. Mit der Art SCIM ruft stattdessen der Anbieter an — sofort, nicht beim naechsten Intervall. Umgesetzt ist, was authentik, Entra ID und Okta wirklich schicken: Users mit dem Filter userName eq, POST/GET/PUT/PATCH/DELETE, dazu ServiceProviderConfig, Schemas und ResourceTypes. Jeder Handler prueft sein Token selbst. Ein Token je Quelle, argon2-gehasht, einmal angezeigt — es ist der einzige Mandantenbeweis, den eine Zustellung mitbringt. DELETE deaktiviert und loescht nichts. Eine SCIM-Quelle wird nicht abgeglichen, an zwei Stellen gehalten: ein Abgleich, der dort nichts liest und ERFOLG meldet, haette ueber die Abschaltregel die ganze Belegschaft deaktiviert. Zwei Befunde kamen aus Gegenproben, nicht aus Tests: - PATCH remove wirkte gar nicht. Der Schalter nurGesetzte las bei PATCH jedes null als "nicht mitgeschickt"; eine beim Anbieter entfernte Abteilung stand hier weiter. Noetig war er nie — patchAnwenden liefert immer den vollstaendigen Zielzustand. - Entra ID schickt active als "True"/"False". Beim Deaktivieren kommt mit einem reinen value === true zufaellig das Richtige heraus, beim Aktivieren nicht: ein wieder freigeschaltetes Konto waere fuer immer inaktiv geblieben. Dazu ein Fehler aus #028: in verbindungTestenAktion stand zweimal dieselbe Bedingung, der zweite Zweig war seit dem ersten Tag unerreichbar. 1432 Tests (vorher 1373). Klickprobe am gebauten Stand mit einer echten Zustellung. Gegen echte Mandanten ist nichts gelaufen — die Anfrageformen stammen aus der Dokumentation, und das steht so in der Testdatei. Closes #49 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
76a61ec771
commit
fcd67018b8
@@ -16,18 +16,24 @@ Zwei Dinge vorweg:
|
||||
Wer im Verzeichnis verschwindet oder dort deaktiviert wird, wird hier **inaktiv**. Gelöscht wird
|
||||
nie, sonst verlöre die Historie ihre Namen.
|
||||
|
||||
## Zwei Arten von Quelle
|
||||
## Drei Arten von Quelle
|
||||
|
||||
Laufzettel liest die Personen aus einem Verzeichnis. Dafür gibt es zwei Wege, und du wählst ihn beim
|
||||
Anlegen der Quelle:
|
||||
Laufzettel braucht die Personen aus einem Verzeichnis. Dafür gibt es drei Wege, und du wählst ihn
|
||||
beim Anlegen der Quelle:
|
||||
|
||||
| Art | Für wen |
|
||||
|---|---|
|
||||
| **LDAP** | ein lokales Active Directory (oder ein anderes LDAP-Verzeichnis) |
|
||||
| **Microsoft Graph** | Entra ID, also Microsoft 365 — für Häuser ohne lokales AD |
|
||||
| Art | Für wen | Richtung |
|
||||
|---|---|---|
|
||||
| **LDAP** | ein lokales Active Directory (oder ein anderes LDAP-Verzeichnis) | Laufzettel holt |
|
||||
| **Microsoft Graph** | Entra ID, also Microsoft 365 — für Häuser ohne lokales AD | Laufzettel holt |
|
||||
| **SCIM** | authentik, Entra ID, Okta — wenn Laufzettel nicht ins Verzeichnis sehen soll oder es nicht erreicht | der Anbieter schickt |
|
||||
|
||||
Beide verhalten sich danach gleich: anlegen, aktualisieren, deaktivieren, **nie löschen**. Was sie
|
||||
unterscheidet, sind die Zugangsdaten und zwei Platzhalter:
|
||||
Die ersten zwei sind dieselbe Arbeitsweise mit anderen Zugangsdaten: Laufzettel verbindet sich nach
|
||||
Zeitplan und liest. **SCIM ist die andere Richtung** — dort gibt es keine Zugangsdaten, keinen
|
||||
Zeitplan und keinen Verbindungstest; der Anbieter schickt jede Änderung in dem Moment, in dem sie
|
||||
dort entsteht. Für SCIM gibt es [einen eigenen Abschnitt](#scim-der-anbieter-schickt) weiter unten.
|
||||
|
||||
Alle drei verhalten sich danach gleich: anlegen, aktualisieren, deaktivieren, **nie löschen**. Was
|
||||
LDAP und Graph unterscheidet, sind die Zugangsdaten und zwei Platzhalter:
|
||||
|
||||
- **`{{ou}}` bleibt bei Graph leer.** Entra ID hat keine OU. Steht an der Abteilung eine Ziel-OU
|
||||
gepflegt, nimmt Laufzettel die; sonst bleibt der Platzhalter stehen.
|
||||
@@ -42,6 +48,109 @@ Was in Entra ID einzurichten ist — App-Registrierung, Berechtigung, Einwilligu
|
||||
[`docs/betrieb/entra-id.md`](../betrieb/entra-id.md).
|
||||
|
||||
|
||||
## SCIM: der Anbieter schickt
|
||||
|
||||
Bei den ersten zwei Arten fragt Laufzettel das Verzeichnis. Bei SCIM ist es umgekehrt: der
|
||||
Identitätsanbieter ruft Laufzettel an, sobald sich bei ihm etwas ändert. Das ist der Weg für zwei
|
||||
Lagen:
|
||||
|
||||
- Das Haus will Laufzettel **keinen Lesezugriff** auf sein Verzeichnis geben.
|
||||
- Laufzettel läuft in einem Netz, das das Verzeichnis **nicht erreicht** — ein Push von außen nach
|
||||
innen geht, ein Lesezugriff von innen nach außen nicht.
|
||||
|
||||
### Was anders ist
|
||||
|
||||
| | LDAP und Graph | SCIM |
|
||||
|---|---|---|
|
||||
| Zugangsdaten | Dienstkonto bzw. App-Registrierung | **keine** — ein Token für den Anbieter |
|
||||
| Zeitplan | alle *n* Minuten | keiner, es kommt sofort |
|
||||
| Verbindung testen | zeigt die ersten zehn Treffer | gibt es nicht — es gibt keine Verbindung |
|
||||
| Abgleich-Log | je Lauf eine Zeile | gibt es nicht; stattdessen **letzte Zustellung** |
|
||||
| Attributzuordnung | einstellbar | fest, der Anbieter bestimmt die Feldnamen (RFC 7643) |
|
||||
| `{{ou}}` | aus dem DN bzw. der Abteilung | leer — SCIM kennt keine OU |
|
||||
|
||||
Was Laufzettel übernimmt: Anmeldename (`userName`), Vor- und Nachname, Anzeigename, Mailadresse,
|
||||
Titel, Abteilung, der Vorgesetzte und ob das Konto aktiv ist. Mehr nicht — keine Gruppen, keine
|
||||
Rollen, keine Passwörter.
|
||||
|
||||
### Einrichten: Laufzettel zuerst
|
||||
|
||||
1. **Quelle anlegen.** Unten auf der Seite **Art → SCIM (wird geliefert)** wählen, einen Namen
|
||||
geben (etwa „authentik Push“), **Zustellungen annehmen** anhaken und speichern. Mehr Felder gibt
|
||||
es nicht.
|
||||
2. **Token erzeugen.** An der neuen Quelle steht **Token erzeugen**. Es erscheint **genau einmal** —
|
||||
kopier es sofort. Gespeichert wird es nur als Hash; ein zweites Ansehen gibt es nicht. Wer es
|
||||
verliert, erzeugt ein neues, und das alte gilt dann nicht mehr.
|
||||
3. **Adresse ablesen.** Unter dem Namen der Quelle steht die Adresse, die der Anbieter braucht, etwa
|
||||
`https://laufzettel.firma.de/api/scim/v2`.
|
||||
|
||||
### Einrichten in authentik
|
||||
|
||||
Unter **Applications → Providers → Create → SCIM Provider**:
|
||||
|
||||
| Feld | Wert |
|
||||
|---|---|
|
||||
| **Name** | etwa „Laufzettel“ |
|
||||
| **URL** | die Adresse von oben, also `https://…/api/scim/v2` |
|
||||
| **Token** | das erzeugte Token, unverändert eingefügt |
|
||||
|
||||
Bei **User filtering** wählst du die Gruppe, deren Mitglieder in Laufzettel stehen sollen — nicht
|
||||
alle Konten, sondern die Menschen, für die Läufe entstehen. Unter **Attribute mapping** genügen die
|
||||
Vorgaben; authentik schickt `userName`, `name`, `emails` und die Erweiterung
|
||||
`enterprise:2.0:User` von selbst.
|
||||
|
||||
Danach **Save** und einmal **Sync** auslösen. In Laufzettel steht dann an der Quelle **Letzte
|
||||
Zustellung am …** und die Personen stehen unter **Mitarbeiter**.
|
||||
|
||||
### Einrichten in Entra ID
|
||||
|
||||
Unter **Entra ID → Enterprise applications → New application → Create your own application →
|
||||
Integrate any other application you don't find in the gallery**. Dann in der neuen Anwendung
|
||||
**Provisioning → New configuration**:
|
||||
|
||||
| Feld | Wert |
|
||||
|---|---|
|
||||
| **Tenant URL** | die Adresse von oben, also `https://…/api/scim/v2` |
|
||||
| **Secret Token** | das erzeugte Token |
|
||||
|
||||
**Test Connection** muss grün werden — Entra ID fragt dabei `ServiceProviderConfig`, `Schemas` und
|
||||
`ResourceTypes` ab und legt probeweise niemanden an. Danach unter **Users and groups** die Gruppe
|
||||
zuweisen und das Provisioning auf **On** stellen.
|
||||
|
||||
Zwei Eigenheiten von Entra ID, die hier nicht stören, aber erklären, was in den Protokollen steht:
|
||||
|
||||
- Entra ID schickt `active` als **Zeichenkette** `"True"`/`"False"`, nicht als `true`/`false`.
|
||||
Laufzettel liest beides.
|
||||
- Der erste vollständige Durchlauf beginnt bis zu **40 Minuten** nach dem Einschalten. Das ist
|
||||
Entra ID und nichts, was sich hier beschleunigen ließe.
|
||||
|
||||
### `Löschen` beim Anbieter heißt hier `inaktiv`
|
||||
|
||||
Nimmt jemand eine Person beim Anbieter aus der zugewiesenen Gruppe oder löscht sie dort, schickt
|
||||
der Anbieter ein `DELETE`. **Laufzettel setzt die Person dann auf inaktiv und löscht nichts.** Die
|
||||
Zeile bleibt, weil Läufe und Geräte an ihr hängen: ein archivierter Lauf muss auch in zehn Jahren
|
||||
noch sagen können, für wen er lief.
|
||||
|
||||
Der Anbieter bekommt `204` und hält die Person damit für entfernt — aus seiner Sicht richtig, denn
|
||||
sie wird ihm nicht mehr zugewiesen. **In Laufzettel steht sie weiter in der Liste**, inaktiv. Das
|
||||
sieht aus wie ein Fehler und ist keiner. Wer sie aus der Ansicht haben will, filtert auf „nur
|
||||
aktive“ — das ist die Vorgabe der Mitarbeiterliste.
|
||||
|
||||
### Wenn nichts ankommt
|
||||
|
||||
| Was der Anbieter meldet | Woran es liegt |
|
||||
|---|---|
|
||||
| `401` | Das Token stimmt nicht, oder **Zustellungen annehmen** ist nicht angehakt. Beides ergibt dieselbe Antwort — absichtlich. |
|
||||
| `404` auf eine bekannte Person | Die Person gehört zu einer **anderen** Quelle. Jedes Token sieht nur die Personen seiner eigenen. |
|
||||
| `409` beim Anlegen | Die Person gibt es in dieser Quelle schon. Der Anbieter fragt normalerweise vorher mit einem Filter; schickt er trotzdem, ist `409` die richtige Antwort und kein Datenverlust. |
|
||||
| `400 invalidFilter` | Der Anbieter benutzt einen Filter, den Laufzettel nicht unterstützt — nur `userName eq "…"` geht. |
|
||||
| nichts, Zeitüberschreitung | Der Reverse Proxy lässt `/api/scim/` nicht durch, oder TLS stimmt nicht. Siehe [`docs/betrieb/scim.md`](../betrieb/scim.md). |
|
||||
|
||||
Eine Quelle **stilllegen**, ohne das Token beim Anbieter zu löschen: Häkchen **Zustellungen
|
||||
annehmen** entfernen. Der Endpunkt antwortet dann mit `401`, und beim Anbieter stehen die
|
||||
Zustellungen als Fehler — sie gehen nicht verloren, er versucht es erneut.
|
||||
|
||||
|
||||
## Eine Quelle anlegen
|
||||
|
||||

|
||||
@@ -116,6 +225,10 @@ Namensänderung der Quelle ihre Anmeldung.
|
||||
|
||||

|
||||
|
||||
Die folgenden vier Abschnitte gelten für **LDAP und Graph**. Bei einer SCIM-Quelle gibt es weder
|
||||
Verbindungstest noch Zeitplan noch Abgleich-Log — dort steht an ihrer Stelle die letzte Zustellung,
|
||||
siehe [oben](#scim-der-anbieter-schickt).
|
||||
|
||||
**Verbindung testen** liest die ersten Einträge und zeigt sie so, wie sie ankämen — **gespeichert
|
||||
wird nichts**. Mach das immer vor dem ersten Abgleich. Klappt es nicht, steht der Grund in Rot
|
||||
darunter; die häufigsten stehen in [Kapitel 08](08-fehlerbehebung.md).
|
||||
@@ -195,3 +308,7 @@ deshalb als Hinweis und nicht als Fehler da. Steigt die Zahl, prüf die Attribut
|
||||
Eine Quelle ist keine Voraussetzung. Personen lassen sich von Hand anlegen oder aus einer CSV-Datei
|
||||
einlesen — ein Verzeichnis nimmt nur die Arbeit ab. Siehe
|
||||
[Kapitel 05](05-mitarbeiter-geraete.md).
|
||||
|
||||
Von Hand angelegte Personen gehen dabei nicht verloren, wenn später eine Quelle dazukommt: taucht
|
||||
jemand mit demselben Anmeldenamen aus dem Verzeichnis oder per SCIM auf, **übernimmt** Laufzettel
|
||||
die vorhandene Zeile statt eine zweite anzulegen. Ihre Läufe und Geräte bleiben an ihr hängen.
|
||||
|
||||
Reference in New Issue
Block a user