Stand: SMTP-Test, Admin-Mail-Tab, Notifiable-Fix, Lazy-Quill
- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er) - Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link - Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest - Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen) - Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
121
CLAUDE.md
Normal file
121
CLAUDE.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# WebApp_Install — Handball Team Manager
|
||||
|
||||
## 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.
|
||||
|
||||
## Architektur
|
||||
|
||||
### Verzeichnisstruktur
|
||||
- `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/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
|
||||
- `database/migrations/` — Nummeriert mit Prefix 0001-0035
|
||||
- `database/seeders/` — AdminSeeder (benotigt ADMIN_EMAIL + ADMIN_PASSWORD in .env)
|
||||
|
||||
### Wichtige Patterns
|
||||
- **Settings**: Key-Value Store in `settings` Tabelle. `Setting::get($key)` mit 1-Stunden-Cache, `Setting::set($key, $value)` mit Cache-Invalidierung
|
||||
- **Locale-Settings**: Rechtliche Texte und E-Mail-Templates sind locale-suffixed: `impressum_html_de`, `datenschutz_html_en`, `password_reset_email_pl` etc.
|
||||
- **Passwort-Hashing**: User-Model nutzt Laravel Cast `'password' => 'hashed'` — KEIN manuelles Hash::make beim Setzen via `$user->password = $pw` noetig
|
||||
- **HTML-Output**: Alle `{!! !!}`-Ausgaben von User-Content muessen durch `HtmlSanitizerService::sanitize()` gehen
|
||||
- **Rollen**: Admin, Coach, ParentRep, User (Enum `UserRole`)
|
||||
- **Soft-Deletes**: User und Player (7 Tage Wiederherstellung)
|
||||
- **Activity-Log**: `ActivityLog::log()` / `ActivityLog::logWithChanges()` fuer Audit-Trail
|
||||
|
||||
### 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)
|
||||
|
||||
### 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}')`
|
||||
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
|
||||
|
||||
### Security
|
||||
- CSP und Permissions-Policy via SecurityHeadersMiddleware (inkl. COOP-Header)
|
||||
- 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
|
||||
- **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
|
||||
- **Path-Traversal-Schutz**: DSGVO-Datei-Downloads pruefen `str_starts_with('dsgvo/')`
|
||||
- **HTML-Sanitisierung**: Alle `{!! !!}`-Ausgaben (Slogan, Settings-Editor, PWA-Banner) durch `HtmlSanitizerService::sanitize()` geschuetzt
|
||||
|
||||
## Conventions
|
||||
- Controller-Methoden: resourceful (index, create, store, show, edit, update, destroy)
|
||||
- Blade-Components: `<x-layouts.admin>`, `<x-layouts.guest>`, `<x-layouts.app>`
|
||||
- Alpine.js fuer interaktive UI-Elemente
|
||||
- Quill.js v1.3.7 fuer WYSIWYG-Editoren (via CDN)
|
||||
- Keine Tests vorhanden — bei Aenderungen manuell testen
|
||||
- Commit-Sprache: Deutsch oder Englisch
|
||||
- Alle Uebersetzungsschluessel muessen in ALLEN 6 Sprachen hinzugefuegt werden
|
||||
|
||||
## Haeufige Aufgaben
|
||||
|
||||
### Neuen Translation-Key hinzufuegen
|
||||
Immer in allen 6 Dateien: `lang/de/`, `lang/en/`, `lang/pl/`, `lang/ru/`, `lang/ar/`, `lang/tr/`
|
||||
|
||||
### Neues Setting hinzufuegen
|
||||
1. Migration erstellen: `Setting::firstOrCreate(['key' => '...'], [...])`
|
||||
2. In SettingsController `edit()` laden
|
||||
3. In View rendern
|
||||
4. In `update()` speichern (mit Sanitization fuer HTML)
|
||||
|
||||
### Neues locale-spezifisches Setting
|
||||
1. Keys: `{setting_name}_{locale}` (z.B. `impressum_html_de`)
|
||||
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
|
||||
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
|
||||
Reference in New Issue
Block a user