CodamAIDocs
Themafertig

Arbeiten für einen Mandanten ohne Anfrage

Wie Hintergrundarbeit (Timer, Jobs) für einen Mandanten läuft, durch dasselbe Tor, und welche drei Stellen einen Mandanten setzen dürfen.

Ausprägungen
Mandant wird bedientMandant nicht bedientverschachteltohne Rollen

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
      }
    }
  }
}
Was TenantScope der Reihe nach tut
  1. 1
    CIAS
    prüft die Angaben: Mandant, handelnde Kennung und Arbeit müssen da sein, der Schlüssel muss gültig sein
  2. 2
    CIAS
    fragt das Mandanten-Tor: Wird nordbau bedient? Derselbe Speicher, dieselbe Regel bei Ausfall
  3. 3
    CIAS
    merkt sich den bisherigen RequestContext und setzt einen neuen: Mandant nordbau, erlaubte Mandanten nur nordbau, Person svc-nightly-cleanup, keine Rollen
  4. 4
    CIAS
    führt die Arbeit aus
  5. 5
    CIAS
    stellt den bisherigen RequestContext wieder her, auch wenn die Arbeit mit einem Fehler endet
    Ergebnis: Die Arbeit lief in nordbau, und nur dort.

Die Ausprägungen

Was bei TenantScope passieren kann

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:

StelleWannWas vorher geprüft wurde
Filterkette (TokenParser)bei jeder Anfrage mit TokenToken gültig, Mandant aufgelöst, Tor hat zugelassen
Mandantenwechsel (ContextSwitch)wenn der Header tenant wirktRealm-Rolle, Ziel in der Liste der erlaubten Mandanten; danach fragt die Filterkette das Tor für das Ziel
TenantScopebei Arbeit ohne AnfrageTor 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

Quellen im Code und in der Wissensdatenbank
  • CIAS/cias-authentication – TenantScope (runAsTenant, callAsTenant, contextFor), TenantNotServedException, CurrentTenantProviderImpl, CiasTokenConfiguration (Bean tenantScope)
  • CIAS/cias-test-support – TenantContextRules (tenantContextComesFromTheGate, drei erlaubte Stellen)
  • CDMS/cdms-integrationtest – TenantContextArchitectureTest
  • commons-persistence – DatabaseRequestContext.requireAllowedTenant (leere Liste lehnt ab)
  • CIAS/cias-authentication/docs/adr – ADR-021 (Abschnitt 5)
Suchen