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>
111 lines
4.9 KiB
Markdown
111 lines
4.9 KiB
Markdown
# 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.
|