Neue, bewusst von tenant_memberships/saas_user_has_role() komplett getrennte Platform-Admin-Ebene (neue Tabelle platform_admins), damit die bestehende, automatisiert getestete Mandanten-Isolation (check-m8-tenant-isolation.php, check-m8-role-matrix.php) unangetastet bleibt - Platform-Admin-Rechte wirken ausschliesslich auf den neuen backoffice-*.php-Seiten. - backoffice.php: Uebersicht aller Mandanten (Status, Teilnehmerzahl, Saldensumme). - backoffice-mandant.php: reine Leseansicht eines Mandanten (Einstellungen, Mitglieder/Rollen, letzte Buchungen, letzte Admin-Aktionen). Bewusst kein Schreibzugriff von hier aus. - backoffice-export.php: nutzt dieselbe app_export_tenant_data() wie der Selbstbedienungs-Export, ausgeloest durch den Platform-Admin fuer beliebige Mandanten. - scripts/grant-platform-admin.php: CLI-only Bootstrap fuer den ersten Platform-Admin, bewusst keine Web-UI dafuer. - Jede Back-Office-Ansicht/-Export wird im Audit-Log DES BETROFFENEN MANDANTEN protokolliert (Transparenzpflicht), nicht nur beim Betreiber. Der bestehende Selbstbedienungs-Export (datenexport.php aus M8) bleibt zusaetzlich bestehen statt ersetzt zu werden: der Mandant ist im AV- Verhaeltnis Verantwortlicher, Art. 15/20/28 DSGVO verpflichten den Auftragsverarbeiter zur Unterstuetzung bei Ausk''unfts-/Portabilitaets- rechten - ein jederzeit verfuegbarer Mandanten-Export erfuellt das direkt. Details und Begruendung in docs/backoffice.md. Live getestet: Back-Office zeigt alle Mandanten korrekt (inkl. echter Bestandsmandanten), Detail/Export fuer Test-Mandant funktioniert, Audit-Log korrekt geschrieben. Kritischer Test bestanden: derselbe Platform-Admin sieht auf normalen Mandanten-Seiten weiterhin nur seinen eigenen Mandanten; ein eingeloggter Nicht-Platform-Admin bekommt 403. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.9 KiB
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
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:
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 dieselbeapp_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.