gwoe-antragspruefer/docs/reference/zugriffsrechte.md

141 lines
6.6 KiB
Markdown
Raw Permalink Normal View History

# 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.