Kontofunktionen vor dem Go-Live und Landingpage-Kuerzung

Vier Luecken geschlossen, die im Alltag sofort aufgefallen waeren:

- Passwort aendern war im eingeloggten Zustand gar nicht moeglich; es gab
  nur den Reset per Mail-Link. saas_change_password() prueft das aktuelle
  Passwort, verlangt ein tatsaechlich anderes und erneuert danach die
  Session-ID. Formular in konto.php.
- mandant-auswahl.php war nur direkt nach dem Login erreichbar. Wer bei
  mehreren Mandanten Mitglied ist, musste sich zum Wechseln abmelden. Die
  Seite bedient jetzt beide Wege, die Mandantenpruefung bleibt unveraendert
  ueber saas_identity_for_user_tenant(). Menuepunkt ab zwei Mitgliedschaften.
- email_verified_at wurde nirgends geprueft, nur angezeigt - bei offener
  Selbstregistrierung konnte sich jemand mit fremder Adresse anmelden und
  alles nutzen. Erzwungen wird jetzt gezielt dort, wo eine Aktion nach
  aussen wirkt: Einladung, Info-Mail, Jahresabschluss. Der Login selbst
  bleibt bewusst frei, sonst waeren alle migrierten Bestandsnutzer mit
  NULL-Verifikation ausgesperrt. Zusaetzlich setzt der Passwort-Reset die
  Verifikation mit, weil der Mail-Link den Postfachzugriff nachweist -
  sonst blieben eingeladene Mitglieder dauerhaft unbestaetigt.
- Login landete auf konto.php statt auf dem Dashboard.

Landingpage: die gruene Vertrauenszeile auf "DSGVO-konform" gekuerzt, die
Eintraege zu Paragraf 19 UStG und "Bestehende Ablaeufe bleiben" entfernt.

