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