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

12 KiB
Raw Blame History

Info-Board Neu - Umsetzungs-Todo

Phase 0 - Projektbasis

  • Projektverzeichnisstruktur unter /srv/docker/info-board-neu festlegen
  • Namenskonventionen fuer Server, Player, Rollen und Pakete definieren
  • Dokumentationsstruktur fuer Architektur, Betrieb und Deployment anlegen
  • Entscheidung fuer Server-Tech-Stack dokumentieren
  • Entscheidung fuer Player-Implementierung dokumentieren
  • Sprachentscheidung dokumentieren: Go als bevorzugte Sprache fuer Agent und moeglichst viele Backend-Komponenten

Phase 1 - Fachliches Fundament

  • Rollenmodell fuer admin und monitorgebundene Nutzer final festschreiben
  • Datenmodell fuer tenant, screen, user, media_asset, playlist, playlist_item, screen_status, screen_snapshot definieren
  • Playlist-Semantik mit duration, valid_from, valid_until, load_timeout, cache_policy, on_error spezifizieren
  • Fallback-Regel fuer ungeplante oder leere Inhalte verbindlich definieren
  • Statusmodell fuer Online/Offline/Degraded/Error definieren
  • Kommandokatalog fuer Admin-Aktionen finalisieren
  • Template- und Kampagnenmodell fuer globale monitoruebergreifende Uebersteuerung finalisieren
  • Prioritaetsregel campaign > tenant_playlist > fallback verbindlich festschreiben
  • Entscheidung dokumentieren, dass playlist_items.screen_id entfernt wird
  • Entscheidung dokumentieren, dass Gruppen bei Kampagnen serverseitig in Einzel-Assignments expandiert werden

Phase 2 - Technische Zielarchitektur

  • Server-Komponentenliste finalisieren
  • API-Schnittstellen grob definieren
  • MQTT-Topic-Struktur finalisieren
  • HTTPS- und MQTT-Aufgabentrennung dokumentieren
  • Screenshot-/Vorschaustrategie spezifizieren
  • Offline- und Cache-Strategie bis auf Dateiebene festlegen
  • Sicherheitsmodell fuer Uploads, Login und Rechte pruefen
  • API fuer Templates, Kampagnen, Aktivierung und Deaktivierung ausarbeiten
  • Provisionierungs-Workflow fuer neue Screens technisch durchplanen
  • Secret-Handling fuer initiale Root-Passwoerter oder Bootstrap-Zugaenge definieren
  • API-Fehlermodell und gemeinsame Fehlerantworten festlegen
  • ACK-Timeout-Strategie fuer device_command festlegen
  • message_wall-Rendering serverseitig verbindlich entscheiden
  • Netzwerktopologie fuer SSH-basierte Erstprovisionierung als Worker-/Jumphost-faehiges Modell festlegen

Phase 3 - Player-Design

  • Minimalen Paketbedarf fuer den Player auf Raspberry Pi OS Debian 13 ermitteln
  • X11-Minimalkonzept fuer Chromium-Kiosk dokumentieren
  • Startmechanismus fuer Chromium ohne Desktop-Umgebung definieren
  • Verzeichnislayout auf dem Player festlegen
  • player-agent fachlich zuschneiden
  • player-ui fachlich zuschneiden (lokale Kiosk-Seite mit Splash + Sysinfo-Overlay)
  • Watchdog-Konzept fuer Browser und Agent definieren
  • Offline-Overlay-Verhalten spezifizieren
  • Fehlerbehandlung fuer Web-Inhalte und Timeouts ausarbeiten
  • Display-Steuerung fuer An/Aus, Rotation und Neustart planen
  • Sysinfo-Overlay erweitern: load, freier RAM, IP-Adresse(n) anzeigen

Phase 4 - Server-Design

  • API-Backend fachlich schneiden
  • Admin-Oberflaeche in Hauptbereiche aufteilen
  • Firmen-/Monitor-Oberflaeche in Hauptbereiche aufteilen
  • Firmen-/Tenant-Oberfläche → siehe docs/TENANT-FEATURE-PLAN.md
  • Storage-Konzept fuer Uploads, Cache-Dateien und Screenshots festlegen
  • Authentifizierungskonzept festlegen
  • Mandantentrennung im Datenmodell und in den APIs absichern
  • Logging- und Monitoring-Konzept definieren
  • Template-Editor fuer globale Kampagnen fachlich schneiden
  • Aktivierungsoberflaeche fuer saisonale oder temporäre Kampagnen planen
  • Gruppierung oder Slot-Modell fuer monitoruebergreifende Layouts planen
  • Provisionierungs-UI fuer neue Screens fachlich und technisch schneiden
  • Jobrunner-Konzept fuer Ansible-gestuetzte Erstinstallation planen

Phase 5 - Prototyping

  • Minimalen Server-Prototyp bauen
  • Minimalen Player-Agent-Prototyp bauen
  • Minimale Player-UI bauen
  • Lokale Test-Playlist mit Bild, Video, PDF und Webseite anlegen
  • BUG: Datei 120papag.mpg ist als type: image gespeichert, muss type: video sein Player-UI versucht <img>-Laden, was fehlschlägt
  • Fallback-Verzeichnisbetrieb demonstrieren
  • valid_from/valid_until im Prototyp pruefen
  • Offline-Sync mit lokalem Cache pruefen
  • 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

  • Docker-Compose-Setup fuer den Server anlegen
  • systemd-Units fuer den Player erstellen
  • Chromium-Kiosk-Startskript erstellen
  • Screenshot-Erzeugung auf dem Player integrieren
  • Heartbeat- und Statusmeldungen integrieren
  • MQTT-Playlist-Change-Synchronisation mit Backend-Debounce (2s) und Agent-Debounce (3s) implementiert
  • Fehler- und Wiederanlaufverhalten verifizieren