Geprueft: neues scripts/check-konto-und-mandantenwechsel.php mit 18
Assertions gruen, Passwortformular zusaetzlich manuell inkl. CSRF (419).
Bestehende Suiten unveraendert gruen: HTTP-Smoke 34 Seiten, Rollenmatrix
55, Mandanten-Isolation 12, M3-Auth 9, M3-Settings 15.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 17:03:50 +02:00
co-authored by Claude Opus 5
parent 52b1726698
commit 39f662d541
13 changed files with 649 additions and 17 deletions
+3 -1
View File
@@ -18,7 +18,9 @@ Vorschlag: Gruppierung in Abschnitte mit Zwischenüberschriften, z. B.:
verwalten.
- **Konto** Kundenkonto, Mandant-Einstellungen, FAQ, Logout.
Noch nicht entschieden/umgesetzt.
**Erledigt.** `footer.php` gruppiert die Sidebar inzwischen in
„Kaffeeliste", „Verwaltung" (rollenabhängig eingeblendet) und „Konto";
dort ist ab zwei Mitgliedschaften auch „Mandant wechseln" ergänzt.
## Kaffeeliste-Ausdruck (PDF-Export) als zentrale Funktion
+85 -1
View File
@@ -207,4 +207,88 @@ Tabellen leer sind und die globale `users`-Zeile erhalten bleibt.
## Noch offen in M8
- Content-Security-Policy (braucht Template-Bereinigung der Inline-Styles).
- Backup-/Restore-Prozess und Monitoring/Fehlerlogging dokumentieren.
- ~~Backup-/Restore-Prozess und Monitoring/Fehlerlogging dokumentieren.~~
Erledigt in `docs/betrieb-backup-monitoring.md`.
## Nachtrag: Kontofunktionen vor dem Go-Live (2026-08-06)
Vier Lücken, die im Alltag sofort aufgefallen wären, geschlossen.
Umgesetzte Dateien:
```text
app/saas-auth.php (saas_change_password, saas_email_verified,
saas_require_verified_email)
konto.php (Formular "Passwort ändern")
mandant-auswahl.php(Wechsel aus bestehender Sitzung)
footer.php ("Mandant wechseln" ab zwei Mandanten)
header.php (Hinweisbanner bei unbestätigter Adresse)
login.php (Ziel nach Login)
mitarbeiterverwalten.php, mailversenden.php, jahresauswertung.php
(Verifikationspflicht)
scripts/check-konto-und-mandantenwechsel.php
```
### Passwortwechsel im eingeloggten Zustand
Bisher gab es ausschließlich den Reset über einen Mail-Link; wer sein
Passwort ändern wollte, musste sich abmelden und „Passwort vergessen"
benutzen. `saas_change_password()` verlangt das aktuelle Passwort als
Identitätsnachweis, erzwingt dieselbe Mindestlänge wie der Reset und
verlangt ein tatsächlich anderes Passwort. Nach dem Wechsel wird die
Session-ID erneuert.
**Bewusst nicht umgesetzt:** ein Abmelden aller anderen Sitzungen. Die
Sessions liegen als Dateien ohne Zuordnung zur User-ID; das bräuchte
einen eigenen Session-Store und lohnt beim aktuellen Umfang nicht.
### Mandantenwechsel ohne Logout
`mandant-auswahl.php` war nur direkt nach dem Login erreichbar (die Seite
stieg ohne `saas_pending_tenant_user_id()` sofort aus). Wer bei mehreren
Mandanten Mitglied ist, musste sich zum Wechseln abmelden. Die Seite
bedient jetzt beide Wege; die Mandantenprüfung läuft unverändert über
`saas_identity_for_user_tenant()`, ein fremder Mandant wird also auch aus
einer bestehenden Sitzung heraus abgewiesen. Der Menüpunkt erscheint nur
ab zwei Mitgliedschaften.
### E-Mail-Verifikation wird erzwungen
`email_verified_at` wurde bisher nur angezeigt, nie geprüft — bei
öffentlicher Selbstregistrierung konnte sich also jemand mit einer
fremden Adresse anmelden und das Produkt voll nutzen.
Erzwungen wird jetzt gezielt dort, wo eine Aktion **nach außen** wirkt:
Mitglieder einladen, Info-Mail an alle, Jahresabschluss mit
Benachrichtigung. Bei den beiden Mailflows bleibt der Dry-Run erlaubt,
die Einladung wird ganz abgewiesen.
Bewusst **nicht** erzwungen wird der Login selbst: die eigene Kaffeeliste
darf man auch unbestätigt führen. Der Grund ist der migrierte Bestand —
eine harte Loginsperre hätte alle Nutzer ausgesperrt, deren
`email_verified_at` aus der Legacy-Migration `NULL` ist.
Ergänzend setzt `saas_reset_password_with_token()` jetzt
`email_verified_at` mit, denn wer den zugestellten Link öffnen konnte,
hat den Zugriff auf das Postfach nachgewiesen. Das betrifft vor allem
eingeladene Mitglieder: deren Zugangsvergabe läuft über genau diesen
Link, ohne dass je eine separate Verifikationsmail verschickt wird — ohne
diese Ergänzung wären eingeladene Admins dauerhaft als unbestätigt
geführt worden.
### Login-Ziel
Nach dem Login ging es auf `konto.php`, also auf eine Stammdatentabelle.
Ziel ist jetzt `index.php`.
### Prüfstatus
`scripts/check-konto-und-mandantenwechsel.php` deckt alle vier Punkte ab
(18 Assertions, grün): Login-Ziel, abgewiesene und erlaubte Einladung,
Mandantenwechsel inklusive Negativfall fremder Mandant, alle vier
Fehlerfälle des Passwortwechsels plus echter Login mit altem und neuem
Passwort, und die Verifikation über den Reset-Link. Zusätzlich manuell
über das Formular in `konto.php` geprüft: Erfolgs- und Fehlermeldung,
CSRF-Schutz (419 ohne Token). Alle bestehenden Suiten bleiben grün
(HTTP-Smoke 34 Seiten, Rollenmatrix 55, Mandanten-Isolation 12,
M3-Auth 9, M3-Settings 15).