Worum es geht
In Benutzer-Modellen sieht jede Person nur ihre eigenen Zeilen. Manchmal muss aber jemand genau das sehen, was eine bestimmte andere Person sieht, etwa der Support bei einer Fehlersuche. Dafür gibt es den Benutzerwechsel: Der Client schickt den Header user mit der ID der anderen Person, der Zielperson.
Die ID einer Person ist ihr Wert im Claim sub des Tokens, also ihre Keycloak-ID, eine lange Kennung wie 3f2a…. Der Login-Name funktioniert nicht.
Mit einem zweiten, freiwilligen Header wählst du, mit wessen Rechten du arbeitest:
Header user-roles | Rollen, Gruppen und Attribute |
|---|---|
fehlt oder own | deine eigenen |
target | die der Zielperson, so wie sie in ihrem eigenen Token stünden |
| jeder andere Wert | Anfrage wird abgelehnt |
Groß- und Kleinschreibung spielt keine Rolle, Target gilt also wie target.
Was wechselt und was bleibt
- ID der Person (
userId) - Name der Person (
userName) - damit der Owner-Filter: du siehst die Zeilen der Zielperson
- damit
_userIdbeim Anlegen: neue Zeilen gehören der Zielperson
- angemeldete Person:
authenticatedUserIdundauthenticatedUserNamebleiben deine - für CIAS selbst bist du der Handelnde: Verwaltung, Obergrenze, Audit
- der Mandant der Anfrage
- Realm-Rollen
- Fachrollen (Client-Rollen)
- Gruppen
- Attribute, also auch jeder Attributfilter
Mit target ist die Übernahme vollständig. Du bekommst auch Rollen, die du selbst nicht hast, sogar Plattformrollen der Zielperson. Es gelten dieselben Regeln wie bei einem echten Token der Zielperson:
- Ist sie Mitglied der Organisation des Mandanten und trägt die Mitgliedschaft Rollen, ersetzen diese ihre globalen Fachrollen. Siehe Effektive Rollen.
- Werte, die pro Mandant angemeldet sind, holt CIAS für die Zielperson, nicht für dich. Siehe Ein Wert pro Person oder pro Mandant.
Die Prüfungen der Reihe nach
-
CIASMandantenwechsel zuerstTrägt die Anfrage auch
tenant, wird dieser Wechsel zuerst angewendet. Geprüft wird danach für den Mandanten, unter dem die Anfrage am Ende läuft. -
CIASRolleHat die angemeldete Person die Realm-Rolle
allowed-user-context-switch?↳ nein 403cias.authentication.user-switch-denied -
CIASZielperson nachschlagenKann CIAS herausfinden, wer die Zielperson ist?↳ nein 403
cias.authentication.user-switch-unavailable -
CIASZielperson bekannt?Gibt es eine Person mit dieser ID?↳ nein 403
cias.authentication.user-switch-denied -
CIASMitglied im Mandanten?Nur wenn die Anfrage unter einem Mandanten läuft: Gehört die Zielperson dazu?↳ nein 403
cias.authentication.user-switch-denied -
CIASFreigabe der ZielpersonHat die Zielperson dir den Wechsel freigegeben, für diesen Mandanten und diesen Modus, und gilt die Freigabe noch? Wird bei jeder Anfrage neu gefragt.↳ nein 403
cias.authentication.user-switch-not-consented, oderuser-switch-unavailable, wenn die Frage nicht beantwortet werden kann - ID und Name der Zielperson im RequestContext, Rollen je nach Modus
Ein Header user-roles mit einem anderen Wert als own oder target führt ebenfalls zu 403 cias.authentication.user-switch-denied.
Mitglied ist die Zielperson, wenn eines davon stimmt:
- Sie ist Mitglied der Organisation mit dem Alias des Mandanten.
- Ihr Attribut
tenantnennt den Mandanten. - Ihr Attribut
allowedTenantsenthält den Mandanten.
Jede Ablehnung sieht gleich aus:
POST /api/rest/note/query
Authorization: Bearer <Token von Lena>
user: 3f2a…HTTP 403
{ "error": "cias.authentication.user-switch-denied",
"message": "request refused" }Warum genau abgelehnt wurde, also Rolle, unbekannt oder nicht im Mandanten, steht nur im Log. So erfährt niemand über den Header, welche IDs es gibt.
Nur die fehlende Freigabe hat einen eigenen Schlüssel, cias.authentication.user-switch-not-consented. Sie wird erst geprüft, wenn feststeht, dass die Zielperson existiert und zum Mandanten gehört. Ein Client kann darauf reagieren und anbieten, eine Freigabe zu beantragen.
| Realm-Rolle allowed-user-context-switch | Zielperson | Header user-roles | Nachschlagen | Freigabe der Zielperson | Ergebnis |
|---|---|---|---|---|---|
| – | – | anderer Wert als own oder target | – | – | 403 user-switch-denied |
| nein | – | – | – | – | 403 user-switch-denied |
| ja | – | – | nicht möglich | – | 403 user-switch-unavailable |
| ja | unbekannt | – | möglich | – | 403 user-switch-denied |
| ja | nicht Mitglied im Mandanten der Anfrage | – | möglich | – | 403 user-switch-denied |
| ja | Mitglied, oder SINGLE | – | möglich | keine, abgelaufen, widerrufen, anderer Mandant oder nur own bei target | 403 user-switch-not-consented |
| ja | Mitglied, oder SINGLE | – | möglich | nicht prüfbar | 403 user-switch-unavailable |
| ja | Mitglied, oder SINGLE | fehlt oder own | möglich | own oder target | ID und Name der Zielperson, deine Rollen und Attribute |
| ja | Mitglied, oder SINGLE | target | möglich | target | ID, Name, Rollen, Gruppen und Attribute der Zielperson |
Woher CIAS die Zielperson kennt
Im Token steht nur die angemeldete Person. Über die Zielperson fragt CIAS deshalb nach und rechnet aus Keycloak nach, was in ihrem Token stünde:
- ihre effektiven Realm-Rollen, auch solche aus zusammengesetzten Rollen und Standardrollen,
- ihre effektiven Client-Rollen am Backend-Client,
- ihre Organisationsmitgliedschaften mit den Rollen ihrer Organisationsgruppen,
- ihre Benutzerattribute, für die der Client einen Attribut-Mapper hat, so wie CIAS sie anlegt.
Nicht nachgebildet werden Mapper auf anderen Client-Scopes und ein groups-Claim. Im Zweifel siehst du mit target also weniger als das echte Token der Zielperson, nie mehr.
Wann: CIAS mit seiner Benutzerverwaltung läuft im selben Programm wie CDMS, etwa im Hub.
Die Nachfrage ist ein einfacher Methodenaufruf.
Ergebnis: Nichts einzurichten.
Wann: CDMS und CIAS laufen als eigene Dienste.
CDMS fragt GET /cias/lookup/users/{sub}/identity?client=<backend-client>, mit demselben Dienst-Token wie für den Mandanten-Lookup. In CDMS steht dafür codamai.cias.tenancy.lookup=remote. In CIAS müssen codamai.cias.user.lookup-rest=true gesetzt sein (derselbe Schalter wie für den Attribut-Lookup) und die eigene Rollenliste codamai.cias.user.switch-lookup-roles (Umgebung CIAS_USER_SWITCH_LOOKUP_ROLES) die Rolle des Dienst-Tokens enthalten.
Ergebnis: Leere Rollenliste heißt: niemand darf fragen, jeder Wechsel endet mit user-switch-unavailable.
Die Rollenliste ist bewusst nicht die des Attribut-Lookups. Die Antwort enthält alle Rollen und Attribute einer Person, das ist mehr, als ein Attributwert verrät.
CDMS merkt sich eine Antwort je Person und Client 30 Sekunden lang (codamai.cias.attribute-lookup.ttl). Ist CIAS danach nicht erreichbar, greift CDMS nicht auf eine ältere Antwort zurück: Der Wechsel wird abgelehnt.
Die Freigabe der Zielperson
Eine Freigabe ist die Zustimmung einer Person, dass eine bestimmte andere Person in ihrem Namen arbeiten darf. Sie ist an alles gebunden, was den Wechsel ausmacht:
| Teil der Freigabe | Bedeutung |
|---|---|
| Freigebende Person | die Zielperson, immer die angemeldete Person, die sie erteilt |
| Empfänger | genau eine Person, nie eine Rolle oder Gruppe |
| Mandant | der Mandant, in dem die Freigabe gilt. Ohne Mandanten (SINGLE) keiner |
| Modus | OWN oder TARGET. TARGET schließt OWN ein |
| Ende | bis wann sie gilt. Ohne Ende nur, wenn die Installation es erlaubt |
| Grund | freiwillig, etwa eine Ticketnummer. Steht in der Mail und im Audit |
Eine Freigabe für OWN deckt user-roles: target nicht ab. Eine Freigabe in acme deckt initech nicht ab.
Wie eine Freigabe entsteht
Wann: Der Support braucht Zugriff, die Zielperson weiß noch nichts davon.
-
1Client→CIASLena schickt
POST /cias/me/switch-requestsmittarget,mode,validUntilundreason. Dafür braucht sie die Rolleallowed-user-context-switch. -
2CIASlegt den Antrag an und schickt Ben die Mail
SWITCH_CONSENT_REQUESTED: wer fragt, mit wessen Rechten, bis wann, warum -
3Client→CIASBen bestätigt mit
POST /cias/me/switch-consents/{id}/approve, optional mit einem eigenen, kürzeren Ende. Oder er lehnt ab mit.../decline.
Ergebnis: Aus dem Antrag wird die Freigabe. Ein zweiter gleicher Antrag liefert den offenen zurück, ohne zweite Mail. Ein unbeantworteter Antrag verfällt nach CIAS_USER_SWITCH_REQUEST_EXPIRY (Standard 24 Stunden).
Wann: Ben will von sich aus, dass Lena diese Woche seine Vorgänge bearbeitet.
Ben schickt POST /cias/me/switch-consents mit grantee, mode, validUntil und reason. Freigebende Person ist immer Ben selbst, es gibt kein Feld, mit dem er jemand anderen eintragen könnte.
Ergebnis: Die Freigabe gilt sofort.
Was danach passiert:
- Erste Nutzung: Beim ersten Wechsel, der auf der Freigabe beruht, bekommt Ben einmal die Mail
SWITCH_CONSENT_USED. - Widerruf:
DELETE /cias/me/switch-consents/{id}. Er wirkt mit der nächsten Anfrage, denn die Freigabe wird nie zwischengespeichert. Widerrufen dürfen Ben, Lena, ein Plattform-Admin und ein Mandanten-Admin (tenant-admin) im eigenen Mandanten. Erteilen darf nur Ben. - Überblick:
GET /cias/me/switch-consentszeigt Ben, was er freigegeben hat, mit der ersten Nutzung.GET /cias/me/switch-consents/receivedzeigt Lena, was sie darf. Admins listen einen Mandanten unterGET /cias/admin/switch-consents. - Audit: Antrag, Freigabe, Ablehnung, Widerruf und erste Nutzung stehen als
SwitchConsentEvent.*in der CIAS-Spur.
Für eine Freigabe, die du nicht sehen darfst, antwortet CIAS mit 404, als gäbe es sie nicht.
Was eine Installation einstellt
| Einstellung | Umgebung | Standard |
|---|---|---|
codamai.cias.user-switch.consent | CIAS_USER_SWITCH_CONSENT | required. off heißt: die Rolle allein entscheidet |
codamai.cias.user-switch.consent-exempt-clients | CIAS_USER_SWITCH_CONSENT_EXEMPT_CLIENTS | leer |
codamai.cias.user-switch.max-consent-duration | CIAS_USER_SWITCH_MAX_CONSENT_DURATION | keine Grenze |
codamai.cias.user-switch.allow-unlimited-consent | CIAS_USER_SWITCH_ALLOW_UNLIMITED_CONSENT | false, also braucht jede Freigabe ein Ende |
codamai.cias.user-switch.request-expiry | CIAS_USER_SWITCH_REQUEST_EXPIRY | PT24H |
codamai.cias.user-switch.revoker-roles | CIAS_USER_SWITCH_REVOKER_ROLES | tenant-admin |
codamai.cias.user-switch.consent-url | CIAS_USER_SWITCH_CONSENT_URL | leer. Sonst steht die Adresse in den Mails |
Jede Einstellung wirkt auch direkt als Umgebungsvariable, in jedem Programm, das CIAS einbindet. Ein unbekannter Wert bei consent verhindert den Start. Ein Ende in der Vergangenheit, über der Höchstdauer oder ein fehlendes Ende ohne Erlaubnis lehnt CIAS mit 422 cias.user.switch-consent-invalid ab.
Getrennt betrieben fragt CDMS die Freigabe bei GET /cias/lookup/users/{sub}/switch-consent?actor=…&tenantKey=…&mode=own|target ab, mit demselben Dienst-Token und denselben Rollen wie beim Nachschlagen der Zielperson. Anders als die Identität wird die Antwort nie gemerkt.
Varianten
Wann: Lena hat allowed-user-context-switch und schickt user: 3f2a…, ohne user-roles. Ben mit der ID 3f2a… ist Mitglied im Mandanten.
-
1Client→CDMSschickt
POST /note/querymit Headeruser: 3f2a… -
2CIASliest das Token, bestimmt Mandant, Rollen und Attribute von Lena
-
3CIASRolle da, Ben bekannt und Mitglied → ID und Name := Ben, Rollen und Attribute bleiben Lenas
-
4CDMS→Databasesucht mit
_userId = 3f2a…
Ergebnis: Die Antwort enthält Bens Notizen, gefiltert mit Lenas Rechten.
Wann: Wie oben, dazu user-roles: target.
-
1Client→CDMSschickt
POST /note/querymituser: 3f2a…unduser-roles: target -
2CIASRolle da, Ben bekannt und Mitglied
-
3CIASID, Name, Realm-Rollen, Fachrollen, Gruppen und Attribute := die von Ben
-
4CDMS→Databasesucht mit
_userId = 3f2a…, prüft Bens Rollen, filtert mit Bens Attributen
Ergebnis: Lena sieht, was Ben sieht, soweit Keycloak Bens Token nachbilden lässt.
Wann: Lena fehlt allowed-user-context-switch.
Der Header wird nicht übergangen, die ganze Anfrage wird abgelehnt.
Ergebnis: 403 cias.authentication.user-switch-denied.
Wann: Zur ID gibt es keine Person, oder sie gehört nicht zum Mandanten, unter dem die Anfrage läuft.
Beides endet gleich. Ein Tippfehler in der ID führt also nicht zu einer leeren Antwort, sondern zu einer Ablehnung.
Ergebnis: 403 cias.authentication.user-switch-denied.
Wann: CIAS ist nicht erreichbar, lehnt den Zugang ab, oder die Nachfrage ist nicht eingerichtet.
Ohne zu wissen, wer die Zielperson ist, wechselt CIAS nicht. Eine ältere Antwort wird nicht verwendet.
Ergebnis: 403 cias.authentication.user-switch-unavailable.
Wann: Die Anfrage schickt etwa user-roles: admin.
Ein unbekannter Wert wird nicht als own gelesen.
Ergebnis: 403 cias.authentication.user-switch-denied.
Wann: Lena hat die Rolle, Ben ist Mitglied, aber Ben hat Lena nichts freigegeben.
Rolle, Zielperson und Mitgliedschaft stimmen. Danach fragt CIAS nach der Freigabe und findet keine. Es wird nichts gewechselt.
Ergebnis: 403 cias.authentication.user-switch-not-consented. Lena kann einen Antrag stellen.
Wann: Ben hat Lena OWN freigegeben, Lena schickt user-roles: target.
OWN deckt target nicht ab. Dieselbe Anfrage ohne user-roles geht durch.
Ergebnis: 403 cias.authentication.user-switch-not-consented.
Wann: Bens Freigabe gilt für acme, Lena arbeitet unter initech.
Eine Freigabe gilt genau in einem Mandanten.
Ergebnis: 403 cias.authentication.user-switch-not-consented.
Wann: Ben widerruft, während Lena arbeitet, oder das Ende ist erreicht.
Die Freigabe wird bei jeder Anfrage neu geprüft. Die nächste Anfrage endet bereits mit der Ablehnung.
Ergebnis: 403 cias.authentication.user-switch-not-consented.
Wann: Ein technischer Client, etwa ein Import, steht in CIAS_USER_SWITCH_CONSENT_EXEMPT_CLIENTS und wechselt mit seinem eigenen Dienstkonto-Token.
Ein Dienstkonto kann niemanden um Freigabe bitten. CIAS erkennt das Token daran, dass azp der Client ist und preferred_username service-account-<client> lautet. Eine Person, die sich über denselben Client anmeldet, ist nicht freigestellt.
Ergebnis: Der Wechsel braucht keine Freigabe. Rolle, Zielperson und Mitgliedschaft werden weiter geprüft.
Wann: CIAS_USER_SWITCH_CONSENT=off
Die Installation entscheidet bewusst, dass die Rolle allein reicht. Beim Start steht dazu eine Warnung im Log.
Ergebnis: Der Wechsel funktioniert ohne Freigabe, die anderen Prüfungen bleiben.
Wann: Mit wirksamem Wechsel wird ein Objekt eines Benutzer-Modells angelegt, in beiden Modi.
CDMS setzt _userId auf die ID der Zielperson. Das neue Objekt gehört ihr, und ohne Wechsel siehst du es nicht mehr.
Ergebnis: So lassen sich Daten für eine andere Person anlegen. Ein mitgeschicktes _userId im JSON bewirkt das nicht.
Wann: Mit wirksamem Wechsel wird ein auditiertes Modell geändert.
Die Revision nennt als Benutzer-ID und Namen die Zielperson. Zusätzlich hält sie ID und Namen der angemeldeten Person als handelnde Person fest. In der Historie steht deren Name in actingUsername.
Ergebnis: Siehe Was eine Revision festhält.
Wann: Die Anfrage trägt die Header tenant und user.
Jeder Wechsel braucht seine eigene Realm-Rolle. Zuerst wird der Mandant gewechselt, nach den Regeln unter Mandantenwechsel per Header. Danach wird die Person gewechselt, und die Mitgliedschaft der Zielperson wird für den Ziel-Mandanten geprüft.
Ergebnis: Du arbeitest als die Zielperson im anderen Mandanten, mit deinen Rollen oder mit ihren.
Wann: CODAMAI_PERSISTENCE_TENANT_MODE=SINGLE
Es gibt keinen Mandanten, also entfällt die Prüfung der Mitgliedschaft. Rolle, Nachschlagen und bekannte Zielperson werden weiter geprüft.
Ergebnis: Den Owner-Filter gibt es in beiden Betriebsarten, also auch den Wechsel.
Fallen
Wie es weitergeht
- Warum Benutzer-Modelle nur eigene Zeilen zeigen: Nur die eigenen Daten (Owner-Filter)
- Für einen anderen Mandanten arbeiten: Mandantenwechsel per Header
- Was eine Revision festhält: Was eine Revision festhält
- Ein Beispiel von Anfang bis Ende: Der Support schaut in einen Mandanten