„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>
166 lines
7.0 KiB
Markdown
166 lines
7.0 KiB
Markdown
# 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:
|
|
|
|
```python
|
|
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`:
|
|
|
|
```dotenv
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```dotenv
|
|
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 |
|