Info Tab und MFA
This commit is contained in:
45
README.md
45
README.md
@@ -18,11 +18,13 @@ Browserbasiertes Digital-Signage-System für interne Info-Screens.
|
||||
- **Digital Voice Agent**: global konfigurierbarer Voice-Agent-Button pro Screen (9 Positionen, iframe-Overlay mit positionsgetreuem Zurück-Button, Typewriter-Tagline mit mehrsprachigen Wechseltexten, optionales Bild über dem Button)
|
||||
- **Stay-on-First**: Screen kann auf erstem Playlist-Element stehen bleiben (kein automatischer Refresh)
|
||||
- **Online/Offline-Status**: Player-Status wird via dateibasiertem Heartbeat ermittelt – grün (online) bei aktivem Player, rot (offline) nach 60s ohne Poll
|
||||
- **Client-Info**: Tooltip beim Hovern über den Online-Button zeigt Browser, Bildschirmauflösung, IP-Adresse und letzten Heartbeat-Zeitpunkt; Info-Tab pro Screen zeigt detaillierte Client-Info-Tabelle
|
||||
- **User-Verwaltung**: Mehrere User mit Rollen (Admin / Superuser / User), E-Mail als Login, Passwort-Hashing (scrypt), Berechtigungen pro Standort
|
||||
- **Passwort-Workflow**: First-Login-Änderung, Admin-Reset mit temporärem Passwort, Notfall-Login bei fehlendem Hash
|
||||
- **MFA (Multi-Faktor)**: Optionale TOTP-Authentifizierung (Google Authenticator, Authy) per User aktivierbar; QR-Code-Scan + Code-Verifikation, 8 Recovery-Codes, Deaktivierung mit Passwort-Bestätigung
|
||||
- **Admin Dashboard**: `/admin/dashboard` – Statistiken (Sites, Screens, User, Admins, Superuser) mit Sparkline-Charts + Trendanzeige, Aktivitätsverlauf
|
||||
- **Aktivitätsverlauf**: Alle Erstell-/Lösch-/Änderungsaktionen sowie Login/Logout werden in `history.json` protokolliert und im Dashboard angezeigt
|
||||
- Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien (Tabler Tabs)
|
||||
- Tab-basierte Admin-UI pro Screen: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien, Info (Tabler Tabs)
|
||||
- Priority-Seite ebenfalls mit Tabs: Playlist und Medien
|
||||
- Dark Mode (localStorage-persistiert)
|
||||
- CI-konformes Admin-UI (CANCOM-Design: `brand-surface`, `nav-surface` rot)
|
||||
@@ -42,7 +44,7 @@ Flask App (Server)
|
||||
├── config.json
|
||||
├── users.json
|
||||
├── history.json
|
||||
├── /tmp/signage-heartbeat/ (Heartbeat-Dateien für Online-Status)
|
||||
├── /tmp/signage-heartbeat/ (Heartbeat-JSONs mit Client-Info für Online-Status)
|
||||
├── media/
|
||||
│ ├── <site>/
|
||||
│ │ ├── <screen>/
|
||||
@@ -86,7 +88,10 @@ signage/
|
||||
│ ├── user_list.html # User-Liste (Admin)
|
||||
│ ├── user_create.html # User anlegen (Admin)
|
||||
│ ├── user_edit.html # User bearbeiten (Admin)
|
||||
│ ├── _header.html # Gemeinsamer Header
|
||||
│ ├── mfa_setup.html # MFA einrichten (QR-Code + Deaktivierung)
|
||||
│ ├── mfa_verify.html # TOTP-Code nach Login eingeben
|
||||
│ ├── mfa_recovery.html # Einmalige Recovery-Codes-Anzeige
|
||||
│ ├── _header.html # Gemeinsamer Header (MFA-Link + Aktiv-Badge)
|
||||
│ ├── _footer.html # Gemeinsamer Footer
|
||||
│ └── _styles.html # Zentrale CSS (Variablen, Dark Mode)
|
||||
├── static/
|
||||
@@ -194,6 +199,11 @@ Server läuft auf `http://localhost:5005` (gunicorn mit 4 Workern, non-root User
|
||||
| `GET /admin/users` | User-Liste (Admin) |
|
||||
| `GET /admin/users/create` | User anlegen (Admin) |
|
||||
| `GET /admin/users/edit/<email>` | User bearbeiten (Admin) |
|
||||
| `GET /mfa/setup` | MFA einrichten / deaktivieren (QR-Code + Verifikation) |
|
||||
| `POST /mfa/setup` | TOTP-Code verifizieren und MFA aktivieren |
|
||||
| `GET /mfa/verify` | TOTP-Code nach Login eingeben |
|
||||
| `POST /mfa/verify` | TOTP-Code prüfen oder Recovery-Code verwenden |
|
||||
| `POST /mfa/disable` | MFA deaktivieren (mit Passwort-Bestätigung) |
|
||||
| `POST /admin/users/create` | User anlegen (Admin) |
|
||||
| `POST /admin/users/delete/<email>` | User löschen (Admin, letzter Admin geschützt) |
|
||||
| `POST /admin/users/reset-password/<email>` | Passwort-Reset mit temporärem Passwort (Admin) |
|
||||
@@ -204,11 +214,12 @@ Server läuft auf `http://localhost:5005` (gunicorn mit 4 Workern, non-root User
|
||||
|
||||
### Heartbeat / Online-Status
|
||||
|
||||
Der Player ruft alle 5s `/playlist/<site>/<screen>/hash` auf. Der Server schreibt dabei einen Timestamp nach `/tmp/signage-heartbeat/<site>/<screen>`. Im Admin-Dashboard wird geprüft, ob dieser Timestamp < 60s alt ist → grüner "Online"- oder roter "Offline"-Button pro Screen.
|
||||
Der Player ruft alle 5s `/playlist/<site>/<screen>/hash?w=<breite>&h=<höhe>` auf. Der Server speichert dabei ein JSON mit Client-Info (IP, User-Agent, Auflösung) nach `/tmp/signage-heartbeat/<site>/<screen>.json`. Im Admin-Dashboard wird geprüft, ob der Timestamp < 60s alt ist → grüner "Online"-Button mit Tooltip (Browser, Auflösung, IP, letztes Update) oder roter "Offline"-Button pro Screen.
|
||||
|
||||
```bash
|
||||
# Heartbeat-Dateien anzeigen
|
||||
ls -la /tmp/signage-heartbeat/*/
|
||||
cat /tmp/signage-heartbeat/stuttgart/lobby.json
|
||||
```
|
||||
|
||||
Nach einem Server-Neustart werden alle Screens kurz rot, bis die Player wieder pollen (max. 60s).
|
||||
@@ -219,7 +230,8 @@ Nach einem Server-Neustart werden alle Screens kurz rot, bis die Player wieder p
|
||||
http://localhost:5005/admin/<standort>
|
||||
```
|
||||
|
||||
- Screens konfigurieren (Tab-basiert: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien)
|
||||
- Screens konfigurieren (Tab-basiert: Playlist, Einstellungen, Aktionen, Digital Voice Agent, Medien, Info)
|
||||
- Info-Tab pro Screen zeigt Client-Info-Tabelle: IP-Adresse, Browser (User-Agent), Bildschirmauflösung und letzter Heartbeat-Zeitpunkt
|
||||
- Medien hochladen / löschen
|
||||
- Playlist per Drag & Drop sortieren
|
||||
- Priority-Playlist verwalten (ebenfalls mit Tabs)
|
||||
@@ -228,7 +240,7 @@ http://localhost:5005/admin/<standort>
|
||||
- Digital Voice Agent pro Screen konfigurieren (Position, Bild ein/aus, Typewriter-Tagline)
|
||||
- Stay-on-First-Modus pro Screen (kein automatischer Refresh)
|
||||
- Standorte anlegen & löschen
|
||||
- **Userverwaltung**: User anlegen, bearbeiten, löschen, Passwort-Reset (temporäres Passwort wird angezeigt)
|
||||
- **Userverwaltung**: User anlegen, bearbeiten, löschen, Passwort-Reset (temporäres Passwort wird angezeigt), MFA-Status pro User in der Liste (grünes "Aktiv"-Badge oder "–")
|
||||
|
||||
### Admin Dashboard
|
||||
|
||||
@@ -281,12 +293,20 @@ Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `
|
||||
- `users.json` wird beim ersten Start aus `config.json.admin` befüllt
|
||||
- **Notfall**: `users.json` löschen → Server-Neustart erzeugt neuen Admin aus `config.json`
|
||||
|
||||
### User-Felder (`users.json`)
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|------|-----|-------------|
|
||||
| `server_url` | string | Öffentliche Server-URL für Player-Links |
|
||||
| `voice_agent_url` | string | URL des Digital Voice Agents (global, read-only im Admin) |
|
||||
|
||||
## Konfiguration (`config.json`)
|
||||
| `password_hash` | string | scrypt-Hash des Passworts |
|
||||
| `role` | string | `admin`, `superuser` oder `user` |
|
||||
| `sites` | array | Freigegebene Standorte (nur für User-Rolle) |
|
||||
| `must_change_password` | bool | Erzwingt Passwort-Änderung beim nächsten Login |
|
||||
| `first_name` / `last_name` | string | Vor- und Nachname |
|
||||
| `department` | string | Abteilung |
|
||||
| `notes` | string | Interne Notizen |
|
||||
| `mfa_enabled` | bool | MFA aktiv (TOTP) |
|
||||
| `mfa_secret` | string | TOTP-Secret (sofort beim ersten GET `/mfa/setup` persistiert) |
|
||||
| `mfa_recovery_codes` | array | 8 Recovery-Codes (hex, 7 Stellen, einmalig verwendbar) |
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -370,8 +390,9 @@ Maximal 3 Kunden eingeben → Logos werden via OpenAI + Brandfetch gesucht → `
|
||||
- `load_users()` / `save_users()` / `get_user()` – User aus `users.json`
|
||||
- `init_user_db()` – initialen Admin aus `config.json` anlegen
|
||||
- `get_accessible_sites(cfg, user)` – Standorte nach User-Berechtigung filtern
|
||||
- `record_heartbeat(site, screen)` – Heartbeat-Datei schreiben (wird vom Player-Poll getriggert)
|
||||
- `record_heartbeat(site, screen, ip, ua, resolution)` – Heartbeat-JSON mit Client-Info schreiben
|
||||
- `screen_is_active(site, screen)` – Prüft ob Heartbeat < 60s alt ist
|
||||
- `get_client_info(site, screen)` – Gibt gespeicherte Client-Info zurück (Dict mit ip, ua, resolution, last_seen)
|
||||
- `admin_required` / `site_access_required` – Zugriffs-Dekoratoren
|
||||
|
||||
---
|
||||
@@ -385,6 +406,8 @@ Wird über `localStorage("signage-theme")` persistiert. Umschalt-Button im Heade
|
||||
## Sicherheit
|
||||
|
||||
- Admin-Bereich per Flask-Login geschützt (E-Mail + Passwort)
|
||||
- Optionale MFA (TOTP) per User aktivierbar (Google Authenticator, Authy)
|
||||
- 8 Recovery-Codes pro User, einmalig bei Aktivierung angezeigt
|
||||
- Passwörter gehasht (scrypt) in `users.json`
|
||||
- Drei Rollen: Admin (alle Standorte + Userverwaltung), Superuser (alle Standorte), User (nur zugewiesene Standorte)
|
||||
- Zugriffskontrolle per Decorator (`@admin_required`, `@site_access_required`)
|
||||
|
||||
Reference in New Issue
Block a user