3.2 KiB
3.2 KiB
Security Kernel — Verantwortungstabelle
Architektur-Prinzip
Authentifizierung → Tenant Membership → Capability-Prüfung → Objektfilter/ACL → RLS als letzte Barriere
Was was prüft
| Schicht | Verantwortung | Was geprüft wird | Wo implementiert |
|---|---|---|---|
| Authentifizierung | User identifizieren | Session-Cookie, CSRF-Token | deps.py:get_current_user() |
| Tenant Membership | User gehört zu Tenant | UserTenant.status == 'active' |
deps.py:get_current_user() |
| Capability (RBAC) | Darf User grundsätzlich Modul nutzen? | contacts:read, contacts:write, etc. |
require_permission() Decorator |
| Objekt-ACL | Darf User DIESEN Datensatz sehen? | owner_id == user_id OR shared via entity_permissions |
visibility.py:apply_visibility_filter() |
| ABAC | Darf User Datensatz mit bestimmten Attributen sehen? | Policy conditions (status, custom fields) | policy_service.py:apply_policy_filter() |
| RLS | Ist User im richtigen Tenant? | tenant_id == current_setting('app.current_tenant_id') |
PostgreSQL RLS Policies |
Was RLS NICHT mehr prüft (seit Migration 0069)
- ❌
owner_id— das machtvisibility.py - ❌
entity_permissions(sharing) — das machtvisibility.py - ❌
is_system_admin— das machtvisibility.py(überspringt Filter) - ❌ Business-Autorisierung — das macht die Application Layer
Was RLS nur noch prüft
- ✅
tenant_id == current_setting('app.current_tenant_id')— Tenant-Isolation - ✅
WITH CHECKfür INSERT/UPDATE — verhindert cross-tenant writes
Defense-in-Depth
RLS (PostgreSQL) → tenant_id Isolation (Safety Belt)
visibility.py (App) → tenant_id + owner_id + sharing (Vehicle Control)
Beide Schichten filtern tenant_id unabhängig voneinander. Selbst wenn eine Schicht versagt, blockt die andere cross-tenant Zugriff.
RLS Policies (nach Migration 0069)
Alle RLS-enabled Tabellen haben genau eine Policy:
CREATE POLICY {table}_tenant_isolation ON {table}
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid)
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::uuid)
Keine Business-Logic in RLS. Keine owner_id, keine sharing, keine permissions.
Session-Variablen
| Variable | Wert | Wo gesetzt |
|---|---|---|
app.current_tenant_id |
UUID des aktuellen Tenants | deps.py:set_tenant_context() |
app.tenant_id |
UUID des aktuellen Tenants (Alias) | deps.py:set_tenant_context() |
app.current_user_id |
UUID des aktuellen Users | deps.py:set_user_context() |
app.is_system_admin |
'true' oder 'false' |
deps.py:set_user_context() |
app.current_user_groups |
Komma-getrennte Group-IDs | deps.py:set_user_context() |
Test-Verifikation
8/8 Cross-Tenant Security Tests grün:
- ✅ visibility_filter_blocks_cross_tenant
- ✅ check_single_entity_access_cross_tenant
- ✅ get_visible_ids_tenant_scoped
- ✅ entity_permissions_tenant_scoped
- ✅ rls_tenant_isolation_policy_exists
- ✅ rls_enabled_on_tenant_tables
- ✅ rls_disabled_on_system_tables
- ✅ tenant_context_variable_consistency