# Enterprise RBAC, Scope Enforcement & Navigation Governance Audit **Datum:** 9. März 2026 **Anwendung:** TenantPilot / TenantAtlas **Stack:** Laravel 12, Filament v5, Livewire v4, PostgreSQL, Tailwind v4 **Auditor:** Enterprise SaaS Security & Architecture Audit **Scope:** RBAC, Multi-Tenancy, Scope Enforcement, Navigation Governance, Filament Authorization --- ## 1. Executive Security / Structure Assessment ### Gesamturteil: **Teilweise robust — mit gezieltem Hardening-Bedarf** Die Anwendung verfügt über ein **architektonisch durchdachtes, capability-first RBAC-Modell** mit klarer Trennung von Platform/Workspace/Tenant-Scopes. Die Kernarchitektur (CapabilityResolver, UiEnforcement, Membership-Manager, Policies) ist solide und enterprise-geeignet. Allerdings gibt es **strukturelle Lücken** in folgenden Bereichen: - System-Panel-Widgets aggregieren cross-workspace/cross-tenant Daten ohne Kompartimentierung - Einige Monitoring-Pages fehlen explizite `canAccess()` Checks und verlassen sich ausschließlich auf Middleware - AuditLog-Modell hat keine `workspace_id` Spalte und damit kein strukturelles Workspace-Scoping - Einige Modelle (OperationRun, BaselineProfile, AlertRule) haben keine Immutabilitäts-Traits am Model-Level - URL-basierte Enumeration von sequenziellen IDs ist möglich (Finding, OperationRun) ### Die 5 kritischsten RBAC-/Scope-Probleme | # | Problem | Severity | |---|---------|----------| | 1 | **System Panel leakt Cross-Workspace/Cross-Tenant Operational Data** — ControlTower-Widgets zeigen Workspace-Namen, Tenant-Namen und Fehlerstatistiken aller Mandanten an jeden PlatformUser | CRITICAL | | 2 | **Directory-Pages exponieren alle Tenants/Workspaces** — System/Directory-Seiten erlauben Aufzählung aller registrierten Tenants/Workspaces mit Health-Status und Permission-Gaps | HIGH | | 3 | **AuditLog hat kein strukturelles Workspace-Scoping** — Nur `tenant_id`, kein `workspace_id`; Scoping ist rein auf Query-Level und kann bei neuen Zugangspfaden umgangen werden | HIGH | | 4 | **Monitoring-Pages (Operations, AuditLog) fehlen `canAccess()` Checks** — Verlassen sich nur auf Middleware + Query-Scoping, nicht auf explizite Page-Level Authorization | MEDIUM | | 5 | **Sequenzielle IDs in URLs erlauben Enumeration** — Finding-IDs, OperationRun-IDs sind sequential integers in Deep Links und Notifications | MEDIUM | ### Die 5 wichtigsten Korrekturen | # | Maßnahme | Priorität | |---|----------|-----------| | 1 | System-Panel Kompartimentierung: Capability-Granularisierung für cross-workspace Sicht | P0 | | 2 | `canAccess()` auf allen Monitoring-Pages implementieren | P1 | | 3 | AuditLog-Modell um `workspace_id` ergänzen mit Migration + Scoping-Trait | P1 | | 4 | OperationRun + BaselineProfile + AuditLog mit DerivesWorkspaceId-Trait absichern | P2 | | 5 | UUID-basierte Route-Keys für sensitive Ressourcen (Finding, OperationRun) evaluieren | P2 | --- ## 2. Access Model Map ### Subjekte | Subjekt | Guard | Scope | Modell | |---------|-------|-------|--------| | **Platform User** | `platform` | System-weit | `PlatformUser` | | **Workspace Owner** | `web` | Workspace | `User` + `WorkspaceMembership(role=owner)` | | **Workspace Manager** | `web` | Workspace | `User` + `WorkspaceMembership(role=manager)` | | **Workspace Operator** | `web` | Workspace | `User` + `WorkspaceMembership(role=operator)` | | **Workspace Readonly** | `web` | Workspace | `User` + `WorkspaceMembership(role=readonly)` | | **Tenant Owner** | `web` | Tenant | `User` + `TenantMembership(role=owner)` | | **Tenant Manager** | `web` | Tenant | `User` + `TenantMembership(role=manager)` | | **Tenant Operator** | `web` | Tenant | `User` + `TenantMembership(role=operator)` | | **Tenant Readonly** | `web` | Tenant | `User` + `TenantMembership(role=readonly)` | ### Scopes | Scope | Kontext | Enforcement | |-------|---------|-------------| | **Platform** | System-Administration (break-glass, directory, ops) | `platform` Guard + PlatformCapabilities | | **Workspace** | Organisation/Account-Isolation | WorkspaceMembership + WorkspaceCapabilityResolver + WorkspaceContext (Session) | | **Tenant** | Mandant innerhalb eines Workspace | TenantMembership + CapabilityResolver + Filament::getTenant() | ### Kernobjekte pro Scope | Scope | Objekte | |-------|---------| | **Platform** | PlatformUser, Access Logs, Break-Glass-Sessions | | **Workspace** | Workspace, WorkspaceMembership, WorkspaceSetting, AlertRule, AlertDestination, BaselineProfile, OperationRun (multi-tenant) | | **Tenant** | Tenant, TenantMembership, Policy, PolicyVersion, BackupSet, BackupItem, BackupSchedule, RestoreRun, Finding, ReviewPack, InventoryItem, EntraGroup, ProviderConnection, TenantPermission, TenantRoleMapping | | **Ambivalent** | AuditLog (nur tenant_id, kein workspace_id), OperationRun (hat workspace_id, aber kein Immutabilitäts-Trait), AlertDelivery (workspace_id + optional tenant_id) | ### Capability-Muster **Klar und konsistent:** - Capability-first Design über `Capabilities::*` und `PlatformCapabilities::*` - Static Registry (`Capabilities::all()`, `Capabilities::isKnown()`) - Rolle→Capability Mapping über `RoleCapabilityMap` und `WorkspaceRoleCapabilityMap` - Gates werden dynamisch aus Capabilities registriert (AuthServiceProvider) - UiEnforcement erzwingt dreilagige Autorisierung (Visibility → Disabled → Server Guard) **Inkonsistent:** - Einige Policies nutzen `Gate::allows()`, andere nutzen `CapabilityResolver::can()` direkt - System-Panel nutzt separate `PlatformCapabilities` ohne Kompartimentierung innerhalb des System-Scopes - AuditLog hat keinen eigenen Capability-Check — Sichtbarkeit wird durch Page-Navigation gesteuert --- ## 3. Findings Table | ID | Severity | Kategorie | Datei / Klasse | Scope | UI-Symptom | Technische Ursache | Risiko | Empfohlene Korrektur | |----|----------|-----------|----------------|-------|------------|---------------------|--------|---------------------| | F-01 | **CRITICAL** | Cross-Scope Data Leak | `App\Filament\System\Widgets\ControlTowerTopOffenders` | System→All | Widget zeigt Top-10-Fehler mit Workspace+Tenant-Namen | Query ohne jegliche Scope-Filterung: `OperationRun::query()->selectRaw('workspace_id, tenant_id...')` | Jeder PlatformUser sieht Betriebsdaten aller Mandanten | Capability-basierte Kompartimentierung oder Anonymisierung im System-Panel | | F-02 | **CRITICAL** | Cross-Scope Data Leak | `App\Filament\System\Widgets\ControlTowerKpis` | System→All | KPI-Dashboard zeigt Gesamtstatistiken über alle Workspaces | Globale `OperationRun::query()` ohne WHERE-Klausel | Informations-Leaking über Betriebsumfang und Fehlerquoten | Scope-Filter oder Capability-Granularisierung | | F-03 | **CRITICAL** | Cross-Scope Data Leak | `App\Filament\System\Widgets\ControlTowerRecentFailures` | System→All | Zeigt letzte fehlgeschlagene Runs mit Workspace-Namen | Ungefilterter Query | Sensitive Betriebs-Metadaten sichtbar | Scope-aware oder anonymisiert | | F-04 | **HIGH** | Tenant Enumeration | `App\Filament\System\Pages\Directory\Tenants` | System | Directory listet alle Tenants mit Health, Status, Permission Gaps | `Tenant::query()->with('workspace')` ohne Scope | Reconnaissance: Angreifer sieht alle Mandanten, deren Konfigurationsprobleme und Provider-Status | Capability-basierter Zugang oder Scope-Einschränkung | | F-05 | **HIGH** | Workspace Enumeration | `App\Filament\System\Pages\Directory\Workspaces` | System | Directory listet alle Workspaces mit Tenant-Count und Fehlerquoten | `Workspace::query()` ohne Scope | Reconnaissance: vollständige Sicht auf Struktur und Probleme aller Organisationen | Wie F-04 | | F-06 | **HIGH** | Missing Structural Scope | `App\Models\AuditLog` | Tenant/Workspace | AuditLog-Einträge haben kein `workspace_id` | Model hat nur `tenant_id`, kein Workspace-Feld | Bei neuen Zugangspfaden oder Queries ohne Tenant-Kontext könnten Audit-Daten cross-workspace leaken | Migration: `workspace_id` Spalte + NOT NULL + DerivesWorkspaceIdFromTenant Trait | | F-07 | **MEDIUM** | Missing `canAccess()` | `App\Filament\Pages\Monitoring\Operations` | Admin | Seite ist über `/admin/operations` direkt erreichbar | Kein expliziter `canAccess()` Check, nur Middleware+Query-Scoping | Falls Middleware fehlschlägt oder umgangen wird, keine zweite Schutzschicht | `canAccess()` mit WorkspaceCapabilityResolver implementieren | | F-08 | **MEDIUM** | Missing `canAccess()` | `App\Filament\Pages\Monitoring\AuditLog` | Admin | Seite über `/admin/audit-log` erreichbar, `shouldRegisterNavigation=false` | Kein `canAccess()`, nur `shouldRegisterNavigation=false` und OperateHubShell | Hidden page ohne Page-Level Auth ist per URL erreichbar | `canAccess()` implementieren | | F-09 | **MEDIUM** | URL Enumeration | Notification Deep Links + Route `{run}` | Tenant | OperationRun-IDs in Notification-URLs sind sequential integers | `OperationRunLinks::tenantlessView($run)` nutzt Integer-ID | ID-Enumeration erlaubt Erkennung valider Run-IDs (Auth verhindert Lesezugriff) | UUID-basierte Route-Keys oder Rate-Limiting | | F-10 | **MEDIUM** | URL Enumeration | Finding-URLs in Tenant Panel | Tenant | Finding-IDs in Links: `/findings/140`, `/findings/139` | Sequential integer keys in URLs | Ähnlich wie F-09 | UUID-basierte Anzeige | | F-11 | **MEDIUM** | No Model Immutability | `App\Models\OperationRun` | Workspace/Tenant | N/A | Kein `DerivesWorkspaceIdFromTenant` Trait; workspace_id wird per boot() auto-filled, aber nicht immutabel erzwungen | Theoretische Reassignment-Möglichkeit (kein aktueller Exploit-Pfad) | DerivesWorkspaceIdFromTenant Trait hinzufügen | | F-12 | **MEDIUM** | No Model Immutability | `App\Models\BaselineProfile` | Workspace | N/A | Kein Scoping-Trait; workspace_id muss bei Creation gesetzt werden | Developer-Fehler bei zukünftigen Code-Änderungen möglich | Trait oder DB-Constraint | | F-13 | **LOW** | Break-Glass Scope | `App\Filament\System\Pages\RepairWorkspaceOwners` | System | PlatformUser kann Owner für beliebigen Workspace zuweisen | Nur `USE_BREAK_GLASS` Capability geprüft, keine Workspace-Zugehörigkeitsvalidierung | Intentional (Emergency-Feature), aber unkompartimentiert | Dokumentation und Audit-Alerting sicherstellen; optional: Require 4-eyes | | F-14 | **LOW** | Access Log Exposure | `App\Filament\System\Pages\Security\AccessLogs` | System | Zeigt alle Platform-Logins und Break-Glass-Aktivitäten | Nur `CONSOLE_VIEW` Capability erforderlich | Offenlegen, wer System-Zugang hat und wann | Separate Capability `SECURITY_AUDIT_VIEW` | | F-15 | **LOW** | Livewire Auth Gap | `App\Livewire\SettingsCatalogSettingsTable` | Tenant | Liest Policy-Einstellungen ohne eigene Auth | Kein eigener Authorization-Check; angenommen: nur auf autorisierten Seiten gemounted | Falls auf anderer Seite gemounted, Daten-Leak möglich | Defensive Auth prüfen oder Assert bei mount() | --- ## 4. Mismatch Matrix | Dimension | Status | Details | |-----------|--------|---------| | **Navigation Visibility ↔ Policy Enforcement** | ✅ Konsistent für Tenant-Panel | Navigation items nutzen `canViewAny()` + Policy-Checks; UiEnforcement erzwingt Dreischicht-Modell | | **Navigation Visibility ↔ Policy Enforcement** | ⚠️ Inkonsistent für Monitoring-Pages | Operations und AuditLog sind `shouldRegisterNavigation=false` aber per URL erreichbar ohne `canAccess()` | | **Navigation Visibility ↔ Policy Enforcement** | ⚠️ Inkonsistent für System-Panel | System-Pages haben nur `DIRECTORY_VIEW`/`OPERATIONS_VIEW` Capability, aber zeigen Daten aller Mandanten | | **Query/Data Scope ↔ Navigation Scope** | ✅ Konsistent für Tenant-scoped Resources | `getEloquentQuery()` filtert konsequent nach `Tenant::current()` | | **Query/Data Scope ↔ Navigation Scope** | ✅ Konsistent für Workspace-scoped Resources | `getEloquentQuery()` filtert nach `WorkspaceContext::currentWorkspaceId()` mit `whereRaw('1 = 0')` als Fallback | | **Query/Data Scope ↔ Navigation Scope** | ⚠️ System-Widgets: Kein Scope, Navigation erlaubt | ControlTower-Widgets haben weder Query-Scope noch Navigation-Gate | | **Route Accessibility ↔ Authorization** | ✅ Tenant Panel Seiten | Middleware-Stack (`ensure-workspace-selected`, `ensure-filament-tenant-selected`, `DenyNonMemberTenantAccess`) | | **Route Accessibility ↔ Authorization** | ✅ Tenantless Run Viewer | `mount()` → `$this->authorize('view', $run)` → OperationRunPolicy prüft Workspace+Tenant+Capability | | **Route Accessibility ↔ Authorization** | ⚠️ AuditLog Page | `shouldRegisterNavigation=false`, aber kein `canAccess()` — per URL erreichbar | | **Action Auth ↔ Bulk Action Auth** | ✅ Konsistent | UiEnforcement erzwingt all-or-nothing Semantik für Bulk Actions mit gleicher Capability wie Single Actions | | **Widget Data ↔ User Scope** | ✅ Dashboard-Widgets | `DashboardKpis`, `NeedsAttention`, `RecentOperations` nutzen `Filament::getTenant()` korrekt | | **Widget Data ↔ User Scope** | ✅ AlertsKpiHeader | Filtert nach workspace_id + user.tenantMemberships + optional activeTenant | | **Widget Data ↔ User Scope** | ⚠️ ControlTower-Widgets | Keine Scope-Filterung — zeigen Platform-weite Aggregate | | **Global Search ↔ Scope** | ✅ ScopesGlobalSearchToTenant Trait | Prüft `canAccessTenant()` und filtert per `whereBelongsTo()` | | **Global Search ↔ Scope** | ⚠️ Trait-Nutzung verifizieren | Unklar ob alle globalsuchbaren Resources diesen Trait nutzen | | **Relation Managers ↔ Parent Scope** | ✅ Konsistent | TenantMembershipsRelationManager, BackupItemsRelationManager etc. erben Parent-Scope des owning Resource | | **Deep Links ↔ Auth** | ✅ Notification-URLs | OperationRunPolicy prüft Workspace+Tenant bei mount() | | **Deep Links ↔ Auth** | ⚠️ IDs in URLs | Sequential integers ermöglichen Enumeration | --- ## 5. Target Enterprise RBAC Model ### Rollen-/Capability-Modell (Zielzustand) ``` Platform Layer ├── Platform Admin: Full system access ├── Platform Ops: Operations monitoring (scoped) ├── Platform Audit: Security audit view only └── Platform Support: Break-glass + Directory (audited, 4-eyes for critical) Workspace Layer ├── Owner: Full workspace control + all tenant capabilities ├── Manager: Workspace admin (no archive/delete) + most tenant capabilities ├── Operator: Execute operations + view └── Readonly: View-only Tenant Layer ├── Owner: Full tenant control ├── Manager: Admin (no delete/membership manage) ├── Operator: Execute sync/backup/run + view └── Readonly: View-only ``` ### Regeln für Navigation Visibility 1. **Navigation zeigt NUR Items, für die der Nutzer authorization hat** — sowohl `shouldRegisterNavigation()` als auch `canAccess()` müssen konsistent sein 2. **Hidden Pages müssen `canAccess()` implementieren** — `shouldRegisterNavigation=false` bedeutet nicht "keine Autorisierung nötig" 3. **System-Panel Navigation muss capability-granular sein** — nicht eine einzige `CONSOLE_VIEW` Capability für alles 4. **Navigation darf niemals die einzige Schutzschicht sein** ### Regeln für Query Scope 1. **Jede `getEloquentQuery()` muss scope-aware sein** — mit `whereRaw('1 = 0')` als Fallback bei fehlendem Kontext 2. **Tenant-scoped Modelle nutzen `DerivesWorkspaceIdFromTenant` oder `$tenantOwnershipRelationshipName`** 3. **Workspace-scoped Modelle filtern nach `WorkspaceContext::currentWorkspaceId()`** 4. **System-Queries (ControlTower) müssen entweder scope-kompartimentiert oder anonymisiert sein** 5. **AuditLog muss `workspace_id` als strukturelles Scope-Feld haben** ### Regeln für Direct URL Access 1. **Jede Page muss `canAccess()` implementieren** — keine Ausnahme für "hidden" Pages 2. **Route Model Binding muss Scope-Ownership erzwingen** — OperationRunPolicy-Muster als Standard 3. **UUID-basierte Route-Keys für sensitive Ressourcen** — Findings, OperationRuns 4. **Rate-Limiting auf Detail-Endpoints** — gegen Enumeration ### Regeln für Actions / Bulk Actions 1. **UiEnforcement ist der Standard für alle Actions** — kein Bypass 2. **Destructive Actions: `->requiresConfirmation()` + `->action(...)` + Capability-Check** 3. **Bulk Actions: all-or-nothing Semantik (bereits implementiert)** 4. **Keine Action darf schwächer autorisiert sein als die entsprechende Single-Record-Action** ### Regeln für Widgets, Search, Exports und Deep Links 1. **Widgets dürfen nur scope-konforme Daten anzeigen** — kein Count über unerlaubte Scopes 2. **Global Search muss `ScopesGlobalSearchToTenant` nutzen** — für alle tenant-scoped Resources 3. **Deep Links in Notifications müssen Policy-geschützt sein** — bereits implementiert, UUID-Keys evaluieren 4. **Export-Funktionen müssen dieselbe Scope-Filterung wie die UI anwenden** --- ## 6. Hardening Recommendations ### Quick Wins (P0-P1, sofort umsetzbar) | # | Maßnahme | Nutzen | Risiko | Aufwand | Priorität | |---|----------|--------|--------|---------|-----------| | QW-1 | `canAccess()` auf `Operations.php` implementieren — WorkspaceCapabilityResolver + Membership Check | Schließt Page-Level Auth Lücke | Minimal: bestehende Pattern kopieren | 1h | P1 | | QW-2 | `canAccess()` auf `AuditLog.php` implementieren | Wie QW-1 | Minimal | 1h | P1 | | QW-3 | System-Panel Capability-Granularisierung: `OPERATIONS_VIEW` in `OPS_VIEW_RUNS`, `OPS_VIEW_FAILURES`, `OPS_VIEW_STUCK` aufteilen | Feinere Zugriffskontrolle | Migration + PlatformUser Capability-Update | 4h | P1 | | QW-4 | ControlTower-Widgets: Option für anonymisierte/aggregierte Ansicht | Verhindert Tenant-Namen-Leak | Widget-Refactor | 4h | P1 | | QW-5 | `SettingsCatalogSettingsTable` Livewire: Defensive Auth in `mount()` | Verhindert Context-Leak bei Wiederverwendung | Minimal | 30min | P2 | ### Mittlere Hardening-Maßnahmen (P2, geplant) | # | Maßnahme | Nutzen | Risiko | Aufwand | Priorität | |---|----------|--------|--------|---------|-----------| | MH-1 | AuditLog Migration: `workspace_id` Spalte + NOT NULL + Index | Strukturelles Workspace-Scoping | Backfill-Migration nötig | 8h | P2 | | MH-2 | `DerivesWorkspaceIdFromTenant` auf OperationRun-Model | Immutabilitäts-Garantie | Sehr gering | 2h | P2 | | MH-3 | `DerivesWorkspaceIdFromTenant` auf BaselineProfile-Model | Wie MH-2 | Gering | 2h | P2 | | MH-4 | UUID-basierte Route-Keys für Finding, OperationRun | Verhindert ID-Enumeration | Model+Migration+Route-Refactor | 12h | P2 | | MH-5 | Rate-Limiting auf `/admin/operations/{run}` und ähnliche Detail-Endpoints | Anti-Enumeration | Middleware-Config | 2h | P2 | | MH-6 | System-Panel Directory: Capability `DIRECTORY_MANAGE` vs `DIRECTORY_VIEW` differenzieren | Least-Privilege im System-Panel | Capability-Registry Update | 4h | P2 | | MH-7 | Break-Glass: 4-eyes-Prinzip oder temporal scoping verschärfen | Privileged-Access-Governance | UX-Design nötig | 8h | P2 | ### Strukturelle Refactors (P3, Spec-gesteuert) | # | Maßnahme | Nutzen | Risiko | Aufwand | Priorität | Spec? | |---|----------|--------|--------|---------|-----------|-------| | SR-1 | Global Scope auf OperationRun für automatisches Workspace-Scoping | Präventiv gegen zukünftige Query-Fehler | Model-Behavior-Änderung; Tests nötig | 16h | P3 | Ja | | SR-2 | System-Panel Kompartimentierung: Scope-basierte Platform Views | Enterprise isolation für managed-service Betrieb | Architektur-Refactor | 40h | P3 | Ja | | SR-3 | Einheitliche `canAccess()` Base-Mixin für alle Pages | DRY + Konsistenz | Base-Class Refactor | 16h | P3 | Ja | | SR-4 | Monitoring-Pages: Unified OperateHub Authorization Layer | Konsolidierte Auth statt Page-for-Page | Architektur | 24h | P3 | Ja | --- ## 7. Project Rules ### Verbindliche Enterprise RBAC Rules 1. **Navigation ist nie die Sicherheitsgrenze.** Jede Filament-Page muss eine `canAccess()` Methode implementieren, die unabhängig von Navigation Visibility autorisiert. `shouldRegisterNavigation=false` ersetzt keinen Authorization-Check. 2. **Jede sichtbare Aktion braucht deckungsgleiche Server-Autorisierung.** UiEnforcement (Tenant) oder WorkspaceUiEnforcement (Workspace) ist der Standard-Wrapper für alle Actions. Kein Action-Handler darf ohne Server-Side Guard ausgeführt werden. 3. **Hidden Pages dürfen nicht unautorisiert per URL erreichbar sein.** Jede Page mit `shouldRegisterNavigation=false` oder `isDiscovered=false` MUSS explizit `canAccess()` implementieren. 4. **Workspace- und Tenant-Scope müssen in Query, Route Binding und Policy konsistent erzwungen werden.** Scope-Enforcement findet auf mindestens zwei Ebenen statt: Query-Level (`getEloquentQuery()`) UND Policy/Gate-Level. 5. **Widgets, Counts und Search dürfen keine scope-fremden Metadaten leaken.** Jedes Dashboard-Widget muss denselben Scope-Filter anwenden wie die zugehörige Tabelle/Resource. System-Widgets müssen scope-kompartimentiert oder anonymisiert sein. 6. **Bulk Actions dürfen nie schwächer autorisiert sein als Single Actions.** UiEnforcement erzwingt all-or-nothing Semantik: Wenn ein Record in der Selektion die Capability verletzt, wird die gesamte Bulk-Action blockiert. 7. **Deep Links und Notifications müssen kanonisch und permission-sicher sein.** Jede URL, die per Notification, E-Mail oder API-Response geteilt wird, muss bei Zugriff denselben Auth-Check durchlaufen wie der UI-Pfad. 8. **Route Model Binding muss Scope-Ownership erzwingen.** Policies müssen bei `view()` immer Workspace-Membership UND Tenant-Membership (falls tenant-scoped) prüfen. Non-Members erhalten 404 (deny-as-not-found), nicht 403. 9. **Capability-first vor rollen-/UI-getriebener Logik.** Autorisierung leitet sich aus Capabilities ab, nicht aus Rollen-Strings oder UI-Zustand. `RoleCapabilityMap` ist die Single Source of Truth. 10. **Kein Zugriff über "zufällig funktionierende" indirekte Pfade.** Jeder neue Pfad (Relation Manager, Tab, Widget, Global Search, Export) muss explizit scope-geprüft werden. Filament Auto-Discovery allein ist keine Autorisierung. 11. **Modelle mit Tenant- oder Workspace-Zugehörigkeit MÜSSEN Immutabilitäts-Traits nutzen.** `DerivesWorkspaceIdFromTenant` oder vergleichbare Traits stellen sicher, dass Scope-Zuweisungen nach Creation nicht verändert werden können. 12. **System-Panel Capabilities müssen granular sein.** Eine einzige `CONSOLE_VIEW` Capability für cross-workspace Sichtbarkeit reicht für Enterprise-Betrieb nicht aus. Differenzierung in Operations, Directory, Security, Break-Glass. 13. **Destructive Actions erfordern immer `->requiresConfirmation()`.** Keine Ausnahme, auch nicht bei "schnellen" Aktionen wie Archive oder Toggle. 14. **AuditLog muss workspace-scoped sein.** Jeder Audit-Eintrag muss `workspace_id` tragen, um cross-workspace Reporting und Compliance zu ermöglichen. --- ## 8. Spec Proposal Backlog ### Spec A: System Panel Kompartimentierung | Feld | Wert | |------|------| | **Titel** | System Panel Capability Granularization & Data Compartmentalization | | **Problem** | Alle PlatformUser mit `CONSOLE_VIEW` sehen Betriebsdaten aller Workspaces/Tenants. ControlTower-Widgets, Directory-Pages und Access-Logs bieten keine Scope-Isolation. | | **Ziel** | PlatformCapabilities aufteilen in `OPS_VIEW_RUNS`, `OPS_VIEW_FAILURES`, `DIRECTORY_VIEW_TENANTS`, `DIRECTORY_VIEW_WORKSPACES`, `SECURITY_AUDIT_VIEW`. ControlTower-Widgets anonymisieren oder scope-kompartimentieren. | | **Scope** | System Panel Widgets, Directory Pages, Access Logs, PlatformCapabilities Registry | | **Non-goals** | Nicht: Umbau der web-Guard-Architektur; nicht: Multi-Panel Redesign | | **Priorität** | P1 — Enterprise-Compliance-Risiko | | **Abhängigkeiten** | Keine | ### Spec B: Page-Level Authorization Enforcement | Feld | Wert | |------|------| | **Titel** | Enforce canAccess() on all Filament Pages | | **Problem** | Mehrere Pages (Operations, AuditLog, potentiell weitere) haben keinen expliziten `canAccess()` Check und verlassen sich nur auf Middleware | | **Ziel** | Alle Pages im Admin/Tenant/System Panel implementieren `canAccess()` mit CapabilityResolver/WorkspaceCapabilityResolver | | **Scope** | Alle Filament Pages; PHPStan oder Pest-Regel zur Enforcement-Prüfung | | **Non-goals** | Nicht: Middleware-Umbau; nicht: UiEnforcement-Refactor | | **Priorität** | P1 — Defense-in-Depth | | **Abhängigkeiten** | Keine | ### Spec C: AuditLog Workspace Scoping | Feld | Wert | |------|------| | **Titel** | Add workspace_id to AuditLog for Structural Scope Isolation | | **Problem** | AuditLog hat nur `tenant_id`, kein `workspace_id`. Query-Level-Scoping ist fragil und kann bei neuen Zugangspfaden umgangen werden. | | **Ziel** | Migration: `workspace_id` Spalte (nullable → later NOT NULL); Backfill-Job; DerivesWorkspaceIdFromTenant-Trait; Index | | **Scope** | AuditLog Model, Migration, Backfill, Audit-Service Updates | | **Non-goals** | Nicht: AuditLog Schema-Redesign; nicht: Retention-Policy | | **Priorität** | P2 | | **Abhängigkeiten** | Keine | ### Spec D: Model Immutability Enforcement | Feld | Wert | |------|------| | **Titel** | Apply DerivesWorkspaceId Trait to OperationRun, BaselineProfile, AlertRule | | **Problem** | OperationRun, BaselineProfile, AlertRule haben keine Model-Level Immutabilitäts-Garantie für workspace_id. Scope-Zuweisungen könnten theoretisch nach Creation verändert werden. | | **Ziel** | DerivesWorkspaceIdFromTenant oder DerivesWorkspaceIdFromTenantWhenPresent Trait auf alle scope-relevanten Modelle anwenden; WorkspaceIsolationViolation als Guard | | **Scope** | OperationRun, BaselineProfile, AlertRule, AlertDestination Models | | **Non-goals** | Nicht: Global Scope Enforcement (separate Spec) | | **Priorität** | P2 | | **Abhängigkeiten** | Keine | ### Spec E: UUID Route Keys für Sensitive Resources | Feld | Wert | |------|------| | **Titel** | UUID-based Route Keys for Findings, OperationRuns, and Sensitive Records | | **Problem** | Sequential integer IDs in URLs ermöglichen Enumeration. Notification-Deep-Links und Tabellen-Links enthalten vorhersagbare IDs. | | **Ziel** | getRouteKeyName() auf `external_id` (UUID) umstellen für Finding, OperationRun; Migration für UUID-Spalte; URL-Refactor | | **Scope** | Finding, OperationRun, ReviewPack Models; Routes; Notification Links | | **Non-goals** | Nicht: Alle Models umstellen; nicht: bestehende DB-PKs ändern | | **Priorität** | P2 | | **Abhängigkeiten** | Keine | ### Spec F: OperateHub Unified Authorization Layer | Feld | Wert | |------|------| | **Titel** | Unified Authorization Layer for Monitoring/OperateHub Pages | | **Problem** | Monitoring-Pages (Operations, Alerts, AuditLog) haben unterschiedliche Auth-Patterns. Operations nutzt kein `canAccess()`, Alerts nutzt WorkspaceCapabilityResolver, AuditLog nutzt keines. | | **Ziel** | Shared Trait oder Base-Page-Klasse für OperateHub-Pages mit einheitlicher `canAccess()` + Capability-Mapping | | **Scope** | OperateHub Pages, Monitoring Cluster, Shared Trait | | **Non-goals** | Nicht: Redesign der OperateHubShell Navigation-Logik | | **Priorität** | P2 | | **Abhängigkeiten** | Spec B (Page-Level Auth Enforcement) | ### Spec G: Global Search Scope Verification & Trait Coverage | Feld | Wert | |------|------| | **Titel** | Verify and Enforce ScopesGlobalSearchToTenant Usage on All Searchable Resources | | **Problem** | `ScopesGlobalSearchToTenant` Trait existiert und ist gut implementiert, aber es ist unklar, ob alle global-suchbaren Resources ihn nutzen. | | **Ziel** | Audit: Welche Resources sind global-suchbar? Nutzen alle den Trait? Pest-Test für Enforcement. | | **Scope** | Alle Filament Resources mit $recordTitleAttribute oder globallySearchable | | **Non-goals** | Nicht: Redesign der Global-Search-Architektur | | **Priorität** | P2 | | **Abhängigkeiten** | Keine | --- ## Appendix: Positive Architecture Highlights ### Was gut funktioniert 1. **Capability-first Design** mit `Capabilities::all()` als Single Source of Truth und dynamischer Gate-Registrierung 2. **UiEnforcement** als dreilagiger RBAC-Wrapper (Visibility → Disabled → Server Guard) — Enterprise-Grade 3. **Deny-as-not-found Semantik** — Non-Members bekommen 404, nicht 403 (verhindert Existence-Leaking) 4. **DerivesWorkspaceIdFromTenant Trait** — Model-Level Immutabilität mit `WorkspaceIsolationViolation` Exception 5. **Last-Owner Guard** — Verhindert Removal/Demotion des letzten Workspace/Tenant Owners 6. **Workspace 7-Step Selection Algorithm** — Robust mit Stale-Session-Handling und Audit-Events 7. **Tenant Middleware Stack** — 4 Middleware-Layer (correct guard → workspace selected → tenant selected → deny non-member) 8. **Break-Glass Auditing** — Vollständig auditiert mit TTL, explicit start/exit, IP-Logging 9. **OperateHubShell** — Konsistente Cross-Tenant-Sicht mit Membership-Validation und Entitlement-Prüfung 10. **WriteGateInterface** — Hardening-Layer gegen unsichere RBAC-Zustände bei Intune-Writes ### Zusammenfassung Die Architektur zeigt ein **reifes, mehrschichtiges Autorisierungsmodell**, das für die meisten Enterprise-Szenarien robust ist. Die identifizierten Lücken betreffen primär: 1. **System-Panel**: Unzureichende Kompartimentierung (alle PlatformUser sehen alles) 2. **Monitoring-Pages**: Fehlende explizite Page-Level Auth (Defense-in-Depth Lücke) 3. **AuditLog**: Fehlendes strukturelles Workspace-Scoping 4. **ID-Enumeration**: Sequenzielle IDs in URLs (kein Datenleak, aber Reconnaissance-Vektor) Keines dieser Findings ermöglicht aktuell einen direkten Daten-Leak oder Privilege-Escalation über die bestehenden Middleware- und Policy-Checks hinaus. Die Maßnahmen dienen der **Enterprise-Härtung und Defense-in-Depth**, nicht der Schließung aktiver Sicherheitslücken.