more admin stuff
This commit is contained in:
154
README.md
154
README.md
@ -16,7 +16,7 @@ app/ Backend (FastAPI, Python 3.11+)
|
||||
ssh_proxy/ SSH-Terminal-WebSocket + SFTP-Filetransfer
|
||||
rdp_proxy/ Guacamole-Protokoll-Tunnel zu guacd
|
||||
recordings/ Hash-verkettete Session-Aufzeichnung
|
||||
db/ SQLite-Migrationen (0001-0005, laufen automatisch beim Start)
|
||||
db/ SQLite-Migrationen (0001-0007, laufen automatisch beim Start)
|
||||
static/, templates/ Frontend (Vanilla JS, xterm.js, guacamole-common-js, Admin-Oberflaeche, API-Doku)
|
||||
scripts/ Betriebs-/Hilfsskripte (Admin anlegen, Assets bauen)
|
||||
ansible/ Deployment (Rollen, systemd-Unit-Templates)
|
||||
@ -48,10 +48,10 @@ verwalten; ein manueller Umweg ueber die rohe JSON-API ist dafuer nicht mehr
|
||||
noetig. Fuer RDP muss zusaetzlich ein laufender `guacd` erreichbar sein (siehe
|
||||
`JUMPHOST_GUACD_HOST`/`_PORT`).
|
||||
|
||||
Die SQLite-Migrationen 0004 (`user_groups`) und 0005 (`api_tokens`) werden
|
||||
beim naechsten Start automatisch angewendet (`app/db.py`, `_apply_migrations`)
|
||||
— kein manueller Migrationsschritt noetig, auch nicht bei einem bestehenden
|
||||
Datenbestand.
|
||||
Die SQLite-Migrationen 0004 (`user_groups`), 0005 (`api_tokens`), 0006
|
||||
(`tenants`) und 0007 (`crud_extras`) werden beim naechsten Start automatisch
|
||||
angewendet (`app/db.py`, `_apply_migrations`) — kein manueller
|
||||
Migrationsschritt noetig, auch nicht bei einem bestehenden Datenbestand.
|
||||
|
||||
## Admin-Oberflaeche, Gruppen-RBAC, API-Tokens und API-Dokumentation
|
||||
|
||||
@ -105,13 +105,94 @@ rendert Endpunkte gruppiert nach Tag, ausschliesslich per
|
||||
`document.createElement`/`textContent` (kein `innerHTML`, keine Inline-
|
||||
Styles/-Scripts, siehe `tests/test_csp_compliance.py`).
|
||||
|
||||
## Phase 8: Mandantenfaehigkeit, CRUD-Vervollstaendigung, Dateitransfer, Mehrfachrollen
|
||||
|
||||
Diese Session hat die Admin-Oberflaeche um sieben zusammenhaengende
|
||||
Erweiterungen ergaenzt:
|
||||
|
||||
**1) Mandantenfaehigkeit ("volle Isolation + Mandanten-Admins", Migration
|
||||
`0006_tenants.sql`)**: jede Hostgruppe, Benutzergruppe, jeder SSH-Key und
|
||||
jedes API-Token gehoert zu genau einem Mandanten (`tenants`-Tabelle,
|
||||
Standard-Mandant `id=1` "Standard" fuer bestehende Daten). Es gibt zwei
|
||||
Admin-Stufen: **Super-Admin** (`users.is_admin=1`, unveraendert wie zuvor)
|
||||
sieht/verwaltet **alle** Mandanten weiterhin vollstaendig — keine Regression
|
||||
gegenueber Phase 7. **Mandanten-Admin** (neue `tenant_admins`-Zuordnung,
|
||||
ein User kann Admin mehrerer Mandanten sein) sieht/verwaltet **ausschliesslich**
|
||||
die Ressourcen seines/seiner Mandanten; jeder Zugriffsversuch auf eine
|
||||
fremde Mandanten-ID liefert bewusst **404** (nicht 403, siehe
|
||||
`app/tenancy.py`, `TenantScope`) — ein Mandanten-Admin soll aus der
|
||||
Fehlerantwort nicht einmal ableiten koennen, dass eine ID ausserhalb seines
|
||||
Mandanten ueberhaupt existiert. Tenant-CRUD und die Ernennung von
|
||||
Mandanten-Admins sind ausschliesslich Super-Admin-Aktionen (neuer Tab
|
||||
"Mandanten" in der Admin-Oberflaeche, fuer Mandanten-Admins nicht sichtbar).
|
||||
Ein API-Token ist immer an **genau einen** Mandanten gebunden
|
||||
(`api_tokens.tenant_id`) und kann diesen nie verlassen.
|
||||
|
||||
**2) CRUD-Vervollstaendigung**: jede Ressource, die die Admin-Oberflaeche
|
||||
anlegen kann, ist jetzt auch bearbeitbar und loeschbar — Benutzer (inkl.
|
||||
Passwort setzen, deaktivieren, reaktivieren), Benutzergruppen, Hostgruppen,
|
||||
Hosts, SSH-Keys (inkl. Rotation). Zwei bewusste Sicherheits-/Integritaets-
|
||||
Entscheidungen dabei: ein Benutzerkonto mit vorhandener Audit-Historie wird
|
||||
**nicht** hart geloescht (die Hash-Chain verweist absichtlich ohne `ON DELETE
|
||||
CASCADE` auf `users.id`, siehe Migration `0007_crud_extras.sql`), sondern
|
||||
deaktiviert und anonymisiert (`deleted_user_<id>`, Passwort/TOTP geloescht);
|
||||
nur ein Konto ganz ohne Audit-Spuren wird tatsaechlich entfernt — die
|
||||
Response (`hard_deleted: true/false`) zeigt an, welcher Fall eintrat. Ein
|
||||
Host wird per Default **soft-deleted** (`is_active=0`, wie schon zuvor vom
|
||||
Katalog beruecksichtigt); `DELETE /admin/hosts/{id}?hard=true` versucht
|
||||
zusaetzlich ein echtes Entfernen, faellt aber automatisch auf Soft-Delete
|
||||
zurueck, falls der Host bereits Sitzungshistorie hat.
|
||||
|
||||
**3) Login-Verlauf**: neuer Tab "Login-Verlauf" filtert clientseitig aus dem
|
||||
bestehenden Audit-Log-Feed (`GET /admin/audit-log`) gezielt Login-/Logout-/
|
||||
Fehlversuch-Ereignisse heraus — kein neuer Backend-Endpunkt noetig, da die
|
||||
Rohdaten bereits vorhanden waren, nur bisher nicht dediziert sichtbar.
|
||||
|
||||
**4) "Details"-Fix fuer Hosts**: der defekte "Details"-Button in der Hosts-
|
||||
Tabelle wurde durch einen neuen Endpunkt `GET /admin/hosts/{id}` (liefert den
|
||||
vollstaendigen, aktuellen Datensatz inkl. zugeordneter SSH-Keys und ob
|
||||
RDP-Zugangsdaten hinterlegt sind) sowie eine neu geschriebene, robuste
|
||||
`showHostDetail()`-Funktion in `admin.js` ersetzt, die bei jedem Aufruf
|
||||
frisch nachlaedt statt sich auf ggf. veraltete Listendaten zu verlassen, und
|
||||
Fehler inline anzeigt statt sie zu verschlucken. Da diese Sandbox die App
|
||||
nicht tatsaechlich im Browser ausfuehren kann, war die urspruengliche
|
||||
Ursache nicht direkt reproduzierbar — bitte nach dem Update pruefen, ob der
|
||||
Button jetzt zuverlaessig funktioniert, oder bei einem verbleibenden Fehler
|
||||
die genaue Browser-Konsolenmeldung mitteilen.
|
||||
|
||||
**5) Eigener "Zugangsdaten"-Tab**: buendelt SSH-Keys UND RDP/Windows-
|
||||
Passwoerter (bisher Teil des Hosts-Formulars) an einer Stelle, inkl. Uebersicht,
|
||||
welche RDP-Hosts bereits ein Passwort hinterlegt haben (`GET
|
||||
/admin/rdp-credentials`).
|
||||
|
||||
**6) Dateitransfer-Fenster (SSH-Terminal)**: der bisherige, `prompt()`-
|
||||
basierte Einzel-Upload-Knopf in der Terminal-Sitzung (`templates/terminal.html`,
|
||||
`static/js/terminal.js`) wurde durch ein eigenstaendiges Dateitransfer-Panel
|
||||
ersetzt, das **beide Richtungen** abdeckt — Upload (wie zuvor, jetzt mit
|
||||
eigenem Formular statt Browser-`prompt()`) und **Download** (neu in der UI;
|
||||
der Backend-Endpunkt `GET /ssh/{host_id}/files/download` existierte bereits
|
||||
und wird per `fetch()` + Blob + synthetischem `<a download>`-Link
|
||||
angesteuert, damit Fehler inline im Panel erscheinen statt die Seite zu
|
||||
verlassen). Das Panel fuehrt zusaetzlich ein kurzes Transfer-Log der
|
||||
laufenden Sitzung. Gilt bewusst nur fuer SSH (RDP hat in dieser
|
||||
Implementierung keinen eigenen Dateitransfer-Endpunkt).
|
||||
|
||||
**7) Mehrfachauswahl bei Rollenvergabe**: `POST /admin/roles/grant` und
|
||||
`POST /admin/group-roles/grant` akzeptieren jetzt `role_names` (Liste, 1-6
|
||||
Rollen) statt einer einzelnen `role_name` — ein Benutzer bzw. eine
|
||||
Benutzergruppe kann damit in einem Schritt mehrere Rollen auf derselben
|
||||
Hostgruppe erhalten (Checkbox-Raster statt Dropdown in der Oberflaeche).
|
||||
Das Entziehen bleibt bewusst pro Zeile/Rolle (`role_name`, Einzahl) — das
|
||||
entspricht dem bestehenden "Entziehen"-Knopf pro Tabellenzeile und braucht
|
||||
keine Mehrfachauswahl.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
pytest -q
|
||||
```
|
||||
|
||||
46 Tests (vorher 27) decken ab: Argon2id/TOTP-Grundfunktionen, Audit-Hash-Chain
|
||||
61 Tests (vorher 46) 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
|
||||
@ -125,19 +206,42 @@ Admin-only-Durchsetzung fuer alle neuen Endpunkte, Token-Scope-Durchsetzung
|
||||
Scope), dass ein Token niemals andere Tokens verwalten kann (Rechte-
|
||||
Eskalationsschutz), sowie Admin-Gating von `/docs` und `/openapi.json`.
|
||||
Details, Vorgehen und Ergebnisse der urspruenglichen 27 Tests: siehe
|
||||
`Pentest_Report.md`.
|
||||
`Pentest_Report.md`. 15 weitere neue Tests zu Phase 8
|
||||
(`tests/test_tenants.py`) decken zusaetzlich ab: Tenant-CRUD (Super-Admin
|
||||
only, Mandanten-Admin explizit ausgeschlossen), volle Mandanten-Isolation
|
||||
(Hostgruppen/Hosts/Benutzer/SSH-Keys/Audit-Log — fremde Mandanten-IDs
|
||||
liefern 404), Mehrfachauswahl bei Einzel- UND Gruppen-Rollenvergabe,
|
||||
Update/Deactivate/Delete fuer Benutzer (inkl. Anonymisieren-statt-Hart-
|
||||
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.
|
||||
|
||||
> **Hinweis:** Die 19 in dieser Session neu hinzugekommenen Tests (5 CSP +
|
||||
> 14 Admin/Gruppen/Token) konnten in der verwendeten Cloud-Sandbox nicht
|
||||
> mit `pytest -q` ausgefuehrt werden, da diese Sandbox keinen Netzwerkzugriff
|
||||
> auf PyPI hat und `fastapi`/`aiosqlite` dort nicht vorinstalliert sind.
|
||||
> Stattdessen wurden alle neuen/gaenderten SQL-Queries gegen eine echte
|
||||
> `sqlite3`-Instanz mit allen 5 Migrationen (inkl. der beiden neuen) manuell
|
||||
> durchgespielt, `app/security/api_tokens.py` und die neuen Pydantic-Schemas
|
||||
> direkt importiert und mit echten Assertions verifiziert, und alle
|
||||
> geaenderten Python-/JS-Dateien mit `py_compile`/`node --check` auf
|
||||
> Syntaxfehler geprueft. Bitte `pytest -q` lokal ausfuehren und Ergebnis
|
||||
> melden.
|
||||
> **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.
|
||||
> 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
|
||||
> synchrones `sqlite3`-basiertes Shim mit aiosqlite-kompatiblem
|
||||
> `async`-Interface) erlauben es, die **echten** Endpunkt-Funktionen aus
|
||||
> `app/admin/routes.py` direkt (ohne HTTP-Layer) gegen eine echte
|
||||
> In-Memory-`sqlite3`-Datenbank mit allen 7 Migrationen auszufuehren — damit
|
||||
> wurden ueber 20 Integrations-Assertions (Mandanten-Isolation,
|
||||
> Rollen-Mehrfachvergabe, Delete-Semantik) tatsaechlich lauffaehig
|
||||
> verifiziert, nicht nur simulierte SQL-Queries. Der neue `tests/test_tenants.py`
|
||||
> 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.
|
||||
|
||||
Manuell zusaetzlich verifiziert (siehe Entwicklungs-Log dieser Session):
|
||||
Server-Start, Static-/Template-Auslieferung, Security-Header, vollstaendiger
|
||||
@ -241,7 +345,15 @@ ist die massgebliche, aktuelle Fassung dieser Liste. Kurzfassung:
|
||||
gibt noch keine erzwungene maximale Laufzeit, keine automatische
|
||||
Benachrichtigung vor Ablauf und keine "Token zuletzt benutzt vor X Tagen
|
||||
-> automatisch deaktivieren"-Routine.
|
||||
11. Ein erneuter `bandit`/`pip-audit`-Lauf ueber die in dieser Session neu
|
||||
hinzugekommenen Dateien (Admin-Oberflaeche, Gruppen-RBAC, API-Tokens)
|
||||
steht noch aus (siehe oben).
|
||||
11. Ein erneuter `bandit`/`pip-audit`-Lauf ueber die in dieser und der
|
||||
Phase-8-Session neu hinzugekommenen Dateien (Admin-Oberflaeche,
|
||||
Gruppen-RBAC, API-Tokens, Mandantenfaehigkeit, Dateitransfer-Panel)
|
||||
steht noch aus (keine neuen Fremdabhaengigkeiten hinzugekommen, siehe
|
||||
oben).
|
||||
12. **Phase 8 / "Details"-Fix**: der Root-Cause des urspruenglich gemeldeten
|
||||
defekten "Details"-Buttons konnte in dieser Sandbox nicht reproduziert
|
||||
werden (die App laesst sich hier nicht im Browser ausfuehren) — der neue
|
||||
`GET /admin/hosts/{id}`-Endpunkt plus robustere `showHostDetail()`-Logik
|
||||
sollten das Problem loesen, sollten aber nach dem Deployment einmal
|
||||
manuell im Browser bestaetigt werden.
|
||||
</content>
|
||||
|
||||
Reference in New Issue
Block a user