Doku: Screen-Usserverwaltung (Phase 8)
Aktualisiert Dokumentation für Screen-User Management nach Backy's Implementierung:
docs/SCHEMA.md:
- users.role erweitert: 'admin' | 'screen_user' | 'tenant'
- Neue Tabelle: user_screen_permissions (user_id, screen_id, created_at, unique constraint, FK mit CASCADE)
- AuthStore: CreateScreenUser, ListScreenUsers, DeleteUser
- ScreenStore: GetAccessibleScreens, HasUserScreenAccess, AddUserToScreen, RemoveUserFromScreen, GetScreenUsers
docs/API-ENDPOINTS.md:
- POST /admin/users — Screen-User anlegen
- POST /admin/users/{userID}/delete — Screen-User löschen
- POST /admin/screens/{screenID}/users — User zu Screen hinzufügen
- POST /admin/screens/{screenID}/users/{userID}/remove — User von Screen entfernen
server/backend/README.md:
- AuthStore und ScreenStore Methoden dokumentiert
- Middleware RequireScreenAccess erklärt
- Migration 003_user_screen_permissions.sql erwähnt
DEVELOPMENT.md:
- users.role Werte dokumentiert (admin, screen_user, tenant)
Co-Authored-By: Backy (Screen-User Implementation) <noreply@anthropic.com>
This commit is contained in:
parent
d1d86126c8
commit
8e0501a012
4 changed files with 190 additions and 7 deletions
|
|
@ -164,6 +164,11 @@ Konfigurierbar ueber:
|
||||||
- `MORZ_INFOBOARD_REGISTER_SECRET` – Pre-Shared-Secret fuer POST /api/v1/screens/register; leer = offen fuer alle
|
- `MORZ_INFOBOARD_REGISTER_SECRET` – Pre-Shared-Secret fuer POST /api/v1/screens/register; leer = offen fuer alle
|
||||||
- `MORZ_INFOBOARD_DEV_MODE` – wenn `true`: Session-Cookie wird ohne `Secure`-Flag gesetzt (nur fuer lokale Entwicklung)
|
- `MORZ_INFOBOARD_DEV_MODE` – wenn `true`: Session-Cookie wird ohne `Secure`-Flag gesetzt (nur fuer lokale Entwicklung)
|
||||||
|
|
||||||
|
**Hinweis zu `users.role`:**
|
||||||
|
- `admin` — hat Zugriff auf alle Admin-Funktionen und Screens
|
||||||
|
- `screen_user` — hat Zugriff nur auf Screens, fuer die explizit ein Eintrag in `user_screen_permissions` existiert
|
||||||
|
- `tenant` — hat Zugriff auf alle Screens seines Tenants (veraltet, noch nicht vollstaendig implementiert)
|
||||||
|
|
||||||
Beispiele:
|
Beispiele:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -694,6 +694,89 @@ Rückleitung zur Admin-Seite.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Screen-User Management (Admin)
|
||||||
|
|
||||||
|
### POST /admin/users
|
||||||
|
|
||||||
|
Erstellt einen neuen Screen-User für einen Tenant (Admin-Formular).
|
||||||
|
|
||||||
|
**Request-Body (Form-Encoded):**
|
||||||
|
```
|
||||||
|
username=screenuser1&password=geheim
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Neuer User mit `role = 'screen_user'` wird angelegt
|
||||||
|
- Passwort wird per bcrypt gehasht
|
||||||
|
- User wird dem aktuellen Tenant zugeordnet
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `200 OK` oder `201 Created` — Screen-User erstellt
|
||||||
|
- `400 Bad Request` — Fehlende oder ungültige Parameter, Username bereits vorhanden
|
||||||
|
- `500 Internal Server Error` — DB-Fehler
|
||||||
|
|
||||||
|
Rückleitung zur Admin-Seite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /admin/users/{userID}/delete
|
||||||
|
|
||||||
|
Löscht einen Screen-User und alle zugeordneten Screen-Permissions.
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- User mit Rolle `screen_user` wird gelöscht
|
||||||
|
- Alle Einträge in `user_screen_permissions` für diesen User werden gelöscht
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `200 OK` — Screen-User gelöscht
|
||||||
|
- `404 Not Found` — User nicht vorhanden oder falscher Typ
|
||||||
|
- `500 Internal Server Error` — DB-Fehler
|
||||||
|
|
||||||
|
Rückleitung zur Admin-Seite.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /admin/screens/{screenID}/users
|
||||||
|
|
||||||
|
Fügt einen Screen-User zu einem Screen hinzu.
|
||||||
|
|
||||||
|
**Request-Body (Form-Encoded):**
|
||||||
|
```
|
||||||
|
user_id=<userID>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Eintrag in `user_screen_permissions` wird erstellt
|
||||||
|
- User muss vom Typ `screen_user` sein
|
||||||
|
- Unique-Constraint verhindert Duplikate
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `200 OK` — User zu Screen hinzugefügt
|
||||||
|
- `400 Bad Request` — Fehlende Parameter, User bereits hinzugefügt
|
||||||
|
- `404 Not Found` — Screen oder User nicht vorhanden
|
||||||
|
- `500 Internal Server Error` — DB-Fehler
|
||||||
|
|
||||||
|
Rückleitung zur Admin-Seite oder zum Screen-Detail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST /admin/screens/{screenID}/users/{userID}/remove
|
||||||
|
|
||||||
|
Entfernt einen Screen-User von einem Screen.
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Eintrag in `user_screen_permissions` wird gelöscht
|
||||||
|
- User behält seine Existenz; nur die Permission wird entfernt
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- `200 OK` — User von Screen entfernt
|
||||||
|
- `404 Not Found` — Screen, User oder Permission nicht vorhanden
|
||||||
|
- `500 Internal Server Error` — DB-Fehler
|
||||||
|
|
||||||
|
Rückleitung zur Admin-Seite oder zum Screen-Detail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Playlist Management UI (Web-Formulare)
|
## Playlist Management UI (Web-Formulare)
|
||||||
|
|
||||||
### GET /manage/{screenSlug}
|
### GET /manage/{screenSlug}
|
||||||
|
|
@ -954,6 +1037,11 @@ Die folgenden Endpoints sind derzeit vorbereitet, aber noch nicht vollständig i
|
||||||
|
|
||||||
## Änderungshistorie
|
## Änderungshistorie
|
||||||
|
|
||||||
|
- **2026-03-23 (Update):** Screen-User Management Endpoints (Doris / Doku-Review)
|
||||||
|
- `POST /admin/users` — Screen-User anlegen
|
||||||
|
- `POST /admin/users/{userID}/delete` — Screen-User löschen
|
||||||
|
- `POST /admin/screens/{screenID}/users` — User zu Screen hinzufügen
|
||||||
|
- `POST /admin/screens/{screenID}/users/{userID}/remove` — User von Screen entfernen
|
||||||
- **2026-03-23 (Update):** Security-Enhancements und Upload-Konsolidierung (Doris / Doku-Review)
|
- **2026-03-23 (Update):** Security-Enhancements und Upload-Konsolidierung (Doris / Doku-Review)
|
||||||
- CSRF-Schutz (Double-Submit-Cookie) in `internal/httpapi/csrf.go`
|
- CSRF-Schutz (Double-Submit-Cookie) in `internal/httpapi/csrf.go`
|
||||||
- Rate-Limiting für `/login` in `internal/httpapi/ratelimit.go`
|
- Rate-Limiting für `/login` in `internal/httpapi/ratelimit.go`
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,34 @@ unique(tenant_id, username)
|
||||||
|
|
||||||
Regeln:
|
Regeln:
|
||||||
|
|
||||||
- `role` in v1: `admin`, `tenant`
|
- `role` in v1: `admin`, `screen_user`, `tenant`
|
||||||
- `username` ist nur innerhalb eines Tenants eindeutig (Unique-Constraint auf `(tenant_id, username)`)
|
- `username` ist nur innerhalb eines Tenants eindeutig (Unique-Constraint auf `(tenant_id, username)`)
|
||||||
- `tenant_id` ist `NOT NULL` — jeder User gehoert genau einem Tenant
|
- `tenant_id` ist `NOT NULL` — jeder User gehoert genau einem Tenant
|
||||||
- IDs sind `text`, nicht `uuid`, enthalten aber UUID-Werte (via `gen_random_uuid()::text`)
|
- IDs sind `text`, nicht `uuid`, enthalten aber UUID-Werte (via `gen_random_uuid()::text`)
|
||||||
- Felder wie `email`, `active`, `last_login_at` und `updated_at` existieren in v1 nicht
|
- Felder wie `email`, `active`, `last_login_at` und `updated_at` existieren in v1 nicht
|
||||||
|
|
||||||
|
### `user_screen_permissions`
|
||||||
|
|
||||||
|
Zweck:
|
||||||
|
|
||||||
|
- Zuordnung von Screen-Usern zu Screens (rollenbasierter Zugriff)
|
||||||
|
|
||||||
|
Spalten:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
id uuid primary key
|
||||||
|
user_id text not null references users(id) on delete cascade
|
||||||
|
screen_id uuid not null references screens(id) on delete cascade
|
||||||
|
created_at timestamptz not null default now()
|
||||||
|
unique(user_id, screen_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
Regeln:
|
||||||
|
|
||||||
|
- `user_id` muss ein User mit `role = 'screen_user'` sein
|
||||||
|
- `screen_id` muss existieren; Loeschen des Screens loescht auch die Permission
|
||||||
|
- Loeschen des Users loescht auch alle seine Permissions
|
||||||
|
|
||||||
### `sessions`
|
### `sessions`
|
||||||
|
|
||||||
Zweck:
|
Zweck:
|
||||||
|
|
@ -529,6 +551,9 @@ last_error_message text null
|
||||||
Die Auth-Tabellen werden durch `server/backend/internal/db/migrations/002_auth.sql` angelegt
|
Die Auth-Tabellen werden durch `server/backend/internal/db/migrations/002_auth.sql` angelegt
|
||||||
und sind vollstaendig unter den Abschnitten `users` und `sessions` oben beschrieben.
|
und sind vollstaendig unter den Abschnitten `users` und `sessions` oben beschrieben.
|
||||||
|
|
||||||
|
Die Screen-Usserverwaltung wird durch `server/backend/internal/db/migrations/003_user_screen_permissions.sql` angelegt
|
||||||
|
und ist unter dem Abschnitt `user_screen_permissions` oben beschrieben.
|
||||||
|
|
||||||
Der `AuthStore` (`internal/store/auth.go`) stellt folgende Methoden bereit:
|
Der `AuthStore` (`internal/store/auth.go`) stellt folgende Methoden bereit:
|
||||||
|
|
||||||
- `GetUserByUsername(ctx, username)` — Nutzer per Username laden (inkl. `TenantSlug` via LEFT JOIN)
|
- `GetUserByUsername(ctx, username)` — Nutzer per Username laden (inkl. `TenantSlug` via LEFT JOIN)
|
||||||
|
|
@ -538,6 +563,17 @@ Der `AuthStore` (`internal/store/auth.go`) stellt folgende Methoden bereit:
|
||||||
- `CleanExpiredSessions(ctx)` — abgelaufene Sessions bereinigen
|
- `CleanExpiredSessions(ctx)` — abgelaufene Sessions bereinigen
|
||||||
- `EnsureAdminUser(ctx, tenantSlug, password)` — Admin-User beim Start anlegen wenn nicht vorhanden
|
- `EnsureAdminUser(ctx, tenantSlug, password)` — Admin-User beim Start anlegen wenn nicht vorhanden
|
||||||
- `VerifyPassword(ctx, userID, password)` — Passwort gegen bcrypt-Hash pruefen
|
- `VerifyPassword(ctx, userID, password)` — Passwort gegen bcrypt-Hash pruefen
|
||||||
|
- `CreateScreenUser(ctx, tenantID, username, password)` — neuen Screen-User anlegen
|
||||||
|
- `ListScreenUsers(ctx, tenantID)` — alle Screen-User eines Tenants auflisten
|
||||||
|
- `DeleteUser(ctx, userID)` — User und alle zugeordneten Permissions loeschen
|
||||||
|
|
||||||
|
Der `ScreenStore` (`internal/store/screen.go`) stellt folgende Methoden bereit:
|
||||||
|
|
||||||
|
- `GetAccessibleScreens(ctx, userID)` — alle Screens, auf die der User Zugriff hat
|
||||||
|
- `HasUserScreenAccess(ctx, userID, screenID)` — prueft ob User auf Screen zugreifen darf
|
||||||
|
- `AddUserToScreen(ctx, userID, screenID)` — User zu Screen hinzufuegen
|
||||||
|
- `RemoveUserFromScreen(ctx, userID, screenID)` — User von Screen entfernen
|
||||||
|
- `GetScreenUsers(ctx, screenID)` — alle User, die auf Screen Zugriff haben
|
||||||
|
|
||||||
## Wichtige Indizes
|
## Wichtige Indizes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,33 @@ Dieses Verzeichnis enthaelt das zentrale Go-Backend fuer das Info-Board-System.
|
||||||
- `internal/mqttnotifier/` — MQTT-Notifizierungen
|
- `internal/mqttnotifier/` — MQTT-Notifizierungen
|
||||||
- `internal/reqcontext/` — Context-Keys fuer authentifizierten User
|
- `internal/reqcontext/` — Context-Keys fuer authentifizierten User
|
||||||
|
|
||||||
|
## Datenbank-Stores
|
||||||
|
|
||||||
|
### AuthStore (`internal/store/auth.go`)
|
||||||
|
|
||||||
|
**Screen-User Management:**
|
||||||
|
- `CreateScreenUser(ctx, tenantID, username, passwordHash)` — neuen Screen-User anlegen
|
||||||
|
- `ListScreenUsers(ctx, tenantID)` — alle Screen-User eines Tenants auflisten
|
||||||
|
- `DeleteUser(ctx, userID)` — User und alle zugeordneten Permissions loeschen
|
||||||
|
|
||||||
|
**Authentifizierung:**
|
||||||
|
- `GetUserByUsername(ctx, username)` — Nutzer per Username laden
|
||||||
|
- `CreateSession(ctx, userID, ttl)` — neue Session anlegen
|
||||||
|
- `GetSessionUser(ctx, sessionID)` — User zu gueltigem Session-Token laden
|
||||||
|
- `DeleteSession(ctx, sessionID)` — Session loeschen (Logout)
|
||||||
|
- `CleanExpiredSessions(ctx)` — abgelaufene Sessions bereinigen
|
||||||
|
- `EnsureAdminUser(ctx, tenantSlug, password)` — Admin-User beim Start anlegen
|
||||||
|
- `VerifyPassword(ctx, userID, password)` — Passwort gegen bcrypt-Hash pruefen
|
||||||
|
|
||||||
|
### ScreenStore (`internal/store/screen.go`)
|
||||||
|
|
||||||
|
**Screen-User Zugriffskontrolle:**
|
||||||
|
- `GetAccessibleScreens(ctx, userID)` — alle Screens, auf die der User Zugriff hat
|
||||||
|
- `HasUserScreenAccess(ctx, userID, screenID)` — prueft ob User auf Screen zugreifen darf (boolean)
|
||||||
|
- `AddUserToScreen(ctx, userID, screenID)` — User zu Screen hinzufuegen (Eintrag in `user_screen_permissions`)
|
||||||
|
- `RemoveUserFromScreen(ctx, userID, screenID)` — User von Screen entfernen
|
||||||
|
- `GetScreenUsers(ctx, screenID)` — alle User, die auf Screen Zugriff haben
|
||||||
|
|
||||||
## Aktuelle Endpunkte
|
## Aktuelle Endpunkte
|
||||||
|
|
||||||
### Oeffentlich (keine Auth)
|
### Oeffentlich (keine Auth)
|
||||||
|
|
@ -76,11 +103,15 @@ Dieses Verzeichnis enthaelt das zentrale Go-Backend fuer das Info-Board-System.
|
||||||
### Nur Admins (`RequireAuth` + `RequireAdmin`)
|
### Nur Admins (`RequireAuth` + `RequireAdmin`)
|
||||||
|
|
||||||
| Methode | Pfad | Beschreibung |
|
| Methode | Pfad | Beschreibung |
|
||||||
|---------|-------------------------------------|---------------------------------------|
|
|---------|-------------------------------------------|---------------------------------------|
|
||||||
| GET | `/admin` | Admin-Uebersicht |
|
| GET | `/admin` | Admin-Uebersicht |
|
||||||
| POST | `/admin/screens/provision` | Provisionierungs-Job starten |
|
| POST | `/admin/screens/provision` | Provisionierungs-Job starten |
|
||||||
| POST | `/admin/screens` | Neuen Screen anlegen |
|
| POST | `/admin/screens` | Neuen Screen anlegen |
|
||||||
| POST | `/admin/screens/{screenId}/delete` | Screen loeschen |
|
| POST | `/admin/screens/{screenId}/delete` | Screen loeschen |
|
||||||
|
| POST | `/admin/users` | Screen-User anlegen |
|
||||||
|
| POST | `/admin/users/{userID}/delete` | Screen-User loeschen |
|
||||||
|
| POST | `/admin/screens/{screenID}/users` | User zu Screen hinzufuegen |
|
||||||
|
| POST | `/admin/screens/{screenID}/users/{userID}/remove` | User von Screen entfernen |
|
||||||
|
|
||||||
### Tenant-scoped (`RequireAuth` + `RequireTenantAccess`)
|
### Tenant-scoped (`RequireAuth` + `RequireTenantAccess`)
|
||||||
|
|
||||||
|
|
@ -113,3 +144,26 @@ Alle Werte per Umgebungsvariable:
|
||||||
| `MORZ_INFOBOARD_MQTT_PASSWORD` | MQTT-Passwort | leer |
|
| `MORZ_INFOBOARD_MQTT_PASSWORD` | MQTT-Passwort | leer |
|
||||||
|
|
||||||
Detailliertere Beschreibung und lokale Startbeispiele: `DEVELOPMENT.md`.
|
Detailliertere Beschreibung und lokale Startbeispiele: `DEVELOPMENT.md`.
|
||||||
|
|
||||||
|
## Middleware
|
||||||
|
|
||||||
|
### `RequireScreenAccess`
|
||||||
|
|
||||||
|
Middleware zur rollenbasierten Zugriffskontrolle auf Screen-Ressourcen.
|
||||||
|
|
||||||
|
**Verhalten:**
|
||||||
|
- Admins duerfen auf alle Screens zugreifen
|
||||||
|
- Screen-User duerfen nur auf Screens zugreifen, fuer die sie in `user_screen_permissions` eingetragen sind
|
||||||
|
- Tenant-User duerfen auf alle Screens ihres Tenants zugreifen
|
||||||
|
- Response: `403 Forbidden` wenn keine Berechtigung
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
- `GET /api/v1/screens/{screenId}/playlist`
|
||||||
|
- `POST /manage/{screenSlug}/...`
|
||||||
|
- Alle privaten Screen-Endpunkte
|
||||||
|
|
||||||
|
## Migrationen
|
||||||
|
|
||||||
|
- `001_core.sql` — initiales Schema (Tenants, Screens, Playlists, Media, etc.)
|
||||||
|
- `002_auth.sql` — Auth-Tabellen (`users`, `sessions`)
|
||||||
|
- `003_user_screen_permissions.sql` — Screen-User Management (`user_screen_permissions`)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue