5 Commits

Author SHA1 Message Date
ec11de1ee9 feat: Self-Service-Kontenverwaltung + Organisationsdaten unter /organisation
Neue Rolle ist_organisationsadmin (auth.py), bewusst getrennt von
is_superuser: is_superuser bleibt exklusiv internes, organisationsuebergreifendes
Tuxflotte-Personal mit Zugriff auf /admin/* - ist_organisationsadmin ist
auf die eigene Organisation beschränkt und kann nie über Self-Service
gesetzt werden (organization_id/is_superuser kommen bei
POST /organisation/konten nie aus dem Formular, sondern immer aus der
Session).

/organisation zeigt jetzt Kontakt-/Stammdaten (bearbeitbar) und die
Kolleg:innen-Liste der eigenen Organisation (für alle sichtbar,
Verwaltung nur für Org-Admins/internes Personal via require_org_admin).
Selbstlöschung des eigenen Kontos wird verweigert.

fetch_benutzer_fuer_organisation() von admin_kunden.py nach auth.py
verschoben (jetzt von beiden Routern importiert, kein Duplikat) -
liefert zusätzlich id + ist_organisationsadmin. admin_kunden.py bekommt
eine neue Organisationsdaten-Sektion (nutzt denselben PATCH-Endpoint)
und die neue Checkbox beim Konto-Anlegen, um den ersten Org-Admin einer
neuen Organisation zu bootstrappen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 07:40:05 +02:00
19b39af0ce feat: Selfservice-UI fuer Organisation + Installationsmedium (Phase 5)
Zwei neue Menuepunkte fuer eingeloggte Kunden (nicht der
organisationsuebergreifende /admin/-Bereich):

- "Organisation": Name bearbeiten (ruft Phase-3-PATCH auf
  provisioning-server auf).
- "Installationsmedium": WLAN-SSID/-Passwort, Vorlagenwahl, Kontingent
  und Gueltigkeitsdauer speichern, personalisierte Kunden-ISO per
  Klick erstellen (Phase-4-Endpoints), Fortschritt/Status-Anzeige
  (Meta-Refresh waehrend pending/running), Download + dd/Rufus-
  Anleitung. Getrennte "aufladen"-Aktion erhoeht Kontingent/
  Gueltigkeit der bestehenden Enrollment Session (neuer PATCH-
  Endpoint), ohne eine neue ISO zu erzeugen - die bereits ausgeteilte
  bleibt gueltig.

Neue Tabelle installationsmedium_konfiguration (Migration 0004, eigene
DB, kein Foreign Key auf provisioning-server-Ressourcen, siehe
ADR-0011) haelt SSID/verschluesselten PSK/Vorlage/Kontingent-Defaults
sowie die IDs der aktuell gueltigen Enrollment Session und des
zugehoerigen ISO-Baus.

WLAN-PSK wird Fernet-verschluesselt gespeichert (encryption.py, neuer
KUNDENPLATTFORM_ENCRYPTION_KEY) - echte Zugangsdaten, kein
Passwort-Hash-Fall. Download laeuft ueber einen Streaming-Proxy
(anode_stream() in anode_client.py) statt eines direkten Links, weil
der Browser kein KUNDENPLATTFORM_TOKEN besitzt.

Lokal end-to-end verifiziert (Wegwerf-Postgres fuer beide Services,
echter build_customer_iso.sh-Lauf ueber die Kundenplattform-UI):
Formular -> Enrollment Session + ISO-Bau -> Status-Polling ->
Download-Proxy liefert byte-identische, korrekt personalisierte Datei
(SSID/PSK/Aktivierungscode per xorriso-Extraktion gegengeprueft) ->
Aufladen erhoeht Kontingent/Gueltigkeit der bestehenden Session ohne
neuen Bau -> "neues Installationsmedium erstellen" erzeugt neue
Session + ISO, alte Session bleibt unangetastet nutzbar (alter
ISO-Bau wird laut Phase-4-Cleanup automatisch entfernt), leeres
PSK-Feld uebernimmt das zuvor gespeicherte PSK korrekt.
Organisation-Name-Aenderung ueber PATCH verifiziert. Unauthentifizierter
Zugriff auf beide neuen Routen leitet korrekt zu /login um.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 01:36:46 +02:00
576613ed0f feat: Auth auf fastapi-users umstellen
Ersetzt die handgestrickte bcrypt+Session-Auth durch fastapi-users
(CookieTransport+JWTStrategy statt Starlette-Session, custom
psycopg3-async-Adapter statt SQLAlchemy). Bestehende bcrypt-Hashes
bleiben gültig und werden beim nächsten Login automatisch auf Argon2
angehoben (pwdlib-Default-PasswordHelper). ist_admin wird zu
is_superuser, damit fastapi-users' eingebaute Semantik direkt nutzbar
ist - require_admin/get_current_user bleiben als Wrapper unter
gleichem Namen, alle Router-Dateien bis auf geraete.py (dict- zu
attribut-Zugriff auf user.organization_id) unverändert.

Lokal end-to-end gegen eine Wegwerf-Postgres-Instanz (podman) verifiziert:
Migration, Bestandskonto-Login mit altem Passwort samt Argon2-Upgrade,
Admin-Kontoanlage über beide Aufrufstellen (create_user.py und
admin_kunden.py), 403 für Nicht-Admins, Logout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 08:29:51 +02:00
ed38f103f2 feat: modulare Struktur für Admin-Bereich (Schritt 1)
app.py auf APIRouter-Module umgestellt (db.py, templating.py,
anode_client.py, auth.py, routers/geraete.py + vier Admin-Router als
Platzhalter). benutzer.ist_admin (Migration 0002) + require_admin-
Dependency (403 ohne Admin-Flag). Admin-Nav-Link im Header nur für
Admin-Konten sichtbar. create_user.py um --admin-Flag erweitert.

Die vier Admin-Bereiche (Technik/Datenbank, Geräte, Flows, Kunden) sind
als Platzhalterseiten unter /admin/... verdrahtet und bekommen in den
nächsten Schritten echte Funktionalität.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 08:51:46 +02:00
c4825acd61 feat: initiale Kundenplattform (Login + Auftragskatalog-Auswahl)
Neuer, von provisioning-server (anode) getrennter Dienst, siehe ADR-0011:
FastAPI + Jinja2, eigene Postgres-DB (benutzer-Tabelle), eigenes
Service-Token (TUXFLOTTE_KUNDENPLATTFORM_TOKEN) für die Kommunikation mit
anode. Org-Zugehörigkeits-Prüfung passiert hier in der Anwendung vor jedem
Aufruf gegen anode.

Umfang v1: Login, Geräteliste, Auftragskatalog geräteweise aus-/abwählen
(inkl. der standardbrowser-Option für Brave). systemd-Unit läuft unter
eigenem unprivilegiertem System-User statt root.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:00:03 +02:00