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>
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 — siehefind_device_in_organization()inapp.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(Defaulthttp://127.0.0.1:8080)TUXFLOTTE_KUNDENPLATTFORM_TOKEN— muss mit dem gleichnamigen Wert in provisioning-servers/etc/tuxflotte/provisioning-server.envübereinstimmenKUNDENPLATTFORM_DATABASE_URLKUNDENPLATTFORM_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
Languages
Python
56.1%
HTML
42.3%
PLpgSQL
1.6%