Spielerpositionen, Statistiken, Fahrgemeinschaften, Spielfeld-Visualisierung
- PlayerPosition Enum (7 Handball-Positionen) mit Label/ShortLabel - Spielerstatistik pro Spiel (Tore, Würfe, TW-Paraden, Bemerkung) - Position-Dropdown in Spieler-Editor und Event-Stats-Formular - Statistik-Seite: TW zuerst, Trennlinie, Feldspieler, Position-Badges - Spielfeld-SVG mit Ampel-Performance (grün/gelb/rot) - Anklickbare Spieler im Spielfeld öffnen Detail-Modal - Fahrgemeinschaften (Anbieten, Zuordnen, Zurückziehen) - Übersetzungen in allen 6 Sprachen (de, en, pl, ru, ar, tr) - .gitignore für Laravel hinzugefügt - Demo-Daten mit Positionen und Statistiken Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
126
CLAUDE.md
126
CLAUDE.md
@@ -3,6 +3,7 @@
|
||||
## Projekt-Typ
|
||||
Laravel 12 WebApp zur Verwaltung von Handball-Teams, Spielern, Eltern, Terminen und Dateien.
|
||||
PHP 8.2+, SQLite/MySQL, Blade + Alpine.js + Tailwind CSS, Quill.js WYSIWYG-Editor.
|
||||
Deployment: Shared Hosting (all-inkl.com, FTP only, kein SSH).
|
||||
|
||||
## Architektur
|
||||
|
||||
@@ -10,14 +11,14 @@ PHP 8.2+, SQLite/MySQL, Blade + Alpine.js + Tailwind CSS, Quill.js WYSIWYG-Edito
|
||||
- `app/Models/` — Eloquent Models (User, Player, Team, Event, Setting, etc.)
|
||||
- `app/Http/Controllers/Admin/` — Admin-Bereich (UserController, SettingsController, etc.)
|
||||
- `app/Http/Controllers/Auth/` — Login, Register, ForgotPassword, ResetPassword
|
||||
- `app/Http/Middleware/` — InstallerMiddleware (Setup-Token), SetLocaleMiddleware, SecurityHeadersMiddleware, ActiveUserMiddleware, DsgvoConsentMiddleware
|
||||
- `app/Http/Middleware/` — InstallerMiddleware, SetLocaleMiddleware, SecurityHeadersMiddleware, ActiveUserMiddleware, DsgvoConsentMiddleware, StaffMiddleware, AdminOnlyMiddleware
|
||||
- `app/Services/` — HtmlSanitizerService (HTMLPurifier), GeocodingService, SupportApiService
|
||||
- `app/Enums/` — UserRole, EventType, EventStatus, ParticipantStatus, CateringStatus
|
||||
- `app/Notifications/` — ResetPasswordNotification (Custom, mit Setting-Template)
|
||||
- `resources/views/` — Blade-Templates (layouts: admin, guest, app)
|
||||
- `lang/{de,en,pl,ru,ar,tr}/` — 6 Sprachen: Deutsch, Englisch, Polnisch, Russisch, Arabisch, Turkisch
|
||||
- `resources/views/` — Blade-Templates (layouts: admin, guest, app, installer)
|
||||
- `lang/{de,en,pl,ru,ar,tr}/` — 6 Sprachen: Deutsch, Englisch, Polnisch, Russisch, Arabisch, Tuerkisch
|
||||
- `database/migrations/` — Nummeriert mit Prefix 0001-0035
|
||||
- `database/seeders/` — AdminSeeder (benotigt ADMIN_EMAIL + ADMIN_PASSWORD in .env)
|
||||
- `database/seeders/` — AdminSeeder, DemoSeeder, FaqSeeder
|
||||
|
||||
### Wichtige Patterns
|
||||
- **Settings**: Key-Value Store in `settings` Tabelle. `Setting::get($key)` mit 1-Stunden-Cache, `Setting::set($key, $value)` mit Cache-Invalidierung
|
||||
@@ -27,43 +28,92 @@ PHP 8.2+, SQLite/MySQL, Blade + Alpine.js + Tailwind CSS, Quill.js WYSIWYG-Edito
|
||||
- **Rollen**: Admin, Coach, ParentRep, User (Enum `UserRole`)
|
||||
- **Soft-Deletes**: User und Player (7 Tage Wiederherstellung)
|
||||
- **Activity-Log**: `ActivityLog::log()` / `ActivityLog::logWithChanges()` fuer Audit-Trail
|
||||
- **User-Model**: Nutzt `Notifiable` Trait (erforderlich fuer Passwort-Reset-Notifications)
|
||||
- **.env-Manipulation**: `updateEnvValues()` Helper in InstallerController und SettingsController
|
||||
|
||||
### Multi-Language
|
||||
- 6 Sprachen: de, en, pl, ru, ar, tr
|
||||
- Translation-Dateien: `lang/{locale}/admin.php`, `auth_ui.php`, `passwords.php`, `ui.php`, `events.php`, `profile.php`, `validation.php`
|
||||
- Locale wird per SetLocaleMiddleware aus `$user->locale` oder Session gesetzt
|
||||
- RTL-Support fuer Arabisch (ar)
|
||||
- **Alle Uebersetzungsschluessel muessen in ALLEN 6 Sprachen hinzugefuegt werden**
|
||||
|
||||
### Installer (5-Step Wizard)
|
||||
1. Systemcheck (PHP-Version, Extensions, Berechtigungen)
|
||||
2. Datenbank (SQLite/MySQL Konfiguration)
|
||||
3. Einstellungen (App-Name, Admin-Account)
|
||||
4. E-Mail (SMTP-Konfiguration mit Verbindungstest, oder Log-Modus zum Ueberspringen)
|
||||
5. Abschluss (Finalize, Demo-Daten, Lizenz)
|
||||
|
||||
- `InstallerMiddleware` prueft `storage/installed` Datei
|
||||
- Setup-Token-Schutz: Token in `storage/setup-token`, SHA256 im Laravel-Log
|
||||
- SMTP-Test nutzt `Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport`
|
||||
- PW-Reset-Standardtexte fuer 6 Sprachen in `getDefaultPasswordResetTexts()`
|
||||
- Session-basierte Datenpersistenz zwischen Schritten
|
||||
|
||||
### Password Reset Flow
|
||||
1. User klickt "Passwort vergessen?" auf Login-Seite
|
||||
2. ForgotPasswordController sendet Reset-Link via Laravel Password Broker
|
||||
3. ResetPasswordNotification laedt optionalen Custom-Template-Text aus `Setting::get('password_reset_email_{locale}')`
|
||||
3. ResetPasswordNotification laedt Custom-Template aus `Setting::get('password_reset_email_{locale}')`
|
||||
4. Platzhalter: `{name}`, `{link}`, `{app_name}`
|
||||
5. Fallback auf Standard-Laravel-Template mit Translation-Keys (`passwords.reset_*`)
|
||||
|
||||
### Installer
|
||||
- InstallerMiddleware prueft `storage/installed` Datei
|
||||
- Setup-Token-Schutz: Token in `storage/setup-token`, wird beim ersten Zugriff generiert und in Laravel-Log geschrieben
|
||||
- Nach Installation wird `storage/installed` erstellt
|
||||
### Admin Settings Tabs
|
||||
- **Allgemein**: App-Name, Slogan, Favicon, Saison
|
||||
- **E-Mail**: SMTP-Konfiguration mit Verbindungstest (schreibt in .env, `Artisan::call('config:clear')`)
|
||||
- **Rechtliches**: Impressum + Datenschutz + PW-Reset-E-Mail pro Sprache (Quill-Editor mit Flaggen-Selector)
|
||||
- **Event-Standards**: Min-Werte fuer Spieler, Catering, Zeitnehmer pro Event-Typ
|
||||
- **Dateikategorien**: CRUD fuer File-Categories
|
||||
- **Sichtbarkeit**: Feature-Toggles
|
||||
- **Lizenz**: License-Key-Validierung via SupportApiService
|
||||
- **Wartung**: Demo-Daten loeschen, Factory-Reset
|
||||
|
||||
### Security
|
||||
### Lazy Quill-Initialisierung (Rechtliches-Tab)
|
||||
- Quill-Editoren werden erst bei Klick auf die Sprach-Flagge erstellt (`initLocaleEditors(locale)`)
|
||||
- Verhindert Datenverlust: Nicht-initialisierte Locales behalten ihre Server-Werte in Hidden-Inputs
|
||||
- `$watch('legalLocale')` + `$nextTick()` fuer verzoegerte Erstellung
|
||||
- `syncEditors()` synchronisiert nur tatsaechlich initialisierte Editoren
|
||||
|
||||
### Route-Struktur (Middleware)
|
||||
- Installer: `throttle:10,1`, ohne SetLocaleMiddleware/ActiveUserMiddleware
|
||||
- Oeffentlich: Login, Register, Legal-Pages, Locale-Switch
|
||||
- User-Bereich: `auth` Middleware
|
||||
- Admin-Bereich: `auth` + `admin` + `prefix('admin')`
|
||||
- Staff-Routes (Admin + Coach): `staff` Middleware
|
||||
- Admin-Only-Routes: `admin-only` Middleware (Settings, Locations, Support)
|
||||
|
||||
## Security
|
||||
|
||||
### Middleware-Stack
|
||||
- CSP und Permissions-Policy via SecurityHeadersMiddleware (inkl. COOP-Header)
|
||||
- `StaffMiddleware` — prueft Admin oder Coach Rolle
|
||||
- `AdminOnlyMiddleware` — prueft explizit Admin-Rolle (Route-Level-Schutz)
|
||||
- Rate-Limiting auf Auth-Routes, User-Actions, Geocoding, Installer
|
||||
|
||||
### Schutzmassnahmen
|
||||
- Honeypot-Feld auf Login/Register-Formularen
|
||||
- Rate-Limiting auf Auth-Routes (`throttle:login`)
|
||||
- DSGVO-Consent-System mit Datei-Upload und Admin-Bestaetigung
|
||||
- Factory Reset benoetigt Passwort + Bestaetigung "RESET-BESTAETIGT"
|
||||
- **File-Autorisierung**: `authorizeFileAccess()` in FileController prueft Team-Zugehoerigkeit und aktive Kategorien
|
||||
- **File-Autorisierung**: `authorizeFileAccess()` in FileController prueft Team-Zugehoerigkeit
|
||||
- **Settings-Whitelist**: SettingsController akzeptiert nur bekannte Keys + validierte Locale-Suffixe
|
||||
- **SSRF-Schutz**: GeocodingService mit Host-Whitelist (nominatim.openstreetmap.org, photon.komoot.io), HTTPS-only, Timeout 5s
|
||||
- **Installer-Sicherheit**: Admin-Passwort wird sofort gehasht (nicht Klartext in Session), Setup-Token nur als SHA256 geloggt
|
||||
- **SSRF-Schutz**: GeocodingService mit Host-Whitelist, HTTPS-only, DNS-Rebinding-Check, Timeout 5s
|
||||
- **Installer-Sicherheit**: Admin-Passwort sofort gehasht, Setup-Token nur als SHA256 geloggt
|
||||
- **Path-Traversal-Schutz**: DSGVO-Datei-Downloads pruefen `str_starts_with('dsgvo/')`
|
||||
- **HTML-Sanitisierung**: Alle `{!! !!}`-Ausgaben (Slogan, Settings-Editor, PWA-Banner) durch `HtmlSanitizerService::sanitize()` geschuetzt
|
||||
- **HTML-Sanitisierung**: Alle User-Content-Ausgaben durch HtmlSanitizerService geschuetzt
|
||||
- **Invitation-Tokens**: SHA-256 gehasht in DB gespeichert
|
||||
- **E-Mail-Normalisierung**: Lowercase-Vergleich bei Login und Password-Reset
|
||||
- **Team-Scoping**: Coach/ParentRep sehen nur ihre zugewiesenen Teams
|
||||
|
||||
### Security-Audit-Historie (Maerz 2026)
|
||||
6 Audits durchgefuehrt, Score: 6.5 → 9.5/10
|
||||
- 95 Findings insgesamt, 88 behoben, 3 akzeptierte Risiken, 4 verbleibende Niedrig/Info
|
||||
- Akzeptierte Risiken: MIME-Spoofing (F05), Enum-Reuse (F20), CSP unsafe-inline (W04, CDN-Abhaengigkeit)
|
||||
|
||||
## Conventions
|
||||
- Controller-Methoden: resourceful (index, create, store, show, edit, update, destroy)
|
||||
- Blade-Components: `<x-layouts.admin>`, `<x-layouts.guest>`, `<x-layouts.app>`
|
||||
- Blade-Components: `<x-layouts.admin>`, `<x-layouts.guest>`, `<x-layouts.app>`, `<x-layouts.installer>`
|
||||
- Alpine.js fuer interaktive UI-Elemente
|
||||
- Quill.js v1.3.7 fuer WYSIWYG-Editoren (via CDN)
|
||||
- Quill.js v1.3.7 fuer WYSIWYG-Editoren (via CDN mit SRI-Hashes)
|
||||
- Keine Tests vorhanden — bei Aenderungen manuell testen
|
||||
- Commit-Sprache: Deutsch oder Englisch
|
||||
- Alle Uebersetzungsschluessel muessen in ALLEN 6 Sprachen hinzugefuegt werden
|
||||
@@ -84,38 +134,14 @@ Immer in allen 6 Dateien: `lang/de/`, `lang/en/`, `lang/pl/`, `lang/ru/`, `lang/
|
||||
2. Migration fuer alle 6 Locales
|
||||
3. In SettingsController `$localeSettings` Array aufnehmen
|
||||
4. In View mit Sprach-Flaggen-Selector anzeigen
|
||||
5. In `update()` mit `str_starts_with()` und `updateOrCreate()` behandeln
|
||||
5. In `update()` mit Whitelist und `updateOrCreate()` behandeln
|
||||
6. **Wichtig**: Neuen Key zur Whitelist in `SettingsController::update()` hinzufuegen ($allowedLocaleKeys)
|
||||
|
||||
## Security Audit & Haertung (Maerz 2026)
|
||||
|
||||
### Audit-Report
|
||||
- **Datei**: `security-audit-2026-03.html` — Vollstaendiger HTML-Report mit allen Findings und Fix-Dokumentation
|
||||
- **Score**: 6.5 → 8.5 / 10 nach Haertung
|
||||
- **Ergebnis**: 20 Findings identifiziert, 19 behoben, 1 als akzeptiertes Risiko
|
||||
|
||||
### Behobene Schwachstellen (nach Datei)
|
||||
|
||||
| Datei | Findings | Aenderungen |
|
||||
|-------|----------|-------------|
|
||||
| `FileController.php` | F01 (Kritisch) | `authorizeFileAccess()` — IDOR-Schutz fuer Downloads/Previews |
|
||||
| `admin/settings/edit.blade.php` | F02 (Kritisch) | Alle `{!! !!}` durch `HtmlSanitizerService::sanitize()` |
|
||||
| `layouts/app.blade.php`, `guest.blade.php` | F03 (Kritisch) | Slogan-Ausgabe sanitisiert |
|
||||
| `Admin/SettingsController.php` | F04 (Hoch) | Settings-Key-Whitelist in `update()` |
|
||||
| `Services/GeocodingService.php` | F06 (Hoch) | ALLOWED_HOSTS, HTTPS-only, SHA256-Cache, Timeout |
|
||||
| `EventController.php` | F07 (Hoch) | `accessibleTeamIds()`, EventType-Validierung, int-Cast |
|
||||
| `InstallerController.php` | F08 (Mittel) | Passwort sofort gehasht (`installer.admin_password_hash`) |
|
||||
| `InstallerMiddleware.php` | F09 (Mittel) | Token als SHA256 geloggt, chmod 0600 |
|
||||
| `SecurityHeadersMiddleware.php` | F10, F19 | COOP-Header hinzugefuegt |
|
||||
| `.env.example` | F11 (Mittel) | SESSION_SECURE_COOKIE + SESSION_SAME_SITE Defaults |
|
||||
| `Admin/TeamController.php` | F12 (Mittel) | Ziel-Team aktiv-Pruefung in `updatePlayerTeam()` |
|
||||
| `Admin/ActivityLogController.php` | F13, F16 | whereRaw→Eloquent, `canViewActivityLog()` statt `id !== 1` |
|
||||
| `CommentController.php` | F14 (Mittel) | `e()` entfernt (Doppel-Escaping behoben) |
|
||||
| `pwa-install-banner.blade.php` | F15 (Mittel) | Translation-Ausgabe sanitisiert |
|
||||
| `ProfileController.php` | F17 (Niedrig) | DSGVO Path-Prefix-Check |
|
||||
| `Auth/ResetPasswordController.php` | F18 (Niedrig) | `Password::min(8)->letters()->numbers()` |
|
||||
|
||||
### Akzeptierte Restrisiken
|
||||
- **F05** (MIME-Spoofing): UUID-Dateinamen + privater Storage + Autorisierung mitigieren das Risiko
|
||||
- **F20** (EventTimekeeper Enum): CateringStatus und TimekeeperStatus haben identische Werte
|
||||
- **CSP unsafe-inline/eval**: CDN-Abhaengigkeit (Tailwind/Alpine.js/Quill.js) erfordert dies — langfristig lokale Assets empfohlen
|
||||
### Neue Admin-Route hinzufuegen
|
||||
1. Route in `routes/web.php` im passenden Middleware-Block:
|
||||
- Alle Admin-Panel-Nutzer: direkt unter `admin` Prefix
|
||||
- Staff (Admin + Coach): unter `staff` Middleware
|
||||
- Nur Admin: unter `admin-only` Middleware
|
||||
2. Controller-Methode mit Autorisierungs-Pruefung
|
||||
3. View erstellen
|
||||
4. Translation-Keys fuer alle 6 Sprachen
|
||||
|
||||
Reference in New Issue
Block a user