Drei unabhängige Dimensionen
Effektiver Zugriff in dForge ist die Schnittmenge dreier unabhängiger Dimensionen:
Zugriff = Ordner (Zeilen) × Entitätsansicht (Spalten) × Rolle (Operationen)
- Ordner entscheidet, welche Zeilen der Anwender sieht (Sicherheit auf Zeilenebene per Filter)
- Entitätsansicht entscheidet, welche Spalten der Anwender sieht (Sicherheit auf Spaltenebene)
- Rolle entscheidet, welche Operationen der Anwender mit dem ausführen kann, was er sieht
Jede Dimension wird unabhängig konfiguriert. Dieselbe Produkttabelle kann in einem Ordner Bestandsspalten zeigen, in einem anderen Preisspalten — und nur die Spalten, die die Rolle des Anwenders zum Schreiben zulässt — ohne jemals Daten zu duplizieren.
Rechte-Modell
Buchstabencodes
Rechte werden als Buchstabencodes auf Sicherheitsobjekten (sec_object) gespeichert.
Entitäten (object_type = 'E') unterstützen vollständiges CRUD + Clone:
| Code | Operation |
|---|---|
S | Select / Lesen |
I | Insert / Anlegen |
U | Update |
D | Delete |
C | Clone / Kopieren |
Aktionen, Berichte, Ordner unterstützen ein einzelnes binäres Recht:
| Code | Operation |
|---|---|
E | Execute / Zugriff |
Additiv
Rechte aus mehreren Rollen werden vereint (Union), niemals widerrufen. Es gibt keine „Deny“-Regel.
Rolle A gewährt: SU
Rolle B gewährt: SIC
Effektiv: SIUC
Wenn keine Rolle eine Berechtigung gewährt, ist sie verweigert. Anwender starten ohne Zugriff, bis Rollen zugewiesen werden (Least Privilege).
Rollen
Modul-Rollen
Rollen werden von Modul-Entwicklern definiert, nicht von Plattform-Admins. Sie sind auf ihr Modul namespaced — zum Beispiel crm.sales_rep, hr.manager, wms.storekeeper. Es gibt keine eingebauten generischen Plattform-Rollen.
Jede Rolle deklariert, auf welche Entitäten, Aktionen, Berichte und Ordner sie Rechte gewährt. Wenn Sie ein Modul installieren, werden seine Rollen zur Zuweisung an Anwender verfügbar.
Rollenzuweisungen
Eine Rollenzuweisung verbindet einen Anwender mit einer Rolle und wird in user_role gespeichert. Die Zuweisung kann sein:
- Global —
folder_id = NULL. Die Rolle gilt in jedem Ordner, in dem die Entitäten des Moduls erscheinen. - Ordnergebunden —
folder_id = <uuid>. Die Rolle gilt nur in diesem Ordner (und in seinen Kindern, wenn Vererbung aktiviert ist).
So kann ein einzelner Anwender für das Unternehmen insgesamt hr.manager sein, aber innerhalb von HR/Geschäftsleitung nur hr.viewer.
Direkte Anwenderrechte
Für einmalige Ausnahmen können Admins Rechte direkt einem Anwender auf einem Sicherheitsobjekt über user_rights gewähren. Das umgeht Rollen vollständig. Sparsam verwenden — die meiste Policy sollte in Rollen leben.
Ordner sind gefilterte Ansichten
Ordner sind keine Container. Sie definieren einen Filter, der bestimmt, welche Datensätze in ihnen sichtbar sind:
-- folder_entity-Zeile
folder_id = "draft_invoices"
entity_id = "invoice"
row_filter = { "status": "draft" }
-- effektive Abfrage, wenn der Anwender den Ordner öffnet
SELECT * FROM accounting.invoice
WHERE status = 'draft'
AND <Anwender-Berechtigungen>
Ein Datensatz kann in vielen Ordnern gleichzeitig erscheinen. Das Ändern der Daten eines Datensatzes kann ihn automatisch in oder aus Ordnern verschwinden lassen. Es gibt keine folder_path-Spalte auf Datentabellen.
Filter komponieren
Jede Abfrage wird durch vier Schichten gefiltert, die in der Datenbank mit AND verknüpft sind:
| Schicht | Quelle | Zweck |
|---|---|---|
| 1. Ordner-Filter | folder_entity.row_filter | Ordner-Kontext (z. B. Lager, Division) |
| 2. Ansichts-Filter | data_view.view_json.filter | Ansichts-spezifische Teilmenge (z. B. amount > 1000) |
| 3. Sicherheits-Filter | Sicherheitsregeln auf Zeilenebene | Zugriffssteuerung (z. B. nur eigene Datensätze) |
| 4. Anwender-Ad-hoc-Filter | UI-Filter-Leiste | Temporäre Filter, die zur Laufzeit hinzugefügt werden |
Sicherheit auf Spaltenebene über Entitätsansichten
Eine Entitätsansicht listet auf, welche Spalten einer Entität zugänglich sind. Alles, was nicht aufgelistet ist, ist versteckt — der Anwender kann es nicht abfragen, sortieren oder überhaupt davon wissen.
Jeder Ordner bindet seine Entitäten an eine bestimmte Entitätsansicht. Dieselbe product-Tabelle kann auf zwei völlig unterschiedliche Weisen exponiert werden:
Lager-Ordner → entity_view = storekeeper_view
Spalten: name, sku, quantity, location, min_stock
Buchhaltungs-Ordner → entity_view = accountant_view
Spalten: name, sku, price, cost, margin
Lagerverwalter und Buchhalter teilen sich die Tabelle, können aber die Spalten der jeweils anderen nicht sehen.
Formeln für Sicherheit auf Zeilenebene
Sicherheitsregeln auf Zeilenebene sind Formeln, die serverseitig zur Abfragezeit ausgewertet werden. Sie werden zu zusätzlichen WHERE-Klauseln auf jeder Abfrage, die der Anwender ausführt.
-- Anwender sehen nur Datensätze, die sie selbst angelegt haben
[created_by] = CURRENT_USER_ID()
-- Abteilungs-gebunden über Ordner-Einstellung
[department_id] = $[DepartmentId]
-- Mit Ordner-Filter kombinieren
[status] = 'active' AND [region] = $[UserRegion]
Über $[SettingName] referenzierte Einstellungen werden aus dem aktuellen Ordner aufgelöst und laufen die Eltern-Kette hinauf. So kann dieselbe Rolle Daten in verschiedenen Ordnern unterschiedlich einschränken, ohne pro Ordner Regeln zu schreiben.
Aktions-Berechtigungen
Eine Aktion ist für einen Anwender nur dann ausführbar, wenn alle der folgenden Bedingungen erfüllt sind:
- Der Anwender hat das
E-Recht auf dassec_objectder Aktion (über Rolle oder direkte Gewährung) - Die
canExecute-Formel der Aktion wird für die Ziel-Datensätze zutrueausgewertet - Der Anwender hat die für die Schreibvorgänge der Aktion erforderlichen Entitätsrechte
- Ordner-Spalten-Overrides verbieten nicht die Spalten, die die Aktion berührt
canExecute wird sowohl auf dem Client (für sofortiges Aktivieren/Deaktivieren des Buttons) als auch auf dem Server (Sicherheits-Re-Check vor der Ausführung) ausgewertet. Dem Ergebnis des Clients wird allein nie vertraut.
Bei Aktionen über mehrere Datensätze muss jeder ausgewählte Datensatz canExecute erfüllen — wenn auch nur einer fehlschlägt, ist der Button deaktiviert.
Ausgegraute Ordner
Wenn ein Anwender Zugriff auf einen Unterordner hat, aber nicht auf seinen Eltern-Ordner, wird der Eltern-Ordner im Sidebar-Baum als nicht auswählbar (ausgegraut, kein Klick-Verhalten) angezeigt. Sie können zu dem Unterordner navigieren, der ihnen gehört, ohne jemals Eltern-Daten zu sehen. Das hält den Navigationsbaum konsistent, ohne verbotene Daten zu offenbaren.
Berechtigungs-Auswertungsreihenfolge
Für jede Operation komponiert die Plattform die effektiven Berechtigungen aus diesen Schichten in dieser Reihenfolge:
- Modul-Zugriff
- Anwender-spezifische Ordner-Gewährungen
- Rollenbasierte Ordner-Gewährungen
- Feld-Overrides auf Ordner-Ebene
- Zeilen-Regeln auf Entitäts-Ebene
- Feld-Flags auf Feld-Ebene
canExecute-Formeln auf Aktions-Ebene
Die endgültige Berechtigung ist das Ergebnis aller kombinierten Schichten. Berechtigungen werden dynamisch pro Anfrage aus den Metadaten und dem aktuellen Ausführungs-Kontext berechnet — sie sind nicht im JWT-Token codiert.
Trennung der Auth-Datenbank
Anwenderkonten und Mandanten-Zugang leben in einer separaten Auth-Datenbank. Mandantendaten leben in mandantenspezifischen Datenbanken. Jeder Mandant hat seinen eigenen Datenbank-Benutzer mit Berechtigungen, die auf seine eigene Datenbank beschränkt sind, sodass eine Kompromittierung eines Mandanten die Daten eines anderen Mandanten nicht offenlegen kann.
| Datenbank | Tabellen | Zweck |
|---|---|---|
dforge_auth | user, tenant, user_tenant, session | Globale Identität und Mandanten-Zugriffs-Gewährungen |
dforge_<tenant> | module_role, sec_object, user_role, user_rights, folder, folder_entity, entity_view, entity_v_column, plus alle Modul-Schemas | Mandantenspezifische Sicherheit und Daten |
Siehe Administration für den täglichen Workflow zum Verwalten von Anwendern, Rollen und Ordnern.