Files
kaffeekasse-saas/docs/backoffice.md
T
clemensandClaude Opus 5 d24ee9bf91 Betreiber-Zentralstelle, Mandanten-Design und schlankerer Einstieg
Funktions-Schalter je Mandant (app/features.php, tenant_features): der
Betreiber schaltet FAQ, Selbsteintrag, PDF, PayPal-Eingang, CSV-Import,
Mailversand, Jahresabschluss, Datenexport und eigenes Design pro Mandant
frei. Gesperrte Funktionen verschwinden aus Menue und Schaltflaechen, ihre
Seiten weisen Aufrufe und POSTs ab. Das Back-Office ist jetzt fuer
Platform-Admins im Menue verlinkt statt nur per URL erreichbar.

Eigenes Design je Mandant: Akzentfarbe und Logo in den Mandant-
Einstellungen, eingebettet ueber app/branding.php; das Logo liegt
geschuetzt in var/tenant_logos und wird nur ueber
tenant-logo-anzeigen.php an den eigenen Mandanten ausgeliefert.

Weniger Startinformationen: Startpaket neuer Mandanten auf zwei
Beispielfragen gekuerzt, Anleitung von ~1400 auf ~750 Woerter gestrafft
und um Abschnitte zu gesperrten Funktionen bereinigt.

Vorder-/Rueckseite erst bei mehr als 50 Personen (vorher schon ab 50).
Die Schwelle liegt jetzt gemeinsam in app/ledger.php und gilt auch fuer
die Vorder-/Rueckseiten-Auswahl beim Erfassen, wo sie bisher unabhaengig
von der Teamgroesse angeboten wurde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 22:52:55 +02:00

8.0 KiB
Raw Blame History

Back-Office (Platform-Admin)

Stand: 2026-07-16

Ergänzung außerhalb der ursprünglichen M0M9-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_idusers.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: Ansicht eines einzelnen Mandanten Einstellungen, Mitglieder mit Rolle, letzte 20 Buchungen, letzte 20 Admin-Aktionen, dazu die Funktions-Schalter (siehe unten). Absichtlich kein Schreibzugriff auf die eigentlichen Mandantendaten (Buchungen, Mitglieder, Einstellungen des Kunden) 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.

Funktions-Schalter je Mandant

Stand: 2026-08-17. Zentralstelle des Betreibers, um einzelne Funktionen pro Mandant freizuschalten oder zu sperren gepflegt in backoffice-mandant.php, umgesetzt in app/features.php und der Tabelle tenant_features (Migration 0025_tenant_features.sql).

Schaltbar sind: FAQ-Seite, Striche selbst eintragen, Kaffeeliste als PDF, PayPal-Zahlungseingang, CSV-Import, Info-Mails und Erinnerungen, Jahresabschluss, Datenexport, eigenes Design.

Design-Entscheidungen:

  • Getrennt von tenant_settings. Dort stellt der Kunde ein, wie eine Funktion arbeitet; hier entscheidet der Betreiber, ob sie ihm überhaupt zur Verfügung steht. Beide Prüfungen stehen nebeneinander, z. B. beim Selbsteintrag: self_entry_enabled (Kunde) und Feature self_entry (Betreiber).
  • Ohne Zeile = freigeschaltet. tenant_features hält nur die Abweichungen vom Standard; ein neuer Mandant bekommt automatisch den vollen Funktionsumfang, ohne dass der Betreiber etwas einschalten muss. Fehlt die Tabelle (Migration noch nicht ausgerollt), bleibt ebenfalls alles freigeschaltet eine ausstehende Migration darf keine Funktion sperren.
  • Gesperrt heißt unsichtbar. Die betroffenen Menüpunkte und Schaltflächen verschwinden (footer.php, kaffeeliste.php, einzahlung.php, konto.php); ein direkter Aufruf der Seite endet mit einem Hinweis statt mit einem Fehler. Die POST-Verarbeitung der betroffenen Seiten hängt an derselben Prüfung, eine gesperrte Funktion lässt sich also auch nicht per Formular-POST auslösen.
  • Jede Änderung landet als backoffice.features_updated im Audit-Log des betroffenen Mandanten wie jeder andere Back-Office-Zugriff auch.

Das Back-Office ist außerdem jetzt für Platform-Admins im Menü verlinkt (Gruppe „Betreiber" in footer.php); vorher war es nur über die direkte URL erreichbar.

Eigenes Design je Mandant

Stand: 2026-08-17, hängt am Feature branding. Der Kunde hinterlegt in mandant-einstellungen.php eine Akzentfarbe und ein Logo:

  • Die Farbe (tenant_settings.brand_color, validiert als #rrggbb) wird in header.php als kleines Stylesheet eingebettet (app/branding.php), das die Akzentfarbe des Templates überschreibt. Eine eigene CSS-Datei je Mandant wäre ein zusätzlicher Request für wenige Zeilen.
  • Das Logo (tenant_settings.brand_logo) liegt wie das PDF-Wasserzeichen in var/tenant_logos und ist damit nicht direkt per URL abrufbar sonst wäre jedes Kundenlogo unter einer ratbaren Adresse öffentlich. Ausgeliefert wird es über tenant-logo-anzeigen.php, das ausschließlich das Logo des Mandanten des angemeldeten Nutzers ausgibt.
  • Wasserzeichen (Ausdruck) und Marken-Logo (Oberfläche) sind getrennte Dateien mit eigenem Namenspräfix (logo_ bzw. brand_).
  • Sperrt der Betreiber branding, verschwindet der Abschnitt aus den Einstellungen und die Oberfläche fällt aufs Standarddesign zurück. Die gespeicherte Farbe bleibt erhalten und gilt nach einer erneuten Freischaltung wieder.

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.