141 lines
6.6 KiB
Markdown
141 lines
6.6 KiB
Markdown
|
|
# Zugriffsrechte & User-Status
|
|||
|
|
|
|||
|
|
Abgeleitet direkt aus dem Code (`app/main.py`, `app/auth.py`, Templates). Stand des Generats: 2026-04-20.
|
|||
|
|
|
|||
|
|
## Drei User-Status
|
|||
|
|
|
|||
|
|
| Status | Signal | Guard im Code |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **Gast** (`anonymous`) | Kein gültiger `access_token`-Cookie | — |
|
|||
|
|
| **Registriert** (`authenticated`) | Gültiger Keycloak-JWT, Rolle beliebig | `Depends(require_auth)` |
|
|||
|
|
| **Admin** | Keycloak-JWT mit Rolle `admin` oder `gwoe-admin` | `Depends(require_admin)` |
|
|||
|
|
|
|||
|
|
Zwei Sonder-Modi:
|
|||
|
|
|
|||
|
|
- **Dev-Modus** (`AUTH_ENABLED=false` in .env): jede Anfrage wird als Anonymous+Admin ausgegeben, sämtliche Guards fallen. Nur lokal, nie in Prod. Siehe ADR 0005.
|
|||
|
|
- **Approval-pending**: Nutzer:innen, die über `/api/auth/register` angelegt sind aber noch nicht von einem Admin via `/api/auth/approve-user` freigeschaltet wurden, können sich einloggen, aber keine `require_auth`-Features nutzen (Keycloak verweigert Token-Ausgabe). Siehe `docs/how-to/keycloak-setup.md`.
|
|||
|
|
|
|||
|
|
## Endpoint × Status-Matrix (63 Routes)
|
|||
|
|
|
|||
|
|
### Admin-only (5)
|
|||
|
|
|
|||
|
|
Nur Nutzer:innen mit Rolle `admin`/`gwoe-admin` erreichen diese:
|
|||
|
|
|
|||
|
|
| Methode | Pfad | Zweck |
|
|||
|
|
|---|---|---|
|
|||
|
|
| POST | `/api/batch-analyze` | Batch-Bewertung starten |
|
|||
|
|
| POST | `/api/programme/index` | Wahlprogramm indexieren |
|
|||
|
|
| POST | `/api/auth/approve-user` | User-Freischaltung |
|
|||
|
|
| GET | `/api/auth/pending-users` | Liste offener Freischaltungs-Anträge |
|
|||
|
|
| DELETE | `/api/assessment/delete` | Bewertung löschen (für Re-Analyse) |
|
|||
|
|
|
|||
|
|
### Authenticated (8)
|
|||
|
|
|
|||
|
|
Jede:r eingeloggte:r Nutzer:in:
|
|||
|
|
|
|||
|
|
| Methode | Pfad | Zweck |
|
|||
|
|
|---|---|---|
|
|||
|
|
| POST | `/analyze` | Freitext-Upload bewerten |
|
|||
|
|
| POST | `/api/analyze-drucksache` | Antrag aus Landtag bewerten |
|
|||
|
|
| POST | `/api/bookmark` | Merklisten-Eintrag toggeln |
|
|||
|
|
| POST | `/api/comment` | Kommentar anlegen |
|
|||
|
|
| DELETE | `/api/comment/{id}` | Eigenen Kommentar löschen |
|
|||
|
|
| POST | `/api/subscriptions` | E-Mail-Abo anlegen |
|
|||
|
|
| DELETE | `/api/subscriptions/{id}` | E-Mail-Abo löschen |
|
|||
|
|
| POST | `/api/vote` | Antrag-Bewertung votieren |
|
|||
|
|
|
|||
|
|
### Optional-User (5)
|
|||
|
|
|
|||
|
|
Funktionieren auch ohne Login, aber personalisieren mit User-Daten wenn eingeloggt:
|
|||
|
|
|
|||
|
|
| Methode | Pfad | Verhalten |
|
|||
|
|
|---|---|---|
|
|||
|
|
| GET | `/api/auth/me` | Gibt Auth-Status zurück |
|
|||
|
|
| GET | `/api/bookmarks` | Liste eigener Bookmarks (wenn eingeloggt), sonst `[]` |
|
|||
|
|
| GET | `/api/comments?drucksache=X` | Öffentliche + eigene Kommentare |
|
|||
|
|
| GET | `/api/subscriptions` | Eigene Abos |
|
|||
|
|
| GET | `/api/votes?drucksache=X` | Alle Votes + eigenes Vote-Flag |
|
|||
|
|
|
|||
|
|
### Public (45)
|
|||
|
|
|
|||
|
|
Alle Lese-Endpoints und statische Seiten sind offen. Darunter u.a.:
|
|||
|
|
|
|||
|
|
- **Seiten:** `/`, `/antrag/{ds}`, `/classic`, `/auswertungen`, `/methodik`, `/quellen`, `/impressum`, `/datenschutz`, `/health`, `/v2/{merkliste,tags,cluster,neu,batch}`
|
|||
|
|
- **API-Listen:** `/api/assessments`, `/api/assessment`, `/api/clusters`, `/api/bundeslaender`, `/api/programme`, `/api/search`, `/api/search-landtag`, `/api/feed.xml`, `/api/wahlprogramm-cite`
|
|||
|
|
- **Auswertungen:** `/api/auswertungen/{matrix,zeitreihe,themen-matrix,export.csv,export.json}`
|
|||
|
|
- **Auth-Flow:** `/api/auth/login`, `/api/auth/register`, `/api/auth/callback`, `/api/auth/login-url`, `/api/auth/refresh`, `/unsubscribe/{sub}/{token}`
|
|||
|
|
- **Jobs:** `/api/analyze-drucksache`-Ergebnisse via `/status/{job_id}`, `/result/{job_id}`, `/result/{job_id}/pdf`, `/api/queue/status`
|
|||
|
|
|
|||
|
|
Das heißt: **Lesen und Navigieren braucht keinen Account**. Erst Aktionen (Merken, Kommentieren, Bewerten, neue Analyse starten) erfordern Login.
|
|||
|
|
|
|||
|
|
## UI-Sichtbarkeit — was sieht wer
|
|||
|
|
|
|||
|
|
### v2-Frontend (`/`, `/antrag/*`, `/v2/*`)
|
|||
|
|
|
|||
|
|
| UI-Element | Gast | Registriert | Admin |
|
|||
|
|
|---|:-:|:-:|:-:|
|
|||
|
|
| Sidebar-Gruppe „Lesen" (Durchsuchen / Merkliste / Tags / Cluster) | ✓ | ✓ | ✓ |
|
|||
|
|
| Sidebar-Gruppe „Prüfen" (Neuer Antrag / Batch-Analyse) | ✓ Links | ✓ funktional | ✓ funktional |
|
|||
|
|
| Sidebar-Gruppe „Daten" (Auswertungen / Export / Feed) | ✓ | ✓ | ✓ |
|
|||
|
|
| Sidebar-Gruppe „Administration" (Freischaltungen / Queue / Abos) | — | — | ✓ (via `{% if is_admin %}` in `base.html:61`) |
|
|||
|
|
| Topbar „Klassische Ansicht" + Theme-Toggle | ✓ | ✓ | ✓ |
|
|||
|
|
| `/v2/merkliste` Bookmark-Liste | Login-CTA | eigene Liste | eigene Liste |
|
|||
|
|
| Bookmark-Stern auf Antragsdetail | Login-CTA | ✓ | ✓ |
|
|||
|
|
| Kommentar-Form | Login-CTA | ✓ | ✓ |
|
|||
|
|
| Vote-Buttons | Login-CTA | ✓ | ✓ |
|
|||
|
|
| Re-Analyze-Button | — | ✓ (nach Ablauf) | ✓ jederzeit |
|
|||
|
|
| Delete-Assessment-Button | — | — | ✓ |
|
|||
|
|
|
|||
|
|
### Classic-Frontend (`/classic`)
|
|||
|
|
|
|||
|
|
| UI-Element | Gast | Registriert | Admin |
|
|||
|
|
|---|:-:|:-:|:-:|
|
|||
|
|
| Listenansicht + Detail | ✓ | ✓ | ✓ |
|
|||
|
|
| Hamburger → Anmelden/Registrieren | ✓ (öffnet Modal) | — | — |
|
|||
|
|
| Hamburger → Auswertungen / Quellen / Methodik | ✓ | ✓ | ✓ |
|
|||
|
|
| Merkliste-Tab | ✓ (localStorage, gerätegebunden) | ✓ (synced mit Server) | ✓ |
|
|||
|
|
| Kommentare anlegen | Login-CTA | ✓ | ✓ |
|
|||
|
|
| Admin-Tab | — | — | ✓ (Freischaltungen, Queue, Batch) |
|
|||
|
|
|
|||
|
|
## Login-/Auth-Flows
|
|||
|
|
|
|||
|
|
Zwei Varianten koexistieren:
|
|||
|
|
|
|||
|
|
### Direct Access Grant (Default in v2 + `/classic`-Modal)
|
|||
|
|
|
|||
|
|
1. User klickt „Anmelden" → Modal öffnet
|
|||
|
|
2. POST `/api/auth/login` mit `username`+`password`
|
|||
|
|
3. Server ruft Keycloak `grant_type=password` gegen Client `gwoe-antragspruefer`
|
|||
|
|
4. Setzt `access_token` (HttpOnly-Cookie) + `rt` (Refresh-Token, Path `/api/auth/refresh`)
|
|||
|
|
5. Modal schließt, UI refreshed via `/api/auth/me`
|
|||
|
|
|
|||
|
|
**Voraussetzung:** Keycloak-Client `gwoe-antragspruefer` hat `directAccessGrantsEnabled: true` (ist gesetzt seit 2026-04-20).
|
|||
|
|
|
|||
|
|
### OIDC Redirect (Fallback)
|
|||
|
|
|
|||
|
|
`/api/auth/login-url` → Keycloak-Login-Seite → `/api/auth/callback` → Cookie gesetzt → Redirect auf `/`.
|
|||
|
|
|
|||
|
|
Wurde mit der Direct-Access-Variante überflüssig, bleibt für Notfall erhalten.
|
|||
|
|
|
|||
|
|
## Admin-Rollen definieren
|
|||
|
|
|
|||
|
|
Im Keycloak-Admin:
|
|||
|
|
|
|||
|
|
1. Realm `collaboration` → **Roles** → Rolle `admin` anlegen (oder `gwoe-admin`)
|
|||
|
|
2. User → dem jeweiligen Nutzer die Rolle zuweisen
|
|||
|
|
3. Code prüft in `auth.py:220`: `"admin" in roles or "gwoe-admin" in roles`
|
|||
|
|
|
|||
|
|
Kein Fein-Granular-Rollen-Modell vorgesehen — `admin` ist alles-oder-nichts.
|
|||
|
|
|
|||
|
|
## Dev-Bypass
|
|||
|
|
|
|||
|
|
In `.env`: `AUTH_ENABLED=false` setzt alle Guards auf Bypass. `require_auth` gibt `Dev-Modus`-User zurück (`sub=anonymous`, `roles=[]`), `require_admin` gibt `roles=["admin"]`. Nur für lokale Entwicklung ohne Keycloak-Stack. In Prod immer auf `true`.
|
|||
|
|
|
|||
|
|
## Änderung gegenüber alter Doku
|
|||
|
|
|
|||
|
|
`docs/reference/api.md` hatte eine stale „Auth"-Spalte mit „Keycloak (geplant)". Diese Doku ersetzt sie inhaltlich — das api.md könnte zusammengelegt oder auf diese Seite verlinken.
|
|||
|
|
|
|||
|
|
## Wartungshinweis
|
|||
|
|
|
|||
|
|
Diese Doku wird nicht automatisch generiert. Bei neuen Routes / Guards manuell nachpflegen oder via `docs/reference/scan-zugriffsrechte.py` (TODO: hat bisher keiner geschrieben) regenerieren.
|