CodamAIDocs
Themafertig

Ablage und Mandantentrennung im Speicher

Wie der Speicherpfad aufgebaut ist, wie Mandanten im Dateisystem getrennt sind und wie eine Datei atomar abgelegt wird.

Ausprägungen
MULTI: Pfad mit MandantSINGLESystem-Modell ohne MandantMandant fehlt → abgelehntatomares AblegenVerzeichnisrechte

Worum es geht

Der Inhalt jeder Datei liegt als eigene Datei im Dateispeicher, meist einem Verzeichnis auf einem Volume. Der Pfad dorthin ist fest aufgebaut. Aus ihm ergibt sich auch die Trennung der Mandanten: Jeder Mandant hat sein eigenes Unterverzeichnis.

Der Pfad

Aufbau
Anfrage
<basePath>/[<mandant>/]<modellpfad>/<fileId>
Beispiel, Betriebsart MULTI, Mandant t1
/files/t1/fileasset/910e2c19-d5d1-4dae-8229-ea5fcbcc6194
TeilwoherBeispiel
basePathKonfiguration codamai.cdms.persistence.file.basePath/files/
MandantMandant der Anfrage, nur wenn nötig (siehe unten)t1
Modellpfadaus dem API-Pfad des Modells, beim Generieren festgelegtfileasset/, company/logo/
fileIdUUID, die der Server beim ersten Speichern vergibt910e2c19-…

Kein Teil des Pfads kommt direkt vom Benutzer. Der Anzeigename Vertrag.pdf steht nur im Datensatz. Den Mandanten prüft CDMS, bevor er in den Pfad kommt: Erlaubt sind Buchstaben, Ziffern, ., _ und -, aber kein ...

Wann der Mandant im Pfad steht

Mit oder ohne Mandanten-Verzeichnis?
BetriebsartEbene des ModellsPfad
MULTIMandant oder Benutzer<basePath>/<mandant>/<modell>/<fileId>
MULTISystem<basePath>/<modell>/<fileId>, gemeinsam für alle Mandanten
SINGLEalle<basePath>/<modell>/<fileId>, ein gemeinsames Verzeichnis

Die Ebene Benutzer trennt im Speicher nicht weiter als die Ebene Mandant. Wer welche Datei eines Mandanten sehen darf, entscheiden die Rechte und der Owner-Filter beim Lesen des Datensatzes. Siehe Modell-Ebenen: System, Mandant, Benutzer.

Ein Verzeichnisbaum

flowchart TB
    B["/files/"] --> T1["t1/"]
    B --> T2["t2/"]
    B --> S["country-flags/ (System-Modell)"]
    T1 --> T1F["fileasset/"]
    T1F --> F1["910e2c19-… (aktuell)"]
    T1F --> F1V["910e2c19-….1790000100000 (Version)"]
    T1 --> T1L["company/logo/"]
    T1L --> L1["3b7a…"]
    T2 --> T2F["fileasset/"]
    T2F --> F2["5c01…"]
    S --> S1["a9e2…"]

Mandantentrennung

Die Trennung liegt allein im Pfad. Eine Anfrage von Mandant t2 sucht nur unter /files/t2/. Selbst mit der fileId einer Datei von t1 findet sie dort nichts.

Was die Pfadtrennung bewirkt (MULTI)
SituationErgebnis
t1 schreibt, t1 liestgefunden
t1 schreibt, t2 liest dieselbe fileIdnicht gefunden, anderes Verzeichnis
Mandanten-Modell, Anfrage ohne Mandantabgelehnt, CDMS_FILE_TENANT_REQUIRED
Mandant mit unzulässigen Zeichenabgelehnt, CDMS_FILE_TENANT_INVALID
System-Modell, beliebiger Mandantgefunden, gemeinsames Verzeichnis

Vorher hat schon die Datenbank getrennt: Den Datensatz einer fremden Datei findet eine Anfrage gar nicht erst. Siehe Mandantentrennung in CDMS.

Atomar ablegen

Ein Upload kommt zuerst als Zwischendatei an, oft in einem anderen Dateisystem als der Speicher. Würde CDMS ihn direkt an den Zielplatz kopieren, könnte ein gleichzeitiger Download eine halb geschriebene Datei bekommen. Deshalb:

  1. 1
    CDMS→File storage
    bei auditierten Modellen: benennt den bisherigen Inhalt in eine Version um
  2. 2
    CDMS→File storage
    schreibt den Inhalt unter einen privaten Namen im Zielverzeichnis: <fileId>.staging-<zufall>
    Das kann bei großen Dateien dauern. Niemand liest diesen Namen.
  3. 3
    CDMS→File storage
    benennt die Zwischendatei in einem Schritt auf <fileId> um
    Ergebnis: Unter <fileId> liegt nie ein halb geschriebener Inhalt.

Verzeichnisrechte

Neue Verzeichnisse legt CDMS mit den Rechten rwxr-x--- an: Der Anwendungsbenutzer darf alles, seine Gruppe lesen, alle anderen nichts. So bleiben Mandantendaten auch für andere Prozesse auf demselben Server verschlossen. Eine Sicherung, die unter derselben Gruppe läuft, kann lesen. Das Basisverzeichnis selbst legt CDMS nicht an, siehe Speicher-Backends.

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-localfs-storage – FileUtils.getPathForCurrentTenant, stagingPath; LocalFSFileController.saveFile (publish); docs/02-path-layout-and-tenant-separation.md; docs/adr/ADR-003, ADR-004, ADR-013, ADR-014, ADR-016, ADR-017
  • CDMS/cdms-generator – DtoMetaProcessor.getFilePath (Modellpfad aus dem API-Pfad)
  • CDMS/cdms-integrationtest – AbstractRecursiveFileDeleteTest (storageRoot mit und ohne Mandantensegment)
Suchen