morz-infoboard/TODO.md
Jesko Anschütz dd3ec070f7 Security-Review + Phase 6: CSRF, Rate-Limiting, Tenant-Isolation, Screenshot, Ansible
### Security-Fixes (K1–K6, W1–W4, W7, N1, N5–N6, V1, V5–V7)
- K1: CSRF-Schutz via Double-Submit-Cookie (httpapi/csrf.go + csrf_helpers.go)
- K2: requireScreenAccess() in allen manage-Handlern (Tenant-Isolation)
- K3: Tenant-Check bei DELETE /api/v1/media/{id}
- K4: requirePlaylistAccess() + GetByItemID() für JSON-API Playlist-Routen
- K5: Admin-Passwort nur noch als [gesetzt] geloggt
- K6: POST /api/v1/screens/register mit Pre-Shared-Secret (MORZ_INFOBOARD_REGISTER_SECRET)
- W1: Race Condition bei order_index behoben (atomare Subquery in AddItem)
- W2: Graceful Shutdown mit 15s Timeout auf SIGTERM/SIGINT
- W3: http.MaxBytesReader (512 MB) in allen Upload-Handlern
- W4: err.Error() nicht mehr an den Client
- W7: Template-Execution via bytes.Buffer (kein partial write bei Fehler)
- N1: Rate-Limiting auf /login (5 Versuche/Minute pro IP, httpapi/ratelimit.go)
- N5: Directory-Listing auf /uploads/ deaktiviert (neuteredFileSystem)
- N6: Uploads nach Tenant getrennt (uploads/{tenantSlug}/)
- V1: Upload-Logik konsolidiert in internal/fileutil/fileutil.go
- V5: Cookie-Name als Konstante reqcontext.SessionCookieName
- V6: Strukturiertes Logging mit log/slog + JSON-Handler
- V7: DB-Pool wird im Graceful-Shutdown geschlossen

### Phase 6: Screenshot-Erzeugung
- player/agent/internal/screenshot/screenshot.go erstellt
- Integration in app.go mit MORZ_INFOBOARD_SCREENSHOT_EVERY Config

### UX: PDF.js Integration
- pdf.min.js + pdf.worker.min.js als lokale Assets eingebettet
- Automatisches Seitendurchblättern im Player

### Ansible: Neue Rollen
- signage_base, signage_server, signage_provision erstellt
- inventory.yml und site.yml erweitert

### Konzept-Docs
- GRUPPEN-KONZEPT.md, KAMPAGNEN-AKTIVIERUNG.md, MONITORING-KONZEPT.md
- PROVISION-KONZEPT.md, TEMPLATE-EDITOR.md, WATCHDOG-KONZEPT.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:06:35 +01:00

