more admin stuff 2

This commit is contained in:
2026-08-20 17:10:19 +02:00
parent e8216b14e9
commit cd5957cbd2
19 changed files with 1120 additions and 78 deletions

112
README.md
View File

@ -186,13 +186,72 @@ Das Entziehen bleibt bewusst pro Zeile/Rolle (`role_name`, Einzahl) — das
entspricht dem bestehenden "Entziehen"-Knopf pro Tabellenzeile und braucht
keine Mehrfachauswahl.
## Phase 9: Host-Key-Bugfix, Sessionview, Live-Verbindungslog, Credentials-RBAC
Sechs zusammenhaengende Punkte aus einer erneuten nummerierten Anfrage, in
derselben Session direkt nach Phase 8 umgesetzt:
**1) Bugfix "Host Key ermitteln" (Fehler 500)**: `discover_and_store_host_key()`
(`app/ssh_proxy/proxy.py`) verband sich zwar erfolgreich fuer den Key-Exchange,
liess aber den danach IMMER folgenden Authentifizierungsfehler (kein
Client-Key fuer die reine Discovery hinterlegt) unbehandelt durchreichen —
das riss als unbehandelte Exception bis zu FastAPI durch und fuehrte praktisch
bei jedem Klick auf "Host-Key ermitteln" zu einem 500. Fix: der waehrend des
Key-Exchange bereits erfasste Fingerprint gilt jetzt trotz des danach
erwarteten Auth-Fehlers als Erfolg; nur ein echter Verbindungsfehler VOR dem
Key-Exchange (Host nicht erreichbar, DNS, Timeout) liefert jetzt sauber 502
statt 500 (neue `HostKeyDiscoveryError`).
**2) Login-Verlauf entfernt**: der in Phase 8 hinzugefuegte Tab "Login-Verlauf"
wurde aus der Admin-Oberflaeche entfernt (Login-/Logout-/Fehlversuch-Ereignisse
bleiben weiterhin vollstaendig im Audit-Log-Tab sichtbar, dort war die
Information ohnehin redundant vorhanden).
**3) Verbindungslog (Live-Tail, inkl. Debug)**: neuer Tab "Verbindungslog" +
WebSocket `GET /admin/ws/logs` streamen die Anwendungslogs dieses
Server-Prozesses live in den Browser — inkl. DEBUG-Detail zum SSH/RDP-
Verbindungsaufbau (`jumphost.*`-Logger werden beim Start auf DEBUG gesetzt,
siehe `app/security/log_stream.py`). Bewusst ein reiner In-Memory-Ring-Buffer
(letzte 1000 Zeilen) + Pub/Sub ohne DB-Persistenz — ein Live-Tail wie
`journalctl -f`, kein durchsuchbares Archiv. Nur fuer Super-Admins sichtbar/
erreichbar, da die Logs mandantenuebergreifend technische Details preisgeben
koennen.
**4) Sessionview-Dashboard (nur Super-Admin)**: neuer Tab "Sessions" +
`GET /admin/sessions` (aktive + historische Sitzungen ueber alle Mandanten),
`POST /admin/sessions/{id}/terminate` (zwangsweises Trennen einer laufenden
Sitzung) und `GET /admin/sessions/{id}/recording` (Integritaetspruefung der
Aufzeichnung). "Beenden" nutzt eine neue prozesslokale Registry
(`app/security/active_sessions.py`) + `asyncio.Task.cancel()` auf die
WebSocket-Route der Sitzung, was den bestehenden Cleanup-Pfad (DB-Update,
Audit-Log-Eintrag, WebSocket schliessen) unveraendert durchlaufen laesst —
funktioniert nur fuer Sitzungen auf demselben Server-Prozess (`killable`-Feld
in der Response zeigt das an, statt einen Beenden-Versuch fehlschlagen zu
lassen).
**5) Hostgruppen/Server im Menue getrennt**: der bisherige kombinierte Tab
"Hosts & Verbindungen" wurde in zwei eigene Tabs "Hostgruppen" und "Server"
aufgeteilt (gleicher Ladepfad/gleiche Formulare, nur die Navigation ist jetzt
getrennt).
**6) Credentials ins RBAC-Modell**: zwei neue Rollen `credentials_view`/
`credentials_manage` (Migration `0008_credentials_roles.sql`), die — wie
`ssh_connect`/`rdp_connect`/`file_transfer` — pro Hostgruppe an einzelne User
oder Benutzergruppen vergeben werden koennen. Damit koennen auch NICHT-Admins
gezielt Zugangsdaten (RDP-Passwort setzen/entfernen, SSH-Key-Zuordnung, nie
der Klartext selbst) fuer Hosts "ihrer" Hostgruppe verwalten, ohne Admin oder
Mandanten-Admin sein zu muessen (neue Dependency
`require_admin_scope_or_host_role` in `app/auth/deps.py`, neuer Endpunkt
`GET /admin/hosts/{id}/credentials`, Dashboard zeigt einen "Zugangsdaten"-
Knopf bei Hosts mit dieser Rolle).
## Tests
```bash
pytest -q
```
61 Tests (vorher 46) decken ab: Argon2id/TOTP-Grundfunktionen, Audit-Hash-Chain
68 Tests (vorher 61) decken ab: Argon2id/TOTP-Grundfunktionen, Audit-Hash-Chain
(inkl. Manipulationserkennung und Trigger-Durchsetzung), RBAC-Logik inkl.
Ablaufdaten, den vollstaendigen Login-Flow (Passwort -> TOTP-Enrollment ->
Session-Cookie -> geschuetzte Endpunkte) gegen die echte FastAPI-App, 17
@ -216,13 +275,30 @@ Loeschen bei vorhandener Audit-Historie vs. echtem Hard-Delete ohne),
Update/Delete fuer Hostgruppen (blockiert solange Hosts enthalten sind),
Hosts (Soft- vs. Hard-Delete), SSH-Keys (inkl. Rotation) und Benutzergruppen,
sowie den neuen `GET /admin/hosts/{id}`-Detailendpunkt inkl. SSH-Key-
Zuordnungen und RDP-Zugangsdaten-Status.
Zuordnungen und RDP-Zugangsdaten-Status. 7 weitere neue Tests zu Phase 9
(`tests/test_phase9.py`) decken zusaetzlich ab: dass "Host-Key ermitteln" bei
einem echten Verbindungsfehler 502 statt 500 liefert und bei einem Auth-Fehler
NACH erfolgreichem Key-Exchange weiterhin als Erfolg gilt; dass
`/admin/sessions*` ausschliesslich Super-Admins erlaubt ist (Mandanten-Admin
= 403) und `terminate`/`recording` fuer nicht (mehr) laufende bzw. nicht
aufgezeichnete Sitzungen sauber 409/404 statt einen internen Fehler liefern;
dass die neuen Rollen `credentials_view`/`credentials_manage` Nicht-Admins
gezielten Lese- bzw. Lese+Schreib-Zugriff auf Zugangsdaten geben (und ein User
ganz ohne diese Rolle weiterhin 403 bekommt), inkl. des neuen
`can_view_credentials`-Felds in `GET /catalog/hosts`. Nebenbei beim
Gegenpruefen entdeckt und mitkorrigiert: drei Tests in
`tests/test_admin_groups_tokens.py` sendeten noch das VOR Phase 8 gueltige
Payload-Feld `role_name` (Einzahl) an `/admin/group-roles/grant`, das
`GroupRoleGrantRequest` seit der Umstellung auf Mehrfachauswahl aber gar nicht
mehr kennt (erwartet `role_names`, eine Liste) — ein beim Phase-8-Umbau
liegen gebliebener Regressionsfehler, jetzt auf `role_names` korrigiert.
> **Hinweis:** Alle 34 in dieser und der vorherigen Session neu
> hinzugekommenen Tests (5 CSP + 14 Admin/Gruppen/Token + 15 Mandanten/CRUD)
> konnten in der verwendeten Cloud-Sandbox nicht mit `pytest -q` ausgefuehrt
> werden, da diese Sandbox keinen Netzwerkzugriff auf PyPI hat und
> `fastapi`/`aiosqlite`/`argon2`/`asyncssh` dort nicht vorinstalliert sind.
> **Hinweis:** Alle 41 in dieser und den beiden vorherigen Sessions neu
> hinzugekommenen Tests (5 CSP + 14 Admin/Gruppen/Token + 15 Mandanten/CRUD +
> 7 Phase 9) konnten in der verwendeten Cloud-Sandbox nicht mit `pytest -q`
> ausgefuehrt werden, da diese Sandbox keinen Netzwerkzugriff auf PyPI hat und
> `fastapi`/`aiosqlite`/`argon2`/`asyncssh` dort nicht vorinstalliert sind
> (auch nicht ueber die Geraete-Bruecke zum lokalen Rechner erreichbar).
> Fuer Phase 8 wurde stattdessen ein tieferes Verifikationsverfahren
> angewendet als in der vorherigen Session: minimale Stub-Module fuer die
> vier fehlenden Pakete (`fastapi`s `APIRouter`-Dekoratoren als No-Ops, ein
@ -236,12 +312,22 @@ Zuordnungen und RDP-Zugangsdaten-Status.
> selbst nutzt weiterhin `httpx.AsyncClient` gegen die echte ASGI-App (wie
> alle anderen Testdateien) und wurde daher zeilenweise gegen die tatsaechliche
> Endpunkt-Implementierung gegengeprueft (Pfade, Payload-Felder, Statuscodes),
> aber nicht selbst mit `pytest` ausgefuehrt. Alle geaenderten Python-/
> JS-Dateien wurden mit `py_compile`/`node --check` auf Syntaxfehler geprueft,
> und jeder `document.getElementById`-Aufruf in `admin.js`/`terminal.js` wurde
> automatisiert gegen die tatsaechlichen HTML-IDs in `admin.html`/`terminal.html`
> abgeglichen (0 Abweichungen). Bitte `pytest -q` lokal ausfuehren und
> Ergebnis melden.
> aber nicht selbst mit `pytest` ausgefuehrt. Fuer Phase 9 zusaetzlich: die
> reparierte Kontrollfluss-Logik von `discover_and_store_host_key()` wurde
> mit einem eigenstaendigen Fake-`asyncssh`-Nachbau isoliert durchgespielt
> (alle drei Faelle: Verbindungsfehler vor Key-Exchange, Auth-Fehler NACH
> Key-Exchange = der eigentliche Bug, voller Erfolg), Migration
> `0008_credentials_roles.sql` wurde sowohl gegen eine leere als auch gegen
> eine bereits mit 0001-0007 befuellte In-Memory-DB angewendet (Lehre aus dem
> Migrations-Vorfall in Phase 8, siehe unten), und die neuen, rein
> In-Process-Module `app/security/log_stream.py` und
> `app/security/active_sessions.py` (kein DB-/HTTP-Bezug) wurden direkt
> importiert und gegen echtes `asyncio`/`logging` ausgefuehrt statt nur
> gelesen. Alle geaenderten Python-/JS-Dateien wurden mit
> `py_compile`/`node --check` auf Syntaxfehler geprueft, und jeder
> `document.getElementById`-Aufruf in `admin.js` wurde automatisiert gegen
> die tatsaechlichen HTML-IDs in `admin.html` abgeglichen (0 Abweichungen).
> Bitte `pytest -q` lokal ausfuehren und Ergebnis melden.
Manuell zusaetzlich verifiziert (siehe Entwicklungs-Log dieser Session):
Server-Start, Static-/Template-Auslieferung, Security-Header, vollstaendiger