Files
Florian WildeandClaude Opus 5 0158d0292f
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
feat(selbstauskunft): Mitarbeiter sehen ihre eigenen Geraete (#55)
„Was ist eigentlich mit meinem Notebook passiert?“ musste bisher ein
Techniker nachsehen. Ein Mitarbeiter kann die Arbeiten an **seinen**
Geraeten jetzt selbst ansehen — mit der fuenften Rolle MITARBEITER,
zugeordnet beim Anmelden ueber den UPN.

Was er sieht, sind Titel, Zeitraum und Status. Keine Schritte, keine
Notizen, keine Kommentare, keine Anhaenge: in einem Lauf stehen
Arbeitsnotizen ueber Menschen und Befehle mit Platzhaltern fuer
Zugangsdaten. Eine Selbstauskunft ist keine Akteneinsicht, und das haelt
nicht die Seite fest, sondern das Lesemodell — eine Seite, die filtert,
ist eine Seite, die es vergessen kann.

`selbstauskunft` ist ein **Ersatz** fuer `lesen` und kein Zusatz; auch
ein ADMIN hat es nicht. Ein zweiter Weg zu denselben Daten waere eine
zweite Stelle, an der jemand die Einschraenkung auf die eigene Person
vergisst. Die Datenbank verlangt das sogar: ein CHECK haelt fest, dass
Rolle und Zuordnung zusammengehoeren, in beide Richtungen.

Ein mehrdeutiger UPN wird abgewiesen und protokolliert. Zwei Kunden
koennen dieselbe Person fuehren; ein findFirst entschiede nach
Sortierung darueber, wessen Geraete jemand sieht.

**Verhaltensaenderung:** wer sich anmeldet und in keiner Rollengruppe
steht, bekommt nicht mehr automatisch LESER — also nicht mehr
Lesezugriff auf alle Laeufe des Hauses. Das greift nur mit
konfigurierten OIDC_GROUP_*; ohne sie bleibt alles wie vorher, denn ohne
abgebildete Gruppen laesst sich Techniker nicht von Mitarbeiter
unterscheiden.

Nebenbei behoben: `staerksteRolle` benutzte LESER als Startwert und
damit als Untergrenze — mit einer Rolle darunter lieferte sie mehr
Rechte als uebergeben. Gefunden von einem Test, der seit #007
unveraendert dasteht.

Closes #55

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-06 07:32:54 +02:00

7.0 KiB

Anmeldung über authentik (OIDC)

Laufzettel spricht generisches OpenID Connect mit Discovery. Der Anbieter muss also nur einen /.well-known/openid-configuration-Endpunkt anbieten — authentik tut das. Ein anderer Anbieter (Keycloak, Entra ID, Zitadel) funktioniert genauso, nur heißen die Menüs anders.

1. Provider in authentik anlegen

Anwendungen → Provider → Erstellen → OAuth2/OpenID Provider

Feld Wert
Name Laufzettel
Authorization flow default-provider-authorization-explicit-consent (oder implicit)
Client type Confidential
Redirect URIs https://laufzettel.example.de/api/auth/oidc/rueckruf
Signing Key ein vorhandener Schlüssel
Scopes openid, profile, email und ein Scope, der groups liefert

Die Redirect-URI ist APP_URL plus /api/auth/oidc/rueckruf — exakt so, mit demselben Schema und ohne abschließenden Schrägstrich. Läuft Laufzettel hinter einem Reverse Proxy, ist APP_URL die öffentliche Adresse, nicht die interne.

Client ID und Client Secret notieren.

2. Gruppen in den Token bringen

Laufzettel liest die Rolle aus dem Claim groups. In authentik gibt es dafür den mitgelieferten Scope-Mapping authentik default OAuth Mapping: OpenID 'profile' nicht — du brauchst ein eigenes Mapping:

Anpassung → Property Mappings → Erstellen → Scope Mapping

  • Name: Laufzettel Gruppen
  • Scope name: groups
  • Expression:
return {
    "groups": [gruppe.name for gruppe in request.user.ak_groups.all()],
}

Dieses Mapping beim Provider unter Advanced protocol settings → Scopes auswählen.

3. Anwendung anlegen

Anwendungen → Anwendungen → Erstellen, den Provider zuordnen, Slug z. B. laufzettel. Über die Anwendung steuerst du per Policy, wer sich überhaupt anmelden darf.

4. Gruppen für die Rollen

Lege in authentik vier Gruppen an und trage die Mitglieder ein:

Gruppe Rolle in Laufzettel Darf
lz-admin ADMIN alles, inkl. Benutzer, Verzeichnis, Abteilungen, Archiv-Verwaltung
lz-redakteur REDAKTEUR Vorlagen anlegen, bearbeiten, veröffentlichen
lz-techniker TECHNIKER Läufe starten, abarbeiten, abschließen, archivieren; Geräte pflegen
lz-leser LESER nur lesen
(keine davon) MITARBEITER nur die eigenen Geräte, siehe unten

Ist jemand in mehreren Gruppen, gewinnt die stärkere Rolle.

Die fünfte Zeile hat keine Gruppe, und das ist Absicht. Wer sich anmeldet und in keiner der vier Gruppen steht, ist für Laufzettel ein Mitarbeiter des Hauses und bekommt die Selbstauskunft — vorausgesetzt, er lässt sich einer Person im Verzeichnis zuordnen. Eine eigene Gruppe dafür anzulegen hieße, jeden Mitarbeiter einzeln einzutragen; „alle übrigen“ ist genau die Menge, die gemeint ist.

5. Laufzettel konfigurieren

In der .env:

APP_URL=https://laufzettel.example.de

OIDC_ISSUER=https://authentik.example.de/application/o/laufzettel/
OIDC_CLIENT_ID=...
OIDC_CLIENT_SECRET=...
OIDC_NAME=authentik

OIDC_GROUP_ADMIN=lz-admin
OIDC_GROUP_EDITOR=lz-redakteur
OIDC_GROUP_TECHNICIAN=lz-techniker
OIDC_GROUP_VIEWER=lz-leser

Den OIDC_ISSUER zeigt authentik beim Provider unter OpenID Configuration Issuer an. Der abschließende Schrägstrich gehört dazu.

Danach neu starten:

docker compose up -d

6. Was beim Login passiert

  • Ein unbekannter Benutzer wird beim ersten Login angelegt (JIT-Provisioning)
  • Die Rolle wird bei jedem Login aus groups neu gesetzt — eine Änderung in authentik greift ab der nächsten Anmeldung
  • Ist der Benutzer in keiner der vier Gruppen, versucht Laufzettel, ihn über seinen UPN einer Person im Verzeichnis zuzuordnen. Klappt das, bekommt er MITARBEITER und sieht nur seine eigenen Geräte; klappt es nicht, wird die Anmeldung abgewiesen — mit einer Begründung auf der Anmeldeseite
  • Bis Version 0.2.0 bekam er stattdessen LESER, also Lesezugriff auf alle Läufe des Hauses. Wer das Verhalten von damals braucht, trägt die betreffenden Leute in lz-leser ein
  • Wer vor dem Login eine bestimmte Seite aufgerufen hat (z. B. /t/LZ-7K3F9Q nach dem Scannen eines QR-Codes), landet danach genau dort

7. Selbstauskunft für Mitarbeiter

Ein Mitarbeiter, der in keiner Rollengruppe steht, sieht unter Meine Geräte die Arbeiten an seinen eigenen Geräten — Titel, Zeitraum, Status. Keine Schritte, keine Notizen, keine Kommentare, keine Anhänge; warum, steht in docs/architektur.md unter „Warum ein Mitarbeiter keine Akteneinsicht bekommt“.

Zugeordnet wird über den UPN. Laufzettel liest ihn in dieser Reihenfolge:

  1. der Anspruch upn (so heißt er bei Entra ID)
  2. preferred_username (authentik und Keycloak schicken dort den Anmeldenamen)
  3. die Mailadresse

Verglichen wird mit Mitarbeiter.upn, unabhängig von Groß- und Kleinschreibung. Damit das greift, muss der UPN in authentik derselbe sein wie der im Active Directory — bei einem AD als Quelle von authentik ist er das meist von selbst.

Zwei Fälle, die bewusst nicht durchgehen:

Fall Was passiert
Zu dem UPN gibt es keine aktive Person Anmeldung abgewiesen, mit Begründung auf der Anmeldeseite
Zu dem UPN gibt es mehr als eine Person Anmeldung abgewiesen und im Protokoll festgehalten

Der zweite Fall kommt im Modus Dienstleistung vor, wenn zwei Kunden dieselbe Person führen. Ein Zufallstreffer wäre dort schlimmer als kein Zugang: er entschiede nach Sortierung darüber, wessen Geräte jemand sieht.

Abschalten lässt sich das Ganze, indem du keine Rollengruppen konfigurierst — dann greift die Weiche nicht, und alles bleibt wie vor 0.2.0. Oder indem du jeden, der hinein soll, in lz-leser einträgst.

8. Notfallzugang

Fällt authentik aus, kommt niemand mehr in Laufzettel. Für diesen Fall gibt es den lokalen Login:

AUTH_LOCAL_ENABLED=true

Er ist standardmäßig aus. Ein lokales Admin-Konto legst du über den Seed an (SEED_ADMIN_EMAIL, SEED_ADMIN_PASSWORD) oder als Admin unter Admin → Benutzer. Die Passwörter liegen als argon2-Hash in der Datenbank.

Empfehlung: ein einziges lokales Admin-Konto, Passwort im Passwortmanager, AUTH_LOCAL_ENABLED im Normalbetrieb auf false und nur im Notfall umgestellt.

9. Fehlersuche

Symptom Ursache
redirect_uri_mismatch Die URI in authentik stimmt nicht zeichengenau mit APP_URL + /api/auth/oidc/rueckruf überein — oft http gegen https oder ein abschließender Schrägstrich
Nach dem Login wieder auf der Anmeldeseite Das Cookie ist Secure, die Verbindung aber http. Entweder HTTPS nutzen oder zum Testen über localhost gehen
Jeder bekommt LESER Der Claim groups fehlt im Token. Scope-Mapping aus Schritt 2 angelegt und beim Provider ausgewählt?
Discovery schlägt fehl OIDC_ISSUER falsch oder von der App aus nicht erreichbar. Im Compose-Stand hängt web im Netz extern — prüfen, ob der Name auflösbar ist