> ## Documentation Index
> Fetch the complete documentation index at: https://docs.iru.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Mandantenisolierung

> Wie Iru die Daten jeder Organisation getrennt hält – in der Datenbank-Engine durch Row-Level Security durchgesetzt – und jede Änderung manipulationssicher nachvollziehbar macht.

Iru Identity ist mandantenfähig: Viele Organisationen nutzen denselben Dienst. Die
wichtigste Eigenschaft dieses Designs ist, dass **ein Mandant niemals die Daten eines anderen
sehen oder verändern kann**. Iru setzt dies in der Datenbank-Engine selbst durch, nicht nur im
Anwendungscode, sodass selbst ein Fehler in einer Abfrage die Grenze nicht überschreiten kann.

## Durch die Datenbank durchgesetzte Isolierung

Jede Tabelle, die Mandantendaten enthält, trägt eine `tenant_id` und ist durch
**PostgreSQL Row-Level Security (RLS)** geschützt:

* Für die Tabelle ist RLS **aktiviert und erzwungen** (`FORCE ROW LEVEL SECURITY`), sodass die
  Richtlinie für jede Rolle gilt. Es gibt keinen privilegierten Weg daran vorbei.
* Eine einzige Richtlinie regelt sowohl Lese- als auch Schreibzugriffe: Zeilen sind nur sichtbar (`USING`) und
  beschreibbar (`WITH CHECK`), wenn ihre `tenant_id` dem Mandanten der
  aktuellen Anfrage entspricht.

```sql theme={null}
-- The shape every tenant table shares
ALTER TABLE app.<table> ENABLE ROW LEVEL SECURITY;
ALTER TABLE app.<table> FORCE  ROW LEVEL SECURITY;

CREATE POLICY tenant_access_policy ON app.<table> FOR ALL
  USING      (tenant_id = current_setting('iru.tenant_id'))
  WITH CHECK (tenant_id = current_setting('iru.tenant_id'));
```

Dies wird einheitlich und in großem Umfang angewendet (**Hunderte von Tabellen**, jede mit erzwungener
RLS und einer identischen Lese- und Schreibrichtlinie), nicht nur auf einige wenige privilegierte Tabellen.

<Note>
  Da die Grenze in der Datenbank liegt, gibt eine Abfrage, die vergisst, nach
  Mandant zu filtern, einfach nichts von einem anderen Mandanten zurück. Die Engine verweigert es, Zeilen
  außerhalb des aktiven Mandanten anzuzeigen oder zu ändern.
</Note>

## Der Mandantenkontext begleitet jede Anfrage

```mermaid theme={null}
flowchart LR
  req["Anfrage an<br/>tenant.subdomain"] --> mw["Mandant auflösen<br/>(ablehnen, falls unbekannt)"]
  mw --> tx["DB-Transaktion öffnen"]
  tx --> setl["SET LOCAL iru.tenant_id = …<br/>+ Auslöser + Korrelations-ID"]
  setl --> rls["RLS-Richtlinie liest<br/>dieselbe Einstellung"]
  rls --> data[("Nur die Zeilen dieses Mandanten")]
```

1. Der Mandant wird in der Middleware aus der Subdomain der Anfrage aufgelöst. Wenn er nicht
   aufgelöst werden kann, wird die Anfrage **abgelehnt, bevor sie überhaupt einen Handler erreicht**.
2. Für jede Datenbanktransaktion setzt Iru den Mandanten (zusammen mit dem handelnden
   Benutzer und einer Korrelations-/Trace-ID) als **transaktionsbezogene** Einstellung
   (`SET LOCAL`). Da sie auf die Transaktion beschränkt ist, kann sie nicht an die
   nächste Anfrage durchsickern, die eine gepoolte Verbindung wiederverwendet.
3. Die RLS-Richtlinie liest genau diese Einstellung, sodass die Sicherheitsgrenze derselbe
   Wert ist, den die Anwendung gesetzt hat – transaktional bei jeder Verbindung hergestellt.

## Minimale Berechtigungen, damit der Prüfverlauf nicht umgeschrieben werden kann

