more admin stuff 2
This commit is contained in:
112
README.md
112
README.md
@ -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
|
||||
|
||||
Reference in New Issue
Block a user