Phase 7 - Ansible-Automatisierung

  • Rolle signage_base erstellen
  • Rolle signage_player erstellen
  • Rolle signage_display erstellen
  • Rolle signage_server erstellen
  • Rolle signage_provision erstellen
  • Inventar-/Variablenmodell fuer mehrere Monitore entwerfen
  • Screen-spezifische Variablen wie screen_id, Rotation und Aufloesung abbilden
  • Erstinstallation eines neuen Players automatisieren
  • Update-Rollout eines bestehenden Players automatisieren
  • 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

  • Flash-Messages nach Aktionen in Manage-UI (Upload, Loeschen, Speichern) — Feedback fuer den Nutzer
  • Screen-Online/Offline-Status in Admin-Tabelle anzeigen (aus /status-Endpoint befuellen)
  • Playlist-Tabelle in overflow-x Wrapper einwickeln (Responsive auf kleinen Screens)
  • PDF-Darstellung: Sidebar und Toolbar im Chromium PDF-Viewer ausblenden (URL-Parameter navpanes=0, toolbar=0)
  • PDF-Darstellung: PDF.js fuer automatisches Seitendurchblaettern integrieren

Mittlere Prioritaet

  • Loesch-Bestaetigung: Bulma-Modal statt browser-nativer confirm()-Dialog
  • Status-Page: Sprache von Englisch auf Deutsch vereinheitlichen
  • Status-Page: Relative Zeitstempel statt RFC3339 ("vor 2 Minuten")
  • Querlinks zwischen Admin-UI und Status-Page (Navigation)
  • Bulma und SortableJS als lokale Assets einbetten statt CDN
  • Player-UI: CSS-Transitions fuer sanfte Content-Wechsel (Fade statt abrupt)
  • Player-UI: Erweitertes Sysinfo-Overlay (aktueller Titel, Playlist-Laenge)
  • Aria-Labels fuer Loesch-Buttons und Drag-Handles (Accessibility)

Niedrige Prioritaet

  • Upload-Fortschrittsbalken in Manage-UI
  • vars.yml Download-Button in Provision-UI statt Copy-Paste
  • Toggle-Switch statt Ja/Nein-Select fuer Enabled-Feld

UX-Bug-Fixes

  • Fix: Protokoll-relative URLs (//cdn...) werden nicht mehr durch URL-Normalisierung kaputtgeschrieben
  • Fix: PDF-Fragment-Parameter werden mit bestehenden Fragments gemerged statt blind angehängt
  • Fix: /api/startup-token setzt Cache-Control: no-store Header (Server + Client)
  • Fix: TestAssetsServed Nil-Dereferenz durch tote Goroutine behoben

Security & Code-Review (Opus, 2026-03-23)

Kritisch — Sicherheitslücken

  • K2 Tenant-Isolation für /manage/{screenSlug}/*: requireScreenAccess() in allen manage-Handlern
  • K3 DELETE /api/v1/media/{id}: Tenant-Check via reqcontext.UserFromContext
  • K4 JSON-API Playlist-Routen (/items, /playlists/*/items, /order, /duration): requirePlaylistAccess() + GetByItemID() im Store
  • K1 CSRF-Schutz: Double-Submit-Cookie-Pattern (httpapi/csrf.go); JS-Injection in alle Templates; Middleware in Router
  • K6 POST /api/v1/screens/register: Pre-Shared-Secret via MORZ_INFOBOARD_REGISTER_SECRET (Header X-Register-Secret); Player-Agent sendet Secret mit
  • K5 Admin-Passwort aus Log entfernt — nur [gesetzt] wird geloggt

Wichtig — Robustheit

  • N5 Directory-Listing auf /uploads/ deaktiviert via neuteredFileSystem (httpapi/uploads.go)
  • N6 Uploads nach Tenant getrennt: fileutil.SaveUploadedFile() legt Dateien in uploads/{tenantSlug}/ ab
  • W1 Race Condition bei order_index behoben: atomare Subquery in AddItem()
  • W2 Graceful Shutdown implementiert: http.Server.Shutdown() mit 15s Timeout auf SIGTERM/SIGINT
  • W3 Upload mit http.MaxBytesReader begrenzt (512 MB) in allen drei Upload-Handlern
  • W4 err.Error() nicht mehr an den Client — generische Fehlermeldungen, Details serverseitig
  • 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)
  • V1 Upload-Logik konsolidiert in internal/fileutil/fileutil.go (SaveUploadedFile)
  • V5 Cookie-Name als Konstante reqcontext.SessionCookieName — manage/auth.go und middleware.go nutzen sie
  • V6 Strukturiertes Logging: log/slog mit JSON-Handler in main.go; app.go nutzt slog.Info/slog.Error
  • V7 DB-Pool wird im Graceful-Shutdown-Handler geschlossen (a.dbPool.Close())

Nice-to-have — Features

  • 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

  • 1. Projektstruktur im neuen Verzeichnis vervollstaendigen
  • 2. Datenmodell in eigener Datei ausformulieren
  • 3. API- und MQTT-Vertrag definieren
  • 4. Player-Minimalkonzept fuer Raspberry Pi OS Debian 13 festzurren
  • 5. Server-Compose-Grundgeruest erstellen
  • 6. Player-Prototyp mit lokalem Browser-Renderer bauen
  • 7. Offline-Cache und Fallback robust machen
  • 8. UIs fuer Admin und Firmen schrittweise aufbauen
  • 9. Ansible-Rollen erstellen
  • 10. Pilotmonitor migrieren