Die Zugriffspruefung lief bisher in 19 Dateien auf einen Fallback gegen die
Legacy-Tabelle kl_Mitarbeiter (checkKaffeelisteAdmin/-Access). Der Zugang
haengt jetzt ausschliesslich an der SaaS-Anmeldung.
- Fallback-Bloecke in allen 19 Dateien entfernt, ebenso getUserName/getUserId
- functions.php besteht nur noch aus der SaaS-Anmeldung; die per String
zusammengebaute Abfrage "WHERE Email like '<eingabe>'" ist damit weg
- config.php baut keine sqlsrv-Verbindung mehr auf, der Kompatibilitaetslayer
lib/sqlsrv_mysql_compat.php ist verwaist und entfaellt
- Auto-Login per DEV_AUTH_EMAIL gibt es nicht mehr: er meldete jeden Besucher
an und machte damit den Login-Schutz unpruefbar. Angemeldet wird auch lokal
ueber login.php; die Variable dient nur noch scripts/init-mysql-dev.php
http-smoke war an die Golden-Master-Daten des Default-Mandanten gebunden und
hatte deshalb sechs dauerhaft rote Pruefungen. Der Test legt sich jetzt einen
eigenen Mandanten mit bekannten Salden an, meldet sich per HTTP an und raeumt
danach auf. Geschuetzte Seiten werden nicht mehr ueber das Wort "Login" im
Text geprueft, sondern ueber die tatsaechliche 302-Umleitung.
http-smoke 27 PASS / 6 FAIL -> 33 PASS / 0 FAIL
role-matrix 55 PASS / 0 FAIL -> unveraendert
tenant-isolation 9 PASS / 1 FAIL -> 11 PASS / 1 FAIL (Altfehler)
Ausserdem "keine Zugang" -> "keinen Zugang" in zwei Fehlermeldungen; der
Rollentest erkannte die Ablehnung an genau diesem Tippfehler und wurde
mitgezogen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Einzahlungen koennen jetzt eine Bemerkung tragen und negativ sein, damit
Abzuege (Auszahlung bei Austritt, Erstattung) nachvollziehbar gebucht werden
koennen.
- ledger_entries.note wurde bisher nur beim Import befuellt und wird nun auch
bei Einzahlungen geschrieben und angezeigt
- Negative Betraege verlangen zwingend eine Bemerkung, sonst ist spaeter nicht
mehr nachvollziehbar, warum jemandem Geld abgezogen wurde
- Plausibilitaetsgrenze von 1000 EUR gegen Groessenordnungs-Tippfehler; bei
einem Fehler in einer Zeile wird gar nichts gebucht und die Eingaben bleiben
stehen
- Legacy-Tabelle kl_Einzahlungen bekommt eine Bemerkung-Spalte, sonst haette
ledger_mirror_legacy_payment die Notiz beim Re-Sync wieder mit NULL
ueberschrieben
- PayPal-Buchungen tragen jetzt Zahler, Datum und Mitteilung als Bemerkung
Ausserdem behoben: letzteneintraege.php hat Einzahlungen und Striche direkt
aus den Legacy-Tabellen gelesen, ohne nach Mandant zu filtern. Jeder
Mandanten-Admin sah damit die Buchungen aus den Legacy-Tabellen, und das
Stornieren lief ueber die nicht mandantengetrennten Legacy-IDs. Die Seite
arbeitet jetzt auf dem mandantengebundenen Journal, und ledger_void_entry
prueft Mandant und Buchungsart, bevor es storniert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bugfixes aus dem Code-Review:
- XSS: unescaptes $_SERVER['PHP_SELF'] in csvupload.php und
letzteneintraege.php durch feste Seitennamen ersetzt.
- Stripe-Webhook: Event-Deduplizierung (neue Tabelle stripe_webhook_events)
gegen doppelte Verarbeitung/Dolibarr-Rechnungen bei Retry-Zustellung.
- Stripe-Webhook: Tarifwechsel aus dem Kundenportal wird lokal nachgezogen
(plan_code aus dem Preis-lookup_key bei subscription.updated).
- Post-Redirect-Get fuer jahresauswertung, mailversenden und die
Selbst-Stricheintragung - verhindert Doppelbuchung/Doppelversand per
Browser-Refresh.
- Jahresbonus: Mails erst nach erfolgreichem Commit; Restcent-Ausgleich
beim letzten Empfaenger, damit die Summe exakt stimmt.
- Verschachtelte HTML-Dokumente in csvupload/einzahlung/stricheintragen
entfernt (Layout kommt aus header.php).
- Rate-Limit fuer den Versand von E-Mail-Verifizierungslinks.
Neue Funktionen:
- Mitglieder koennen ihren zuletzt selbst eingetragenen Strich wieder
stornieren (nur eigene Web-Eintraege, Kassenwart-Eintraege bleiben).
- Monatsuebersicht des eigenen Verbrauchs im Mitglieder-Dashboard.
- Automatische Zahlungserinnerung: opt-in pro Mandant ab der
Warnschwelle, mit Intervall; Cron-Skript scripts/send-payment-reminders.php.
- CSV-Import fuer Mitgliederlisten inkl. herunterladbarer Vorlage
(mitglieder-vorlage.php), Semikolon-/Komma- und BOM-Erkennung.
- Logo als PDF-Wasserzeichen pro Mandant (Upload in den Mandant-
Einstellungen, geschuetzt unter var/tenant_logos/, ersetzt den
Text-Wasserzeichen im Ausdruck).
Robusterer Teilnehmer-Lookup im Dashboard ueber user_id (Fallback E-Mail).
Erste drei Bausteine der Haertung:
- Security-Headers (X-Content-Type-Options, X-Frame-Options, Referrer-
Policy, Permissions-Policy, HSTS bei HTTPS) laufen automatisch ueber
app_send_security_headers() am Ende von app/bootstrap.php fuer jede
dynamische Seite; landing.php war als einzige Seite ganz ohne PHP und
bekam einen minimalen Bootstrap-Aufruf. Bewusst kein CSP, da die
bestehenden Templates durchgaengig auf Inline-style-Attribute setzen.
- DB-gestuetzte Rate-Limits (neue Tabelle rate_limit_attempts) fuer
Login (10/15min je E-Mail, 20/15min je IP), Registrierung (5/h je IP)
und Passwort-Reset-Anfrage (5/h je E-Mail, 10/h je IP); bei
ausgereiztem Reset-Limit erscheint dieselbe generische Meldung wie im
Erfolgsfall, um kein Konto-Enumeration-Signal zu geben.
- Zentrales Audit-Log (neue Tabelle audit_log) fuer Mitgliederverwaltung,
Zugangsvergabe/-entzug, Storno, Mandant-Einstellungen, Hinweise,
CSV-Import, Jahresbonus-Verteilung und Live-Mailversand; sichtbar fuer
Owner/Admin auf mandant-einstellungen.php.
Live getestet: Rate-Limit greift nach 10 Fehlversuchen, Audit-Log-Eintrag
mit korrekten Metadaten und Nutzernamen ueber einen isolierten Test-
Mandanten geprueft. Alle Regressionstests weiterhin gruen (26/26 Smoke,
104 Golden-Master-Assertions).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- stricheintragen.php und einzahlung.php erhielten bisher gar keine
Zugriffskontrolle (nur CSRF), jetzt Rollen-/Legacy-Fallback wie in
kaffeeliste.php; Sammeleintraege spiegeln transaktional ins Ledger
(ledger_mirror_legacy_payment neu ergaenzt).
- letzteneintraege.php war rein ueber Legacy-Admin gesperrt und haette
neue SaaS-Mandanten ausgeschlossen; Loeschen markiert den gespiegelten
Ledger-Eintrag jetzt per voided_at statt ihn zu entfernen
(ledger_void_entry_by_legacy_id).
- check-m4-ledger-migration.php an das Storno-Modell angepasst: verwaiste
Ledger-Zeilen sind nur noch ein Fehler, wenn sie nicht voided sind.
- Nebenbei: PHP-Warning bei Sammelerfassung behoben, toten Code entfernt,
veraltete README (verwies auf nicht existierende saas-app/-Struktur)
korrigiert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Löschaktionen in letzteneintraege.php mit CSRF-Token absichern
- POST ohne Token für Einzahlungen und Strich-Einträge blockieren
- M2- und Sicherheitsdokumentation zum CSRF-Rollout aktualisieren