# M2 Technisches Fundament Stand: 2026-07-13 M2 fuehrt die technischen Grundbausteine fuer den SaaS-Umbau ein, ohne das Legacy-Verhalten oder das bestehende Design zu veraendern. Status: abgeschlossen fuer den M2-Scope. Bewusst ausgelagerte Themen sind unten als Grenzen und M3-/M6-Uebergabe dokumentiert. ## Ziel - Versionierte Datenbankmigrationen statt loser Schema-Ausfuehrung. - Zentrales Bootstrap fuer Umgebung, Session und CSRF-Helper. - Bestehende Legacy-Seiten bleiben kompatibel. - Layout-Trennung wird vorbereitet, aber noch nicht in bestehende Seiten hineingezogen. ## Umgesetzte Bausteine ### Bootstrap - `app/bootstrap.php` definiert zentrale Helfer: - `app_env()` - `app_is_dev()` - `app_start_session()` - `app_csrf_token()` - `app_csrf_field()` - `app_verify_csrf()` - `app_require_csrf()` - `config.php` laedt den Bootstrap und startet die Session mit sicheren Cookie-Optionen. - Sessions werden standardmaessig unter `var/sessions` abgelegt, weil die lokale PHP-Umgebung keinen beschreibbaren Systempfad garantiert. `var/` ist von Git ignoriert. Der Pfad kann ueber `APP_SESSION_PATH` ueberschrieben werden. - CSRF ist bewusst noch nicht global erzwungen. Die vorhandenen POST-Seiten werden spaeter einzeln umgestellt, damit keine Formulare oder Spezialflows brechen. ### CSRF-Rollout Erste Legacy-POST-Seiten sind opt-in abgesichert: - `hinweise.php`: Hinweis anlegen und Hinweis loeschen. Der bisherige GET-Loeschlink wurde durch ein POST-Formular mit CSRF-Token ersetzt. - `mitarbeiterverwalten.php`: Mitglied anlegen, Bearbeitungsformular oeffnen, Mitglied speichern, aktivieren und deaktivieren. - `namenanpassen.php`: Anzeigenamen aktualisieren. - `index.php`: eigene Web-Striche eintragen. - `stricheintragen.php`: Sammelerfassung von Strichen. - `einzahlung.php`: Sammelerfassung von Einzahlungen. - `letzteneintraege.php`: letzte Einzahlungen und Strich-Eintraege loeschen. - `csvupload.php`: CSV-Zahlungsimport. Noch offen: - Spezialprozesse: `mailversenden.php`, `jahresauswertung.php`. ### CSV-Upload-Haertung - `csvupload.php` nutzt jetzt CSRF. - Uploads werden unter `var/uploads` gespeichert und nach der Verarbeitung geloescht. Damit liegen importierte Dateien nicht mehr im Webroot. - Dateiendung, Dateigroesse und MIME-Typ werden vor der Verarbeitung geprueft. - Hochgeladene Dateien bekommen serverseitig erzeugte Zufallsnamen. - CSV-Auswertungswerte werden HTML-escaped ausgegeben. - Die PayPal-Namenssuche nutzt `Name` und `paypalname` mit expliziter Parameterbindung. Offen fuer M6: - Importvorschau vor dem Schreiben. - Import-Batch/Audit-Log. - Saubere Fehlerberichte je CSV-Zeile. ### Migrationen - `database/migrations/0001_legacy_mysql_baseline.sql` bildet die bisherige MySQL-Dev-Baseline als erste versionierte Migration ab. - `scripts/dev-db.php` verwaltet `schema_migrations` und fuehrt neue Migrationen idempotent aus. - `scripts/migrate.php` ist der direkte Runner fuer Migrationen. - `scripts/init-mysql-dev.php` nutzt ab jetzt ebenfalls die Migrationslogik. ### Layout - `header.php` und `footer.php` bleiben vorerst Legacy-Wrapper. - Die spaetere Trennung in Public-Layout und App-Layout wird erst umgesetzt, wenn die neue Route-/View-Struktur steht. - Die bestehende HTML5-UP-Struktur, Sidebar und Assets bleiben unveraendert. ## Ausfuehrung Mit normaler PHP-CLI: ```bash php scripts/migrate.php php scripts/init-mysql-dev.php php scripts/check-golden-master.php php scripts/http-smoke.php ``` In der aktuellen lokalen Umgebung: ```bash LD_LIBRARY_PATH="$PWD/.local/php/usr/lib/x86_64-linux-gnu:$PWD/.local/php/usr/lib/x86_64-linux-gnu/sasl2" \ "$PWD/.local/php/usr/bin/php8.3" \ -c "$PWD/.local/php-dev.ini" \ scripts/migrate.php ``` Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`, `DB_USER` und `DB_PASS`. `scripts/init-mysql-dev.php` braucht zusaetzlich `DEV_AUTH_EMAIL`. ## Aktueller Pruefstatus - Migration `0001_legacy_mysql_baseline.sql` erfolgreich angewendet. - Zweiter Migrationslauf meldet: Datenbank ist aktuell. - PHP-Syntax fuer Bootstrap, Migrationen, Init-Skript und Config ist sauber. - Session-Start laeuft in der lokalen Dev-Umgebung ohne PHP-Warnings ueber `var/sessions`. - CSRF negative Tests: `hinweise.php`, `mitarbeiterverwalten.php` und `namenanpassen.php` liefern bei POST ohne Token HTTP 419. - CSRF positive Tests: gueltige Token funktionieren fuer Hinweis-Anlage, Mitglieder-Bearbeitungsformular und Namensanpassung. Der temporaere Testhinweis wurde wieder entfernt. - CSRF negative Tests fuer Buchungsflows: `index.php`, `stricheintragen.php` und `einzahlung.php` liefern bei POST ohne Token HTTP 419. - CSRF positive Tests fuer Buchungsflows: gueltige Token funktionieren fuer eigene Web-Striche, Sammelstriche und Sammeleinzahlungen. Die temporaeren Testbuchungen wurden wieder entfernt. - CSRF negative Tests fuer Korrektur-/Loeschflows: `letzteneintraege.php` liefert bei POST ohne Token HTTP 419. - CSRF positive Tests fuer Korrektur-/Loeschflows: gueltige Token funktionieren fuer das Loeschen temporaerer Einzahlungs- und Strich-Testeintraege. - CSRF negative Test fuer CSV-Upload: POST ohne Token liefert HTTP 419. - CSV-Upload positive Tests: gueltiges Token verarbeitet eine CSV-Datei, erkennt eine PayPal-Alias-Dublette und hinterlaesst keine Datei in `var/uploads`. - CSV-Upload negative Tests: Nicht-CSV-Dateien werden abgewiesen. - Golden Master weiterhin gruen mit 104 Assertions. - HTTP-Smoke weiterhin gruen mit 23 sicheren Seiten inklusive Landingpage, Login, Registrierung, Passwort-Reset, E-Mail-Verifikation und geschuetzter Mandant-Einstellungen, geschuetzter Mandantenauswahl sowie Ledger-Preview. ## Bewusste Grenzen - Keine Tenant-/User-/Rollen-Tabellen in M2. Diese gehoeren zu M3. - Keine globale CSRF-Erzwingung fuer Spezialprozesse. `mailversenden.php` und `jahresauswertung.php` werden in M6 als Jobs mit Dry-Run, Audit und Versandlog neu betrachtet. - Kein Layout-Umbau in M2. Die visuelle Struktur bleibt stabil; Public-/App- Layouts werden mit der M3-/M7-Struktur vorbereitet. - PDF-/Mail-/Jahresprozesse bleiben als M6-Themen offen. ## Abschlusskriterien - Migrationsrunner ist vorhanden und idempotent. - Bootstrap, Session und CSRF-Helper sind zentral verfuegbar. - Die relevanten Legacy-Schreibseiten sind CSRF-geschuetzt. - CSV-Uploads liegen nicht mehr im Webroot. - Bestehende Legacy-Seiten bleiben per HTTP-Smoke erreichbar. - Golden-Master-Fachwerte bleiben unveraendert. ## Uebergabe an M3 M3 startet mit additiven SaaS-Tabellen und einer Default-Tenant-Abbildung. Die Legacy-Seiten sollen dabei weiterhin ueber die bestehenden Tabellen laufen, bis der fachliche Kern in M4/M5 schrittweise auf das Zielmodell umgestellt wird. M3 wurde inzwischen gestartet. Umgesetzte Details stehen in `docs/m3-saas-basis-vorbereitung.md`. Der urspruengliche Startpunkt war: 1. `tenants`, `tenant_settings`, `users`, `tenant_memberships` und `participants` als neue Migration anlegen. 2. Einen Default-Tenant fuer die bestehende Kaffeeliste definieren. 3. Bestehende `kl_Mitarbeiter` in `participants` spiegeln, inklusive `legacy_mitarbeiter_id`. 4. Admins aus `kl_Mitarbeiter.admin` als `tenant_memberships.role = 'admin'` beziehungsweise fuer den ersten Hauptnutzer als `owner` abbilden. 5. Erst danach Login/Registrierung und Public-/App-Routen anschliessen.