Thomas Stallinger 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
2026-08-06 08:29:51 +02:00
2026-08-06 08:29:51 +02:00

kundenplattform

Kunden-Web-UI für den Tuxflotte-Auftragskatalog — von provisioning-server (anode) bewusst getrennter Dienst, siehe ADR-0011.

Architektur

  • FastAPI + Jinja2 (serverseitig gerendert, kein Frontend-Build).
  • Eigene Postgres-Datenbank (benutzer-Tabelle: E-Mail/Passwort-Hash pro Organisation) — getrennt von provisioning-servers Datenbank, keine Cross-Database-Referenzen.
  • Spricht mit anode ausschließlich über dessen HTTP-API, authentifiziert mit einem eigenen Service-Token (TUXFLOTTE_KUNDENPLATTFORM_TOKEN, getrennt vom Admin-Token). Organisationszugehörigkeits-Prüfung (darf dieser Login auf dieses Gerät zugreifen?) passiert hier in der Anwendung, vor jedem Aufruf gegen anode — siehe find_device_in_organization() in app.py.

Setup (lokal)

python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
KUNDENPLATTFORM_DATABASE_URL=postgresql://... .venv/bin/psql ... -f migrations/0001_initial_schema.sql

Env-Variablen:

  • TUXFLOTTE_ANODE_URL (Default http://127.0.0.1:8080)
  • TUXFLOTTE_KUNDENPLATTFORM_TOKEN — muss mit dem gleichnamigen Wert in provisioning-servers /etc/tuxflotte/provisioning-server.env übereinstimmen
  • KUNDENPLATTFORM_DATABASE_URL
  • KUNDENPLATTFORM_SESSION_SECRET — beliebiger zufälliger String zum Signieren der Session-Cookie (z.B. openssl rand -base64 32)

Start: .venv/bin/uvicorn app:app --port 8081

Konto anlegen

Kein Self-Service in v1 (siehe ADR-0011) — Konten werden manuell angelegt:

KUNDENPLATTFORM_DATABASE_URL=... .venv/bin/python scripts/create_user.py \
    --organization-id <organization-uuid> \
    --email admin@schule-beispiel.de

Deploy (anode)

Wie bei provisioning-server, git-basiert statt scp:

git pull --ff-only
systemctl restart kundenplattform

Läuft unter einem eigenen, unprivilegierten System-User kundenplattform (nicht root, anders als provisioning-server) — siehe kundenplattform.service. Port 8081 (provisioning-server belegt 8080).

Öffentliche Erreichbarkeit über Pangolin ist nicht Teil dieses Repos — das ist ein separater Ops/DNS/Reverse-Proxy-Schritt.

Description
No description provided
Readme 310 KiB
Languages
Python 56.1%
HTML 42.3%
PLpgSQL 1.6%