Worum es geht
Nicht jede Arbeit beginnt mit einer Anfrage. Ein Zeitgeber räumt jede Nacht alte Einträge auf, ein Nachrichtenempfänger verarbeitet eine Nachricht aus einer Warteschlange, ein Job rechnet Berichte für jeden Kunden. Diese Arbeit hat kein Token und läuft deshalb nicht durch die Filterkette, also auch nicht durchs Mandanten-Tor.
Das ist gefährlich. Die Persistenz prüft selbst nicht mehr, ob ein Mandant bedient wird, denn das erledigt das Tor. Bekommt sie einen Mandantenschlüssel, leitet sie die Arbeit in dessen Datenbank, und wo automatisches Anlegen freigegeben ist, legt sie für einen unbekannten Schlüssel sogar eine neue Datenbank an. Ein Job, der für einen gesperrten oder falsch geschriebenen Mandanten arbeitet, würde nicht aufgehalten.
So läuft es
@Component
class NightlyCleanup {
private final TenantScope scope;
private final CleanupService cleanup;
void run(List<String> tenantKeys) {
for (String tenant : tenantKeys) {
try {
// asks the gate, fills the context, restores it afterwards
scope.runAsTenant(tenant, "svc-nightly-cleanup", cleanup::purgeOldEntries);
} catch (TenantNotServedException refused) {
// tenant suspended, unknown, or CIAS unreachable: skip it
}
}
}
}
-
1CIASprüft die Angaben: Mandant, handelnde Kennung und Arbeit müssen da sein, der Schlüssel muss gültig sein
-
2CIASfragt das Mandanten-Tor: Wird
nordbaubedient? Derselbe Speicher, dieselbe Regel bei Ausfall -
3CIASmerkt sich den bisherigen RequestContext und setzt einen neuen: Mandant
nordbau, erlaubte Mandanten nurnordbau, Personsvc-nightly-cleanup, keine Rollen -
4CIASführt die Arbeit aus
-
5CIASstellt den bisherigen RequestContext wieder her, auch wenn die Arbeit mit einem Fehler endetErgebnis: Die Arbeit lief in
nordbau, und nur dort.
Die Ausprägungen
Wann: nordbau ist ACTIVE und im Gültigkeitsfenster.
Die Arbeit läuft im Mandanten nordbau. Die Persistenz findet nordbau in der Liste der erlaubten Mandanten und leitet in dessen Datenbank.
Ergebnis: callAsTenant gibt das Ergebnis der Arbeit zurück, runAsTenant nichts.
Wann: nordbau ist gesperrt, geschlossen, abgelaufen oder unbekannt, oder CIAS ist nicht erreichbar und nichts ist gemerkt.
Die Arbeit läuft nicht. TenantScope wirft eine TenantNotServedException, und im Log steht, für welchen Mandanten und als wer abgelehnt wurde.
Ergebnis: Der Aufrufer fängt die Ausnahme und macht mit dem nächsten Mandanten weiter.
Wann: Ein Job geht über mehrere Mandanten und öffnet für jeden einen eigenen Scope, auch innerhalb eines anderen.
Jeder Scope merkt sich den Kontext, den er vorgefunden hat, und stellt genau den wieder her.
Ergebnis: Nach dem inneren Scope arbeitet der äußere wieder in seinem Mandanten.
Wann: Die Arbeit ruft Code auf, der eine Rolle verlangt.
Der Kontext hat keine Rollen, also lehnt jede Rollenprüfung ab. Ein Job arbeitet mit der Befugnis seines eigenen Codes, nicht mit der einer Person.
Ergebnis: Könnte sich ein Job selbst Rollen geben, könnte jeder Code, der einen Mandanten nennen kann, sich jedes Recht verschaffen.
Warum eine handelnde Kennung Pflicht ist
runAsTenant verlangt neben dem Mandanten eine Kennung, als wer die Arbeit läuft, etwa svc-nightly-cleanup. Ohne sie lehnt TenantScope ab. Ein Job, der Daten ändert, hinterlässt Spuren im Audit, und „das System“ sagt dort nichts. Die Kennung steht in der Revision so, wie bei einer Anfrage die Person steht, die sie geschickt hat. Als Adresse und Browser steht dort internal.
Die drei Stellen, die einen Mandanten setzen dürfen
Einen Mandanten in den RequestContext zu schreiben heißt: festlegen, in welcher Datenbank gearbeitet wird. Das dürfen genau drei Stellen, alle in CIAS:
| Stelle | Wann | Was vorher geprüft wurde |
|---|---|---|
Filterkette (TokenParser) | bei jeder Anfrage mit Token | Token gültig, Mandant aufgelöst, Tor hat zugelassen |
Mandantenwechsel (ContextSwitch) | wenn der Header tenant wirkt | Realm-Rolle, Ziel in der Liste der erlaubten Mandanten; danach fragt die Filterkette das Tor für das Ziel |
TenantScope | bei Arbeit ohne Anfrage | Tor hat zugelassen |
Jeder andere Code, der den Mandanten selbst setzt, umgeht das Tor. Dagegen gibt es eine Architekturregel in cias-test-support: TenantContextRules.tenantContextComesFromTheGate(). Ein Modul nimmt sie in seine Tests auf, und der Build schlägt fehl, sobald eine Klasse den Mandanten im RequestContext selbst setzt. CDMS führt diese Regel in seinen Integrationstests aus.
Fallen
Weiter
- Den Mandanten zulassen (Mandanten-Tor)
- Die Sicht von CDMS: Wird der Mandant bedient?
- Was eine Revision festhält: Was eine Revision festhält