feat(auth): Rollen, Benutzerverwaltung und Passkeys (#027, #028)

Drei Rollen: Administrator, Techniker, Kunde. Jede der 28 Aktionen nennt das
Recht, das sie verlangt; die Rechte stehen an einer Stelle. Kundenkonten
haengen an genau einem Kunden und sehen nur dessen Hosts und Rechnungen -
ohne Einkaufspreise und Marge. Die Einschraenkung sitzt in den Abfragen
selbst, damit keine Seite vergessen kann, sie weiterzugeben; sechs Tests
pruefen das.

Dabei gefunden: Neu ueber OIDC angelegte Konten haetten die Vorgabe des
Datenmodells geerbt - ADMIN. Jetzt gibt es dafuer eine Einstellung mit der
Vorgabe Techniker.

Passkeys (#028) als gleichwertiger, passwortloser Anmeldeweg neben Passwort
und OIDC. Mehrere je Konto, Aufgabe in einem kurzlebigen Cookie, rpID aus der
oeffentlichen Adresse. Ohne HTTPS sagt die Seite es, statt still zu scheitern.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-19 00:46:29 +02:00
co-authored by Claude Opus 5
parent 7d8e5c9172
commit 2f45e33e20
66 changed files with 2201 additions and 114 deletions
+41
View File
@@ -18,6 +18,7 @@ auch mit Hosts hinter NAT.
8. [Benachrichtigungen einrichten](#8-benachrichtigungen-einrichten)
9. [Exporte](#9-exporte)
9a. [Einstellungen](#9a-einstellungen)
9b. [Benutzer, Rollen und Passkeys](#9b-benutzer-rollen-und-passkeys)
10. [Sicherung und Aktualisierung](#10-sicherung-und-aktualisierung)
11. [Fehlersuche](#11-fehlersuche)
@@ -341,6 +342,46 @@ Zeitzone gilt für PDF, CSV und Benachrichtigungen.
---
## 9b. Benutzer, Rollen und Passkeys
Unter *Einstellungen → Benutzer* legen Administratoren Konten an und vergeben eine von drei Rollen:
![Benutzerverwaltung](bilder/benutzer.png)
| Rolle | Darf |
|---|---|
| **Administrator** | alles, einschließlich Benutzer und Systemeinstellungen |
| **Techniker** | alles Fachliche: Hosts, Kunden, Rechnungen, Tokens, Warnregeln – aber keine Benutzer |
| **Kunde** | nur die Hosts und Rechnungen des eigenen Kunden, **ohne Einkaufspreise und Marge**; darf Notizen schreiben und Warnungen quittieren |
Ein Kundenkonto ist an genau einen Kunden gebunden – das Feld erscheint, sobald die Rolle „Kunde“
gewählt ist. Die Einschränkung sitzt in den Abfragen selbst, nicht in der Anzeige: Was ein Kundenkonto
nicht sehen darf, steht auch nicht im ausgelieferten HTML.
Ein Passwort ist beim Anlegen freiwillig. Ohne Passwort entsteht ein Konto, das sich nur über OIDC oder
einen Passkey anmeldet. **Ein neues Passwort beendet alle laufenden Sitzungen dieses Kontos**, ebenso
das Sperren.
### Passkeys
Ein Passkey ersetzt das Passwort: Der Browser bestätigt die Anmeldung mit Fingerabdruck, Gesicht, PIN
oder einem Sicherheitsschlüssel. Jeder Benutzer verwaltet seine eigenen unter *Meine Passkeys*:
![Passkeys](bilder/passkeys.png)
Zwei Dinge sind wichtig:
1. **Passkeys brauchen HTTPS.** Nur `localhost` ist ausgenommen. Wird das Dashboard über `http://`
im LAN aufgerufen, weist die Seite darauf hin und der Knopf bleibt gesperrt.
2. **Der Passkey hängt an der Adresse.** Wer später von `http://server:3000` auf
`https://proxledger.firma.de` wechselt, muss neu registrieren. Deshalb zuerst den Reverse Proxy
einrichten, dann Passkeys anlegen.
Passwort, OIDC und Passkey stehen gleichberechtigt nebeneinander – der lokale Zugang bleibt als
Rückweg immer nutzbar.
---
## 10. Sicherung und Aktualisierung
**Sichern:**
Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 KiB

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 253 KiB