Die Datenbankrolle, unter der die Anwendung läuft, kann **Live**-Daten lesen und schreiben, hat aber
nur **Lese**zugriff auf das **Audit-Schema**. Selbst wenn Anwendungs-SQL
irgendwie missbraucht würde, hat es keine Berechtigung, den Prüfverlauf zu ändern oder zu löschen. Der Verlauf ist
auf der Berechtigungsebene geschützt, unabhängig von jeglicher Anwendungslogik.

Auch die referenzielle Integrität ist mandantenbezogen: Fremdschlüssel sind zusammengesetzt
(`tenant_id` + ID), sodass ein Datensatz immer nur auf einen anderen Datensatz **im
selben Mandanten** verweisen kann.

## Jede Änderung wird protokolliert (nur anhängend)

Iru führt einen vollständigen, manipulationssicheren Verlauf der Änderungen an Mandantendaten.

<AccordionGroup>
  <Accordion title="Was erfasst wird" icon="clipboard-list">
    Jede veränderbare Tabelle hat eine gespiegelte **Audit-(Journal-)Tabelle**, die von einem
    Datenbank-Trigger bei Einfügen, Aktualisieren und Löschen beschrieben wird. Jeder Eintrag erfasst, **was**
    passiert ist (die Aktion), **wer** (sowohl der handelnde Datenbank-Principal als auch der
    authentifizierte Benutzer), **wann**, eine **Korrelations-/Trace-ID**, die ihn mit der
    ursprünglichen Anfrage verknüpft, sowie den **vollständigen Vorher-/Nachher-Zustand** der Zeile.
  </Accordion>

  <Accordion title="Warum nur angehängt wird" icon="lock">
    Der Verlauf ist auf drei unabhängige Arten geschützt: Der Trigger *fügt* immer nur
    Journalzeilen *ein*; die Datenbankrolle der Anwendung hat **nur Lese**zugriff auf das
    Audit-Schema; und der Trigger läuft mit Definer-Rechten. Es gibt keinen Weg für
    die Anwendung, einen Audit-Datensatz zu ändern oder zu löschen.
  </Accordion>

  <Accordion title="Keine unbemerkte Abweichung" icon="circle-check">
    Eine ständige Konsistenzprüfung vergleicht die Spalten jeder Live-Tabelle mit ihrer
    Audit-Tabelle und schlägt fehl, wenn eine Spalte fehlt, sodass neue Felder nicht unbemerkt
    dem Prüfverlauf entgehen können. Schemamigrationen sind selbst versioniert,
    per Prüfsumme verifiziert und protokolliert.
  </Accordion>
</AccordionGroup>

## Sensible Felder erhalten eine zusätzliche Schutzebene

Zusätzlich zur Isolierung werden besonders sensible Werte **auf Feldebene
mit AES-256-GCM verschlüsselt** (authentifizierte Verschlüsselung), bevor sie gespeichert werden, und
nur beim Lesen durch die Anwendung entschlüsselt. Verschlüsselungsschlüssel werden zur
Laufzeit aus einem verwalteten Secret Store bereitgestellt, niemals aus dem Code oder der Datenbank.

## Einordnung in das Gesamtbild

Die Isolierung arbeitet Hand in Hand mit der **regionalen Datenresidenz**: Jede Region hat ihre
eigene Datenbank, und die Abfragen eines Mandanten laufen immer nur gegen die Datenbank seiner Region
(siehe [Plattformarchitektur](/de/identity/security/platform-architecture)).
Innerhalb dieser Datenbank hält RLS jeden Mandanten getrennt.

<CardGroup cols={2}>
  <Card title="Plattformarchitektur" icon="server" href="/de/identity/security/platform-architecture">
    Die Regionen, Ebenen und Infrastruktur, auf denen diese Isolierung läuft.
  </Card>

  <Card title="Authentifizierungsarchitektur" icon="signature" href="/de/identity/security/authentication-architecture">
    Wie Anmelde- und API-Anfragen Phishing-resistent und manipulationssicher gemacht werden.
  </Card>

  <Card title="Aktivitätsprotokoll" icon="list-timeline" href="/de/identity/administration/activity-log">
    Der Prüfverlauf, für Administratoren zur Überprüfung aufbereitet.
  </Card>

  <Card title="Sicherheit und Datenschutz" icon="lock" href="/de/identity/security/security-and-privacy">
    Die Garantien, die sich aus diesen Mechanismen ergeben.
  </Card>
</CardGroup>
