# Back-Office (Platform-Admin) Stand: 2026-07-16 Ergänzung außerhalb der ursprünglichen M0–M9-Meilensteine: ein Betreiber-Zugang, der mandantenübergreifend alle Kunden einsehen kann – getrennt von der normalen, streng mandantengebundenen Rollenlogik. ## Design-Entscheidung: getrennt von `tenant_memberships` Platform-Admin-Rechte sind bewusst **kein** Teil von `saas_user_has_role()`/`tenant_memberships`, sondern eine eigene, komplett separate Tabelle `platform_admins` (`user_id` → `users.id`). Das ist kein Zufall: Alle bestehenden Mandanten-Seiten und die automatisierten Isolationstests (`scripts/check-m8-tenant-isolation.php`, `scripts/check-m8-role-matrix.php`) verlassen sich darauf, dass ein Login niemals automatisch mandantenübergreifenden Zugriff bekommt. Ein Platform-Admin-Flag direkt in `tenant_memberships` oder `users` einzubauen hätte dieses Fundament unterlaufen können. Stattdessen prüft ausschließlich der neue Code-Pfad in `app/platform-admin.php` (`app_require_platform_admin()`) auf Back-Office-Seiten die Platform-Admin-Eigenschaft; alle bestehenden Seiten sind unverändert und bleiben strikt mandantengebunden. ## Umgesetzte Dateien ```text database/migrations/0013_saas_platform_admins.sql app/platform-admin.php scripts/grant-platform-admin.php backoffice.php backoffice-mandant.php backoffice-export.php ``` ## Erster Platform-Admin Es gibt bewusst **keine Weboberfläche**, um den ersten Platform-Admin zu setzen – das wäre ein öffentlich erreichbarer "werde Admin"-Endpunkt und ein erhebliches Risiko. Stattdessen: Person registriert sich normal als Mandant (oder nutzt einen bestehenden Login), danach per Shell-Zugriff auf dem Server: ```bash php scripts/grant-platform-admin.php person@example.com ``` Das Skript prüft, dass der Account existiert, und legt nur dann den `platform_admins`-Eintrag an. Zugang entziehen aktuell nur per direktem `DELETE FROM platform_admins WHERE user_id = ?` (keine UI dafür – aus demselben Grund wie beim Setzen). ## Funktionsumfang - `backoffice.php`: Übersicht aller Mandanten mit Kürzel, Status, Erstelldatum, Teilnehmerzahl (aktiv/gesamt) und Saldensumme. - `backoffice-mandant.php?tenant_id=X`: reine Leseansicht eines einzelnen Mandanten – Einstellungen, Mitglieder mit Rolle, letzte 20 Buchungen, letzte 20 Admin-Aktionen. Absichtlich **kein** Schreibzugriff auf Mandantendaten von hier aus, um das Risiko einer versehentlichen Fremdänderung auszuschließen. - `backoffice-export.php`: nutzt dieselbe `app_export_tenant_data()`- Funktion wie der Selbstbedienungs-Export, aber ausgelöst durch den Platform-Admin für einen beliebigen Mandanten. ## Verhältnis zum Selbstbedienungs-Export (`datenexport.php`) Bewusste Entscheidung: Der bestehende Selbstbedienungs-Export für Mandanten-Owner/Admin (`datenexport.php`, aus M8) bleibt zusätzlich bestehen, statt ihn durch das Back-Office zu ersetzen. Begründung: Der Mandant ist im Auftragsverarbeitungs-Verhältnis der Verantwortliche (Controller), der Betreiber dieser App der Auftragsverarbeiter (Processor). Art. 15/20 DSGVO geben den betroffenen Personen ein Auskunfts-/Portabilitätsrecht, und Art. 28 DSGVO verpflichtet den Auftragsverarbeiter, den Verantwortlichen bei der Erfüllung dieser Rechte zu unterstützen sowie Daten am Vertragsende zurückzugeben – ein jederzeit verfügbarer Selbstbedienungs-Export erfüllt genau das. Das Back-Office ergänzt das um einen Betreiber-seitigen Zugriff für Support, Migrationen oder eigene Nachweispflichten, ersetzt den Mandanten-Selbstexport aber nicht. ## Audit-Trail Jede Back-Office-Ansicht und jeder Back-Office-Export wird im Audit-Log **des betroffenen Mandanten** protokolliert (`backoffice.tenant_viewed`, `backoffice.tenant_exported`, mit dem Platform-Admin als `actor_user_id`). Ein Mandant sieht damit über sein eigenes Protokoll (`mandant-einstellungen.php`), wann der Betreiber auf seine Daten zugegriffen hat – Transparenzpflicht statt stiller Einsicht. ## Prüfstatus - `scripts/check-m8-tenant-isolation.php`: weiterhin grün mit 10 Assertions – das Back-Office berührt die geprüften Pfade nicht. - `scripts/check-m8-role-matrix.php`: weiterhin grün mit 55 Assertions. - HTTP-Smoke: grün mit 30 geprüften Seiten (zwei neue Login-Schutz-Checks für `backoffice.php`/`backoffice-mandant.php`). - Golden Master: grün mit 104 Assertions. Live getestet: eigens angelegter Test-Mandant, per CLI-Skript zum Platform-Admin gemacht, Back-Office-Übersicht zeigt korrekt alle Mandanten (inklusive der echten Bestandsmandanten), Detailansicht und Export für den eigenen Test-Mandanten funktionieren, Audit-Log-Einträge korrekt geschrieben. Kritischer Isolationstest bestätigt: derselbe Platform-Admin sieht auf regulären Mandanten-Seiten (`kaffeeliste.php`) weiterhin ausschließlich seinen eigenen Mandanten. Ein zweiter, eingeloggter, aber nicht privilegierter Testnutzer bekommt auf `backoffice.php` korrekt `403 Forbidden`. Alle Testdaten anschließend vollständig entfernt.