232 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Info-Board Neu - Umsetzungs-Todo
## Phase 0 - Projektbasis
- [x] Projektverzeichnisstruktur unter `/srv/docker/info-board-neu` festlegen
- [x] Namenskonventionen fuer Server, Player, Rollen und Pakete definieren
- [x] Dokumentationsstruktur fuer Architektur, Betrieb und Deployment anlegen
- [x] Entscheidung fuer Server-Tech-Stack dokumentieren
- [x] Entscheidung fuer Player-Implementierung dokumentieren
- [x] Sprachentscheidung dokumentieren: `Go` als bevorzugte Sprache fuer Agent und moeglichst viele Backend-Komponenten
## Phase 1 - Fachliches Fundament
- [x] Rollenmodell fuer `admin` und monitorgebundene Nutzer final festschreiben
- [x] Datenmodell fuer `tenant`, `screen`, `user`, `media_asset`, `playlist`, `playlist_item`, `screen_status`, `screen_snapshot` definieren
- [x] Playlist-Semantik mit `duration`, `valid_from`, `valid_until`, `load_timeout`, `cache_policy`, `on_error` spezifizieren
- [x] Fallback-Regel fuer ungeplante oder leere Inhalte verbindlich definieren
- [x] Statusmodell fuer Online/Offline/Degraded/Error definieren
- [x] Kommandokatalog fuer Admin-Aktionen finalisieren
- [x] Template- und Kampagnenmodell fuer globale monitoruebergreifende Uebersteuerung finalisieren
- [x] Prioritaetsregel `campaign > tenant_playlist > fallback` verbindlich festschreiben
- [x] Entscheidung dokumentieren, dass `playlist_items.screen_id` entfernt wird
- [x] Entscheidung dokumentieren, dass Gruppen bei Kampagnen serverseitig in Einzel-Assignments expandiert werden
## Phase 2 - Technische Zielarchitektur
- [x] Server-Komponentenliste finalisieren
- [x] API-Schnittstellen grob definieren
- [x] MQTT-Topic-Struktur finalisieren
- [x] HTTPS- und MQTT-Aufgabentrennung dokumentieren
- [x] Screenshot-/Vorschaustrategie spezifizieren
- [x] Offline- und Cache-Strategie bis auf Dateiebene festlegen
- [x] Sicherheitsmodell fuer Uploads, Login und Rechte pruefen
- [x] API fuer Templates, Kampagnen, Aktivierung und Deaktivierung ausarbeiten
- [x] Provisionierungs-Workflow fuer neue Screens technisch durchplanen
- [x] Secret-Handling fuer initiale Root-Passwoerter oder Bootstrap-Zugaenge definieren
- [x] API-Fehlermodell und gemeinsame Fehlerantworten festlegen
- [x] ACK-Timeout-Strategie fuer `device_command` festlegen
- [x] `message_wall`-Rendering serverseitig verbindlich entscheiden
- [x] Netzwerktopologie fuer SSH-basierte Erstprovisionierung als Worker-/Jumphost-faehiges Modell festlegen
## Phase 3 - Player-Design
- [x] Minimalen Paketbedarf fuer den Player auf Raspberry Pi OS Debian 13 ermitteln
- [x] X11-Minimalkonzept fuer Chromium-Kiosk dokumentieren
- [x] Startmechanismus fuer Chromium ohne Desktop-Umgebung definieren
- [x] Verzeichnislayout auf dem Player festlegen
- [x] `player-agent` fachlich zuschneiden
- [x] `player-ui` fachlich zuschneiden (lokale Kiosk-Seite mit Splash + Sysinfo-Overlay)
- [x] Watchdog-Konzept fuer Browser und Agent definieren
- [x] Offline-Overlay-Verhalten spezifizieren
- [x] Fehlerbehandlung fuer Web-Inhalte und Timeouts ausarbeiten
- [x] Display-Steuerung fuer An/Aus, Rotation und Neustart planen
- [x] Sysinfo-Overlay erweitern: load, freier RAM, IP-Adresse(n) anzeigen
## Phase 4 - Server-Design
- [x] API-Backend fachlich schneiden
- [x] Admin-Oberflaeche in Hauptbereiche aufteilen
- [x] Firmen-/Monitor-Oberflaeche in Hauptbereiche aufteilen
- [x] Firmen-/Tenant-Oberfläche → siehe docs/TENANT-FEATURE-PLAN.md
- [x] Storage-Konzept fuer Uploads, Cache-Dateien und Screenshots festlegen
- [x] Authentifizierungskonzept festlegen
- [x] Mandantentrennung im Datenmodell und in den APIs absichern
- [x] Logging- und Monitoring-Konzept definieren
- [x] Template-Editor fuer globale Kampagnen fachlich schneiden
- [x] Aktivierungsoberflaeche fuer saisonale oder temporäre Kampagnen planen
- [x] Gruppierung oder Slot-Modell fuer monitoruebergreifende Layouts planen
- [x] Provisionierungs-UI fuer neue Screens fachlich und technisch schneiden
- [x] Jobrunner-Konzept fuer Ansible-gestuetzte Erstinstallation planen
## Phase 5 - Prototyping
- [x] Minimalen Server-Prototyp bauen
- [x] Minimalen Player-Agent-Prototyp bauen
- [x] Minimale Player-UI bauen
- [ ] Lokale Test-Playlist mit Bild, Video, PDF und Webseite anlegen
- [x] **BUG**: Datei `120papag.mpg` ist als `type: image` gespeichert, muss `type: video` sein Player-UI versucht `<img>`-Laden, was fehlschlägt
- [x] Fallback-Verzeichnisbetrieb demonstrieren
- [ ] `valid_from`/`valid_until` im Prototyp pruefen
- [x] Offline-Sync mit lokalem Cache pruefen
- [x] MQTT-Topic `signage/screen/{screenSlug}/playlist-changed` spezifiziert und dokumentiert
- [ ] MQTT-Kommandos `reload`, `restart_player`, `reboot`, `display_on`, `display_off` testweise durchspielen
- [ ] globale Kampagne testen, die tenantbezogenen Content temporär ueberschreibt
- [ ] Rueckfall auf Normalbetrieb nach manueller Deaktivierung pruefen
## Phase 6 - Betriebsfaehigkeit
- [x] Docker-Compose-Setup fuer den Server anlegen
- [x] systemd-Units fuer den Player erstellen
- [x] Chromium-Kiosk-Startskript erstellen
- [x] Screenshot-Erzeugung auf dem Player integrieren
- [x] Heartbeat- und Statusmeldungen integrieren
- [x] MQTT-Playlist-Change-Synchronisation mit Backend-Debounce (2s) und Agent-Debounce (3s) implementiert
- [ ] Fehler- und Wiederanlaufverhalten verifizieren
## Phase 7 - Ansible-Automatisierung
- [x] Rolle `signage_base` erstellen
- [x] Rolle `signage_player` erstellen
- [x] Rolle `signage_display` erstellen
- [x] Rolle `signage_server` erstellen
- [x] Rolle `signage_provision` erstellen
- [x] Inventar-/Variablenmodell fuer mehrere Monitore entwerfen
- [x] Screen-spezifische Variablen wie `screen_id`, Rotation und Aufloesung abbilden
- [x] Erstinstallation eines neuen Players automatisieren
- [x] Update-Rollout eines bestehenden Players automatisieren
- [x] Bootstrap ueber Root-Passwort auf SSH-Key und dauerhafte Verwaltung umstellen
## Phase 8 - Pilotbetrieb
- [ ] Einen Referenzmonitor fuer den Pilotbetrieb auswaehlen
- [ ] Pilotmonitor neu aufsetzen
- [ ] Verbindung zum Zentralserver herstellen
- [ ] Upload- und Playlist-Pflege mit einem Testmandanten pruefen
- [ ] Admin-Funktionen am Pilotmonitor pruefen
- [ ] globale Template-Aktivierung fuer einen oder mehrere Monitore im Pilot testen
- [ ] Offline-Betrieb real testen
- [ ] Browser-/Renderer-Stabilitaet ueber laengeren Zeitraum beobachten
- [ ] komplette Neu-Provisionierung eines frischen Test-Screens aus dem Admin-Backend pruefen
## Phase 9 - Migration der Bestandsmonitore
- [ ] Migrationsreihenfolge fuer `info01` bis `info09` festlegen
- [ ] Rueckfallstrategie pro Monitor definieren
- [ ] Altinhalte in das neue Medienmodell ueberfuehren
- [ ] Playlists uebernehmen oder neu modellieren
- [ ] Monitore nacheinander umstellen
- [ ] Nach jeder Umstellung Stabilitaet und Fernsteuerung pruefen
## Phase 10 - Nacharbeiten
- [ ] Betriebsdokumentation schreiben
- [ ] Admin-Handbuch schreiben
- [ ] Kurzhandbuch fuer Firmen-Nutzer schreiben
- [ ] Backup- und Restore-Konzept dokumentieren
- [ ] Update- und Release-Prozess festlegen
- [ ] Langfristige Wayland-Neubewertung fuer spaetere Version vormerken
## UX-Verbesserungen (Gestaltungsplan)
### Hohe Prioritaet
- [x] Flash-Messages nach Aktionen in Manage-UI (Upload, Loeschen, Speichern) — Feedback fuer den Nutzer
- [x] Screen-Online/Offline-Status in Admin-Tabelle anzeigen (aus /status-Endpoint befuellen)
- [x] Playlist-Tabelle in overflow-x Wrapper einwickeln (Responsive auf kleinen Screens)
- [x] PDF-Darstellung: Sidebar und Toolbar im Chromium PDF-Viewer ausblenden (URL-Parameter navpanes=0, toolbar=0)
- [x] PDF-Darstellung: PDF.js fuer automatisches Seitendurchblaettern integrieren
### Mittlere Prioritaet
- [x] Loesch-Bestaetigung: Bulma-Modal statt browser-nativer confirm()-Dialog
- [x] Status-Page: Sprache von Englisch auf Deutsch vereinheitlichen
- [x] Status-Page: Relative Zeitstempel statt RFC3339 ("vor 2 Minuten")
- [x] Querlinks zwischen Admin-UI und Status-Page (Navigation)
- [x] Bulma und SortableJS als lokale Assets einbetten statt CDN
- [x] Player-UI: CSS-Transitions fuer sanfte Content-Wechsel (Fade statt abrupt)
- [x] Player-UI: Erweitertes Sysinfo-Overlay (aktueller Titel, Playlist-Laenge)
- [x] Aria-Labels fuer Loesch-Buttons und Drag-Handles (Accessibility)
### Niedrige Prioritaet
- [x] Upload-Fortschrittsbalken in Manage-UI
- [x] vars.yml Download-Button in Provision-UI statt Copy-Paste
- [x] Toggle-Switch statt Ja/Nein-Select fuer Enabled-Feld
## UX-Bug-Fixes
- [x] Fix: Protokoll-relative URLs (//cdn...) werden nicht mehr durch URL-Normalisierung kaputtgeschrieben
- [x] Fix: PDF-Fragment-Parameter werden mit bestehenden Fragments gemerged statt blind angehängt
- [x] Fix: /api/startup-token setzt Cache-Control: no-store Header (Server + Client)
- [x] Fix: TestAssetsServed Nil-Dereferenz durch tote Goroutine behoben
## Security & Code-Review (Opus, 2026-03-23)
### Kritisch — Sicherheitslücken
- [x] **K2** Tenant-Isolation für `/manage/{screenSlug}/*`: `requireScreenAccess()` in allen manage-Handlern
- [x] **K3** `DELETE /api/v1/media/{id}`: Tenant-Check via reqcontext.UserFromContext
- [x] **K4** JSON-API Playlist-Routen (`/items`, `/playlists/*/items`, `/order`, `/duration`): `requirePlaylistAccess()` + `GetByItemID()` im Store
- [x] **K1** CSRF-Schutz: Double-Submit-Cookie-Pattern (`httpapi/csrf.go`); JS-Injection in alle Templates; Middleware in Router
- [x] **K6** `POST /api/v1/screens/register`: Pre-Shared-Secret via `MORZ_INFOBOARD_REGISTER_SECRET` (Header `X-Register-Secret`); Player-Agent sendet Secret mit
- [x] **K5** Admin-Passwort aus Log entfernt — nur `[gesetzt]` wird geloggt
### Wichtig — Robustheit
- [x] **N5** Directory-Listing auf `/uploads/` deaktiviert via `neuteredFileSystem` (`httpapi/uploads.go`)
- [x] **N6** Uploads nach Tenant getrennt: `fileutil.SaveUploadedFile()` legt Dateien in `uploads/{tenantSlug}/` ab
- [x] **W1** Race Condition bei `order_index` behoben: atomare Subquery in `AddItem()`
- [x] **W2** Graceful Shutdown implementiert: `http.Server.Shutdown()` mit 15s Timeout auf SIGTERM/SIGINT
- [x] **W3** Upload mit `http.MaxBytesReader` begrenzt (512 MB) in allen drei Upload-Handlern
- [x] **W4** `err.Error()` nicht mehr an den Client — generische Fehlermeldungen, Details serverseitig
- [x] **W7** Template-Execution-Errors: `bytes.Buffer`-Rendering, erst bei Erfolg an Client senden (`renderTemplate()`)
### Verbesserung — Wartbarkeit
- [ ] **V3** Keine Tests für Auth, Middleware, Tenant-Handler (gesamter Phase-1-5-Code ohne Abdeckung)
- [x] **V1** Upload-Logik konsolidiert in `internal/fileutil/fileutil.go` (`SaveUploadedFile`)
- [x] **V5** Cookie-Name als Konstante `reqcontext.SessionCookieName` — manage/auth.go und middleware.go nutzen sie
- [x] **V6** Strukturiertes Logging: `log/slog` mit JSON-Handler in `main.go`; `app.go` nutzt `slog.Info/slog.Error`
- [x] **V7** DB-Pool wird im Graceful-Shutdown-Handler geschlossen (`a.dbPool.Close()`)
### Nice-to-have — Features
- [x] **N1** Rate-Limiting auf `/login`: In-Memory Sliding-Window (5 Versuche/Minute pro IP) via `httpapi/ratelimit.go`
- [ ] **N2** Passwort-Änderung / Self-Service-Reset
- [ ] **N3** Tenant-User-Management im Admin-UI
- [ ] **N4** Session-TTL via Config-Variable steuerbar (aktuell hardcoded 8h)
**Hinweis K6:** `MORZ_INFOBOARD_REGISTER_SECRET` muss in `server/.env` / `docker-compose.yml` und in der Player-Config (`MORZ_INFOBOARD_REGISTER_SECRET` oder `register_secret` in `config.json`) identisch gesetzt werden. Wenn die Variable leer ist, bleibt der Endpoint offen (Rückwärtskompatibilität).
## Querschnittsthemen
- [ ] Datensicherung fuer Datenbank und Medien einplanen
- [ ] TLS-/Reverse-Proxy-Konzept festlegen
- [ ] Ressourcenverbrauch auf Raspberry Pi beobachten
- [ ] Verhalten bei grossen Videos optimieren
- [ ] Verhalten bei defekten externen Webseiten absichern
- [ ] Datenschutz- und Rechtefragen fuer Screenshots/Vorschauen pruefen
## Erste konkrete Abarbeitungsreihenfolge
- [x] 1. Projektstruktur im neuen Verzeichnis vervollstaendigen
- [x] 2. Datenmodell in eigener Datei ausformulieren
- [x] 3. API- und MQTT-Vertrag definieren
- [x] 4. Player-Minimalkonzept fuer Raspberry Pi OS Debian 13 festzurren
- [x] 5. Server-Compose-Grundgeruest erstellen
- [x] 6. Player-Prototyp mit lokalem Browser-Renderer bauen
- [x] 7. Offline-Cache und Fallback robust machen
- [x] 8. UIs fuer Admin und Firmen schrittweise aufbauen
- [x] 9. Ansible-Rollen erstellen
- [ ] 10. Pilotmonitor migrieren