From a9c57caaf14baf5f6922942b853a0b4dc5878af5 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 28 Dec 2025 13:20:59 +0100 Subject: [PATCH 1/5] docs: add oidc validation runbook --- CHANGELOG.md | 2 + README.md | 33 +++++++++++++++- docs/oidc-validation.md | 38 +++++++++++++++++++ project-management/PROJECT_STATUS.md | 3 ++ .../requirements/epics/EPIC_000003.md | 3 +- .../requirements/stories/US_000025.md | 19 ++++++++++ .../requirements/tasks/TASK_000025.md | 20 ++++++++++ .../requirements/tasks/TASK_000026.md | 20 ++++++++++ 8 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 docs/oidc-validation.md create mode 100644 project-management/requirements/stories/US_000025.md create mode 100644 project-management/requirements/tasks/TASK_000025.md create mode 100644 project-management/requirements/tasks/TASK_000026.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f17345e..1c6b838 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ By: Codex (GPT-5) | 28.12.2025 | πŸ—οΈ Planning | ID: EPIC_000007 und US_000020-US_000023 dokumentiert. By: Codex (GPT-5) | | 28.12.2025 | πŸ—οΈ Planning | ID: US_000024 dokumentiert; OIDC- und Login-Stories praezisiert. By: Codex (GPT-5) | | 28.12.2025 | πŸ—οΈ Planning | ID: US_000024 Theme-Assets unter assets/design vorbereitet. By: Codex (GPT-5) | +| 28.12.2025 | πŸ—οΈ Planning | ID: US_000025 und TASK_000025-TASK_000026 fuer OIDC-Validierung gestartet. By: Codex (GPT-5) | +| 28.12.2025 | πŸ“ Req | ID: US_000025 Runbook/Validierungsschritte dokumentiert. By: Codex (GPT-5) | --- ## Legende diff --git a/README.md b/README.md index be19f2f..c5517c0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ cd /opt/sk ./scripts/install.sh sudo systemctl status skd.service ``` -Then open `http://localhost:8000/` and set the API token in the UI. +Then open `http://localhost:8000/` and log in via PAM (default) to start quickly. ## Configuration Set in `/etc/skd/env` (see `env.example`): @@ -34,6 +34,31 @@ Set in `/etc/skd/env` (see `env.example`): Notes: - `./scripts/install.sh` will create `/etc/skd/env` from `env.example` if missing (edit afterwards) and ensure the `skd` service user/group exist. +## OIDC Setup +OIDC ist optional. Wenn der Provider noch nicht bereit ist, bleibe bei `SKD_AUTH_MODE=pam`. +1. Issuer muss der externen URL des Providers entsprechen (TLS trust erforderlich). +2. OIDC Client registrieren (DCR), z.B.: +```bash +export SKD_OIDC_ISSUER="https://auth.example.org" +export SKD_OIDC_REDIRECT_URI="https://[:port]/login/oidc/callback" +export OIDC_INITIAL_ACCESS_TOKEN="" +./scripts/register_oidc_client.sh +``` +3. Danach in `/etc/skd/env` setzen: +``` +SKD_AUTH_MODE=oidc +SKD_OIDC_ISSUER=... +SKD_OIDC_CLIENT_ID=... +SKD_OIDC_CLIENT_SECRET=... +SKD_OIDC_REDIRECT_URI=... +SKD_OIDC_SCOPES=openid profile email +SKD_SESSION_COOKIE_SECURE=true +``` +Hinweise: +- Redirect-URI muss exakt sein (keine Wildcards). +- Bei Host/Port-Aenderung neu registrieren und neue Credentials setzen. +- Allowlist fuer OIDC: `SKD_AUTH_ALLOWED_USERS` prueft `preferred_username`, `email` oder `sub`. + ## Running - Service: managed by systemd; `./scripts/install.sh` writes the unit dynamically to `/etc/systemd/system/skd.service` with the current repo path and restarts it. - Manual run: `./scripts/run.sh` (uses `.venv`, defaults to `0.0.0.0:8000`). @@ -41,6 +66,12 @@ Notes: - Login (OIDC): open `http://localhost:8000/login/oidc/start` β†’ provider β†’ redirected back with session cookie set. - Health: `curl -H "Authorization: Bearer " http://localhost:8000/health` +## OIDC Validation & Fallbacks +- Validierungsschritte: `docs/oidc-validation.md` (State, Token-Exchange, Claims, Cookie). +- Falls Discovery/JWKS nicht verfuegbar: OIDC deaktivieren und PAM nutzen. +- Falls DCR nicht verfuegbar: Client manuell im IdP anlegen und `SKD_OIDC_*` setzen. +- Bei Self-Signed TLS: CA im System trusten oder in Dev PAM nutzen. + ## API (Bearer token via `/login`) - `GET /users` β†’ `[{user, logged_in}]` (manageable system users; excludes root) - `POST /users/{name}/disable` with JSON `{countdown?, sound?, message?}` diff --git a/docs/oidc-validation.md b/docs/oidc-validation.md new file mode 100644 index 0000000..a9f323f --- /dev/null +++ b/docs/oidc-validation.md @@ -0,0 +1,38 @@ +ID: DOC_000003 | Version: 0.1.0 | Status: Draft +By: Codex (GPT-5) + +# OIDC End-to-End Validation (Kiddo) + +## Purpose +Validiere den OIDC-Login-Flow gegen einen realen oder Stub-Provider und dokumentiere Ergebnisse. + +## Preconditions +- Kiddo laeuft und ist erreichbar (z.B. `http://localhost:8000`). +- OIDC Provider oder Stub erreichbar. +- `SKD_AUTH_MODE=oidc` und `SKD_OIDC_*` gesetzt. +- Redirect-URI: `https://[:port]/login/oidc/callback` ist registriert. + +## Validation Steps +1. OIDC Start + - Aufruf: `GET /login/oidc/start` + - Erwartet: Redirect zum Provider, State-Cookie gesetzt. +2. Callback + - Provider ruft `GET /login/oidc/callback?code=...&state=...` auf. + - Erwartet: State valid, Token-Exchange erfolgreich, Session-Cookie gesetzt, Redirect `/`. +3. Session Check + - Aufruf: `GET /me` mit Cookie oder Bearer. + - Erwartet: `user` und `auth_mode=oidc`. +4. Allowlist + - Falls `SKD_AUTH_ALLOWED_USERS` gesetzt: nicht erlaubte User werden mit 403 abgewiesen. + +## Results +- Datum: +- Provider: +- Host/Redirect: +- Ergebnis: +- Fehlerbilder: + +## Fallbacks bei unvollstaendigem IdP +- Discovery/JWKS fehlt: OIDC deaktivieren und PAM nutzen. +- DCR fehlt: Client manuell im IdP anlegen und `SKD_OIDC_*` setzen. +- TLS-Probleme: CA trusten oder PAM fuer Dev nutzen. diff --git a/project-management/PROJECT_STATUS.md b/project-management/PROJECT_STATUS.md index 5377483..17fcd7c 100644 --- a/project-management/PROJECT_STATUS.md +++ b/project-management/PROJECT_STATUS.md @@ -45,6 +45,9 @@ Sicheres, remote steuerbares System zum Sperren/Entsperren lokaler Nutzerkonten. - [x] TASK_000008: OIDC auth callback - [x] US_000009: Autorisierung und /me-Identitaet - [x] TASK_000009: Authorization /me gate +- [ ] US_000025: OIDC End-to-End Validierung und Runbook +- [ ] TASK_000025: OIDC E2E validation +- [ ] TASK_000026: OIDC runbook update ### EPIC_000004: Web UI - [x] US_000010: Index-Seite ausliefern diff --git a/project-management/requirements/epics/EPIC_000003.md b/project-management/requirements/epics/EPIC_000003.md index e3802ef..c92f9fe 100644 --- a/project-management/requirements/epics/EPIC_000003.md +++ b/project-management/requirements/epics/EPIC_000003.md @@ -34,7 +34,8 @@ Stelle einen sicheren Admin-Login bereit, der Token oder Session-Cookies ausstel - Abhaengigkeit von PAM und OIDC-Provider-Verfuegbarkeit. - Cookie-Sicherheit muss korrekt konfiguriert sein. -## Zugeordnete User Stories (Done) +## Zugeordnete User Stories - US_000007: PAM-Login mit Token - US_000008: OIDC-Login Flow - US_000009: Autorisierung und /me-Identitaet +- US_000025: OIDC End-to-End Validierung und Runbook diff --git a/project-management/requirements/stories/US_000025.md b/project-management/requirements/stories/US_000025.md new file mode 100644 index 0000000..3304951 --- /dev/null +++ b/project-management/requirements/stories/US_000025.md @@ -0,0 +1,19 @@ +ID: US_000025 | Version: 0.1.0 | Status: Draft +By: Codex (GPT-5) + +# US_000025: OIDC End-to-End Validierung und Runbook + +Status: Draft + +Als Betreiber moechte ich den OIDC-Login Ende-zu-Ende validieren und dokumentieren, damit der Betrieb auch mit einem noch nicht vollstaendigen IdP planbar ist. + +## Akzeptanzkriterien +- Given ein erreichbarer OIDC-Provider oder ein Stub/Test-Provider +- When der OIDC-Flow ueber `/login/oidc/start` und `/login/oidc/callback` durchlaufen wird +- Then Login, Token-Validierung und Session-Cookie werden erfolgreich nachvollzogen +- And die erforderlichen ENV-Variablen und Redirect-URIs sind im Runbook dokumentiert +- And Fallback-Optionen fuer nicht verfuegbare OIDC-Funktionen sind beschrieben + +## Task-Platzhalter +- TASK_000025: OIDC E2E validation (Details bei Story-Start) +- TASK_000026: OIDC runbook update (Details bei Story-Start) diff --git a/project-management/requirements/tasks/TASK_000025.md b/project-management/requirements/tasks/TASK_000025.md new file mode 100644 index 0000000..7fef59f --- /dev/null +++ b/project-management/requirements/tasks/TASK_000025.md @@ -0,0 +1,20 @@ +ID: TASK_000025 | Version: 0.1.0 | Status: Draft +By: Codex (GPT-5) + +# TASK_000025: OIDC E2E validation + +## Outcome +OIDC-Login Flow ist Ende-zu-Ende gegen einen realen oder Stub-Provider verifiziert. + +## Story-Bezug +US_000025 + +## Beschreibung +- Teste `/login/oidc/start` und `/login/oidc/callback` mit einem Provider (oder Stub). +- Validierung: State, Token-Exchange, Claims, Allowlist, Session-Cookie. +- Dokumentiere Abweichungen bei nicht verfuegbaren IdP-Funktionen. +- Testplan liegt in `docs/oidc-validation.md`. + +## Definition of Done (DoD) +- Testschritte sind dokumentiert. +- Ergebnisse inklusive Fehlerbilder sind festgehalten. diff --git a/project-management/requirements/tasks/TASK_000026.md b/project-management/requirements/tasks/TASK_000026.md new file mode 100644 index 0000000..d670218 --- /dev/null +++ b/project-management/requirements/tasks/TASK_000026.md @@ -0,0 +1,20 @@ +ID: TASK_000026 | Version: 0.1.0 | Status: Draft +By: Codex (GPT-5) + +# TASK_000026: OIDC runbook update + +## Outcome +Runbook beschreibt OIDC-Setup, Redirect-URIs und Fallbacks fuer einen nicht vollstaendigen IdP. + +## Story-Bezug +US_000025 + +## Beschreibung +- Ergaenze README mit klaren OIDC-ENV-Beispielen. +- Beschreibe exakte Redirect-URI und Host/Port-Wechsel. +- Fuege Fallbacks hinzu (PAM-Login, Hinweise zu fehlenden IdP-Features). +- Verweise auf `docs/oidc-validation.md` fuer die Testschritte. + +## Definition of Done (DoD) +- README enthaelt konkrete OIDC-Setup-Schritte. +- Fallbacks sind fuer Operatoren nachvollziehbar. From 98d51655baf162c88eb60fb527d9f76cb7597fe2 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 28 Dec 2025 13:32:08 +0100 Subject: [PATCH 2/5] docs: record oidc validation blocked --- CHANGELOG.md | 1 + docs/oidc-validation.md | 10 +++++----- project-management/PROJECT_STATUS.md | 1 + project-management/requirements/stories/US_000025.md | 2 +- project-management/requirements/tasks/TASK_000025.md | 2 +- project-management/requirements/tasks/TASK_000026.md | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c6b838..f157703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ By: Codex (GPT-5) | 28.12.2025 | πŸ—οΈ Planning | ID: US_000024 Theme-Assets unter assets/design vorbereitet. By: Codex (GPT-5) | | 28.12.2025 | πŸ—οΈ Planning | ID: US_000025 und TASK_000025-TASK_000026 fuer OIDC-Validierung gestartet. By: Codex (GPT-5) | | 28.12.2025 | πŸ“ Req | ID: US_000025 Runbook/Validierungsschritte dokumentiert. By: Codex (GPT-5) | +| 28.12.2025 | πŸ“ Req | ID: US_000025 Validation blocked (Service/IdP nicht bereit). By: Codex (GPT-5) | --- ## Legende diff --git a/docs/oidc-validation.md b/docs/oidc-validation.md index a9f323f..98297c6 100644 --- a/docs/oidc-validation.md +++ b/docs/oidc-validation.md @@ -26,11 +26,11 @@ Validiere den OIDC-Login-Flow gegen einen realen oder Stub-Provider und dokument - Falls `SKD_AUTH_ALLOWED_USERS` gesetzt: nicht erlaubte User werden mit 403 abgewiesen. ## Results -- Datum: -- Provider: -- Host/Redirect: -- Ergebnis: -- Fehlerbilder: +- Datum: 28.12.2025 +- Provider: nicht konfiguriert (IdP noch nicht bereit) +- Host/Redirect: n/a +- Ergebnis: Blocked (Service nicht erreichbar unter http://localhost:8000/health) +- Fehlerbilder: curl (7) Couldn't connect to server ## Fallbacks bei unvollstaendigem IdP - Discovery/JWKS fehlt: OIDC deaktivieren und PAM nutzen. diff --git a/project-management/PROJECT_STATUS.md b/project-management/PROJECT_STATUS.md index 17fcd7c..4d17259 100644 --- a/project-management/PROJECT_STATUS.md +++ b/project-management/PROJECT_STATUS.md @@ -87,6 +87,7 @@ Sicheres, remote steuerbares System zum Sperren/Entsperren lokaler Nutzerkonten. ## Offene Risiken / Abhaengigkeiten - Betrieb erfordert Root/sudo und lokale System-Tools (notify-send, sound player, uvicorn). +- OIDC-Validierung blockiert bis IdP bereit und Service laeuft. ## Naechste Schritte - Anforderungen beim naechsten Feature-Start erweitern. diff --git a/project-management/requirements/stories/US_000025.md b/project-management/requirements/stories/US_000025.md index 3304951..ed5574e 100644 --- a/project-management/requirements/stories/US_000025.md +++ b/project-management/requirements/stories/US_000025.md @@ -3,7 +3,7 @@ By: Codex (GPT-5) # US_000025: OIDC End-to-End Validierung und Runbook -Status: Draft +Status: Blocked Als Betreiber moechte ich den OIDC-Login Ende-zu-Ende validieren und dokumentieren, damit der Betrieb auch mit einem noch nicht vollstaendigen IdP planbar ist. diff --git a/project-management/requirements/tasks/TASK_000025.md b/project-management/requirements/tasks/TASK_000025.md index 7fef59f..6f1ee08 100644 --- a/project-management/requirements/tasks/TASK_000025.md +++ b/project-management/requirements/tasks/TASK_000025.md @@ -1,4 +1,4 @@ -ID: TASK_000025 | Version: 0.1.0 | Status: Draft +ID: TASK_000025 | Version: 0.1.0 | Status: Blocked By: Codex (GPT-5) # TASK_000025: OIDC E2E validation diff --git a/project-management/requirements/tasks/TASK_000026.md b/project-management/requirements/tasks/TASK_000026.md index d670218..780049d 100644 --- a/project-management/requirements/tasks/TASK_000026.md +++ b/project-management/requirements/tasks/TASK_000026.md @@ -1,4 +1,4 @@ -ID: TASK_000026 | Version: 0.1.0 | Status: Draft +ID: TASK_000026 | Version: 0.1.0 | Status: Blocked By: Codex (GPT-5) # TASK_000026: OIDC runbook update From 4809027227c304737842dfd9f1709cce26d0d59a Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 28 Dec 2025 13:37:43 +0100 Subject: [PATCH 3/5] docs: note oidc pr preparation --- CHANGELOG.md | 1 + project-management/PROJECT_STATUS.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f157703..be5be33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ By: Codex (GPT-5) | 28.12.2025 | πŸ—οΈ Planning | ID: US_000025 und TASK_000025-TASK_000026 fuer OIDC-Validierung gestartet. By: Codex (GPT-5) | | 28.12.2025 | πŸ“ Req | ID: US_000025 Runbook/Validierungsschritte dokumentiert. By: Codex (GPT-5) | | 28.12.2025 | πŸ“ Req | ID: US_000025 Validation blocked (Service/IdP nicht bereit). By: Codex (GPT-5) | +| 28.12.2025 | πŸ—οΈ Planning | ID: PR-Vorbereitung fuer feature/oidc-validation geplant. By: Codex (GPT-5) | --- ## Legende diff --git a/project-management/PROJECT_STATUS.md b/project-management/PROJECT_STATUS.md index 4d17259..57eb03c 100644 --- a/project-management/PROJECT_STATUS.md +++ b/project-management/PROJECT_STATUS.md @@ -91,4 +91,5 @@ Sicheres, remote steuerbares System zum Sperren/Entsperren lokaler Nutzerkonten. ## Naechste Schritte - Anforderungen beim naechsten Feature-Start erweitern. -- Tests fuer kritische Pfade evaluieren. +- OIDC-Validierung abschliessen und US_000025 auf Done setzen. +- PR vorbereiten: feature/oidc-validation (Summary, Risiko, Testschritte). From 97ea7070ccb2dc9458c912176c042312a4c84ab7 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 28 Dec 2025 13:49:22 +0100 Subject: [PATCH 4/5] docs: enforce pam always available --- CHANGELOG.md | 1 + README.md | 2 +- env.example | 3 ++- project-management/requirements/stories/US_000007.md | 1 + project-management/requirements/stories/US_000008.md | 3 ++- project-management/requirements/stories/US_000021.md | 2 +- project-management/requirements/stories/US_000022.md | 1 + 7 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be5be33..cd6029e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ By: Codex (GPT-5) | 28.12.2025 | πŸ“ Req | ID: US_000025 Runbook/Validierungsschritte dokumentiert. By: Codex (GPT-5) | | 28.12.2025 | πŸ“ Req | ID: US_000025 Validation blocked (Service/IdP nicht bereit). By: Codex (GPT-5) | | 28.12.2025 | πŸ—οΈ Planning | ID: PR-Vorbereitung fuer feature/oidc-validation geplant. By: Codex (GPT-5) | +| 28.12.2025 | πŸ—οΈ Planning | ID: PAM immer aktiv; OIDC optional mit deaktivierter UI-Option dokumentiert. By: Codex (GPT-5) | --- ## Legende diff --git a/README.md b/README.md index c5517c0..be0f1cf 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Then open `http://localhost:8000/` and log in via PAM (default) to start quickly ## Configuration Set in `/etc/skd/env` (see `env.example`): -- `SKD_AUTH_MODE`: `pam` (default) or `oidc`. +- PAM-Login ist immer aktiv. OIDC wird zusaetzlich angeboten, wenn konfiguriert. - `SKD_AUTH_SECRET`: HMAC secret for bearer tokens/cookies (set a strong value). - `SKD_TOKEN_TTL_SECONDS`: token lifetime (default 900s). - `SKD_AUTH_ALLOWED_USERS`: optional comma list of accounts allowed to log in (used for PAM and as an allowlist for OIDC claims). diff --git a/env.example b/env.example index bacefa3..5abcb2c 100644 --- a/env.example +++ b/env.example @@ -3,7 +3,8 @@ SKD_ALLOWED_USERS=child1,child2 SKD_AUTH_SECRET=change-me-secret SKD_TOKEN_TTL_SECONDS=900 -# Auth mode: pam (default) or oidc +# PAM ist immer aktiv; OIDC wird zusaetzlich angeboten, wenn konfiguriert. +# SKD_AUTH_MODE bleibt optional und wird derzeit nicht erzwungen. SKD_AUTH_MODE=pam SKD_AUTH_ALLOWED_USERS= SKD_AUTH_ALLOWED_GROUPS=sudo diff --git a/project-management/requirements/stories/US_000007.md b/project-management/requirements/stories/US_000007.md index 62860cb..415f677 100644 --- a/project-management/requirements/stories/US_000007.md +++ b/project-management/requirements/stories/US_000007.md @@ -13,6 +13,7 @@ Als Admin moechte ich mich per PAM-Login anmelden, damit ich ein Session-Token e - Then die Antwort enthaelt `token` und `expires_in` - And ein Session-Cookie mit dem Token wird gesetzt - And der Login ist ohne OIDC-Konfiguration als Schnellstart moeglich +- And PAM-Login bleibt auch bei aktivem OIDC verfuegbar ## Task-Platzhalter - TASK_000007: PAM login token (Details bei Story-Start) diff --git a/project-management/requirements/stories/US_000008.md b/project-management/requirements/stories/US_000008.md index 41bee96..a71a346 100644 --- a/project-management/requirements/stories/US_000008.md +++ b/project-management/requirements/stories/US_000008.md @@ -8,12 +8,13 @@ Status: Done Als Admin moechte ich mich per OIDC anmelden, damit ich ohne Passwort-Login zugreifen kann. ## Akzeptanzkriterien -- Given `SKD_AUTH_MODE=oidc` und ein erreichbarer OIDC-Provider +- Given OIDC ist konfiguriert und ein erreichbarer OIDC-Provider - When ein GET auf `/login/oidc/start` erfolgt - Then der Nutzer wird zum Provider umgeleitet und ein State-Cookie gesetzt - When der Provider auf `/login/oidc/callback` mit Code und State zurueckleitet - Then der State wird validiert und ein Session-Cookie gesetzt - And bei ungueltigem State erfolgt eine 400-Antwort +- And die OIDC-Option wird deaktiviert, wenn keine OIDC-Konfiguration vorliegt ## Task-Platzhalter - TASK_000008: OIDC auth callback (Details bei Story-Start) diff --git a/project-management/requirements/stories/US_000021.md b/project-management/requirements/stories/US_000021.md index e5e1204..9c8b800 100644 --- a/project-management/requirements/stories/US_000021.md +++ b/project-management/requirements/stories/US_000021.md @@ -10,7 +10,7 @@ Als Operator moechte ich Konfigurationen per ENV setzen, damit Verhalten und Def ## Akzeptanzkriterien - Given Umgebungsvariablen aus `env.example` - When der Service startet -- Then Auth- und Session-Settings werden aus ENV geladen (`SKD_AUTH_MODE`, `SKD_AUTH_SECRET`, `SKD_TOKEN_TTL_SECONDS`, `SKD_AUTH_ALLOWED_USERS`, `SKD_AUTH_ALLOWED_GROUPS`, `SKD_AUTH_PAM_SERVICE`, `SKD_SESSION_COOKIE_NAME`, `SKD_SESSION_COOKIE_SECURE`, `SKD_OIDC_STATE_COOKIE_NAME`) +- Then Auth- und Session-Settings werden aus ENV geladen (`SKD_AUTH_SECRET`, `SKD_TOKEN_TTL_SECONDS`, `SKD_AUTH_ALLOWED_USERS`, `SKD_AUTH_ALLOWED_GROUPS`, `SKD_AUTH_PAM_SERVICE`, `SKD_SESSION_COOKIE_NAME`, `SKD_SESSION_COOKIE_SECURE`, `SKD_OIDC_STATE_COOKIE_NAME`) - And OIDC-Settings werden aus ENV geladen (`SKD_OIDC_ISSUER`, `SKD_OIDC_CLIENT_ID`, `SKD_OIDC_CLIENT_SECRET`, `SKD_OIDC_REDIRECT_URI`, `SKD_OIDC_SCOPES`) - And Allowlist/Defaults werden aus ENV geladen (`SKD_ALLOWED_USERS`, `SKD_DEFAULT_COUNTDOWN`, `SKD_DEFAULT_SOUND`, `SKD_NOTIFY_TIMEOUT`, `SKD_DRY_RUN`) - And Sound/Notify-Pfade sind ueber ENV ueberschreibbar (`SKD_SOUND_PLAYER`, `SKD_SOUND_FILE`, `SKD_NOTIFY_SEND_PATH`) diff --git a/project-management/requirements/stories/US_000022.md b/project-management/requirements/stories/US_000022.md index 77e6326..5056dbb 100644 --- a/project-management/requirements/stories/US_000022.md +++ b/project-management/requirements/stories/US_000022.md @@ -14,6 +14,7 @@ Als Admin moechte ich mich im Web-UI anmelden, Nutzer laden und Aktionen ausfueh - When ich Nutzer lade und eine Aktion sende - Then die Aktionsergebnisse (Steps/Status) werden als Text angezeigt - And Fehlerantworten werden als Text angezeigt +- And die OIDC-Option ist deaktiviert, wenn keine Konfiguration vorliegt ## Task-Platzhalter - TASK_000022: UI login and actions (Details bei Story-Start) From e3802887558443e82fd4cb89c26cab733ae5e4e7 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 28 Dec 2025 13:49:30 +0100 Subject: [PATCH 5/5] code: keep pam enabled and gate oidc --- backend/app.py | 14 ++++++++++---- backend/auth.py | 20 ++++++++++---------- backend/settings.py | 3 +++ backend/templates/index.html | 21 ++++++++++++++++++--- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/backend/app.py b/backend/app.py index e58c4ed..a46f2ce 100644 --- a/backend/app.py +++ b/backend/app.py @@ -29,10 +29,10 @@ templates = Jinja2Templates(directory="backend/templates") def get_oidc_client(settings: Settings = Depends(get_settings)) -> OIDCClient: - if settings.auth_mode != "oidc": + if not settings.oidc_enabled: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - detail="OIDC auth not enabled", + detail="OIDC not configured", ) try: return OIDCClient(settings) @@ -59,7 +59,8 @@ def whoami( current_user: str = Depends(get_current_admin), settings: Settings = Depends(get_settings), ) -> dict: - return {"user": current_user, "auth_mode": settings.auth_mode} + auth_mode = "pam+oidc" if settings.oidc_enabled else "pam" + return {"user": current_user, "auth_mode": auth_mode} @app.post("/login", response_model=LoginResponse) @@ -115,7 +116,7 @@ def oidc_callback( username = oidc.extract_username(claims) if not username: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Missing username claim") - if not is_authorized_admin(username, settings): + if not is_authorized_admin(username, settings, mode="oidc"): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User not authorized to log in") token = issue_token(username, settings) @@ -132,6 +133,11 @@ def oidc_callback( return redirect +@app.get("/login/oidc/status") +def oidc_status(settings: Settings = Depends(get_settings)) -> dict: + return {"enabled": settings.oidc_enabled} + + @app.get("/users", response_model=List[UserStatus], dependencies=[Depends(get_current_admin)]) def users(settings: Settings = Depends(get_settings)) -> List[UserStatus]: logged_in = set(actions.list_logged_in_users()) diff --git a/backend/auth.py b/backend/auth.py index a402bd4..b012197 100644 --- a/backend/auth.py +++ b/backend/auth.py @@ -1,7 +1,7 @@ import datetime as dt import grp import pwd -from typing import List, Set +from typing import List, Set, Optional import jwt import pam @@ -23,8 +23,8 @@ def _is_member_of(username: str, groups: Set[str]) -> bool: return bool(user_groups & groups) -def is_authorized_admin(username: str, settings: Settings) -> bool: - if settings.auth_mode == "oidc": +def is_authorized_admin(username: str, settings: Settings, mode: Optional[str] = None) -> bool: + if mode == "oidc": allowed_users = set(settings.auth_allowed_users) if allowed_users and username not in allowed_users: return False @@ -46,12 +46,7 @@ def is_authorized_admin(username: str, settings: Settings) -> bool: def authenticate_admin_user(username: str, password: str, settings: Settings) -> None: - if settings.auth_mode != "pam": - raise HTTPException( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - detail="Password login disabled; OIDC is configured", - ) - if not is_authorized_admin(username, settings): + if not is_authorized_admin(username, settings, mode="pam"): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="User not authorized to log in", @@ -82,7 +77,12 @@ def decode_token(token: str, settings: Settings) -> str: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") from exc username = payload.get("sub") - if not username or not is_authorized_admin(username, settings): + if not username: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") + if not ( + is_authorized_admin(username, settings, mode="pam") + or is_authorized_admin(username, settings, mode="oidc") + ): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Unauthorized user") return username diff --git a/backend/settings.py b/backend/settings.py index 4a7ebbf..e4b1165 100644 --- a/backend/settings.py +++ b/backend/settings.py @@ -32,6 +32,9 @@ class Settings: self.oidc_state_cookie_name: str = os.getenv( "SKD_OIDC_STATE_COOKIE_NAME", "skd_oidc_state" ) + self.oidc_enabled: bool = bool( + self.oidc_issuer and self.oidc_client_id and self.oidc_client_secret + ) self.default_countdown: int = int(os.getenv("SKD_DEFAULT_COUNTDOWN", "60")) self.default_sound: bool = os.getenv("SKD_DEFAULT_SOUND", "false").lower() == "true" self.notify_timeout: int = int(os.getenv("SKD_NOTIFY_TIMEOUT", "5")) diff --git a/backend/templates/index.html b/backend/templates/index.html index c7011dc..257941c 100644 --- a/backend/templates/index.html +++ b/backend/templates/index.html @@ -18,11 +18,11 @@
-

Login (nur Root-User)

+

Login

Bevorzugt OIDC nutzen, falls konfiguriert. Die Anmeldung ΓΆffnet den Identity Provider und setzt eine Session-Cookie.


-

Lokale Anmeldung (PAM) nur falls OIDC nicht verfΓΌgbar:

+

Lokale Anmeldung (PAM) ist immer moeglich:

@@ -84,7 +84,7 @@
-