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>
41 lines
1.1 KiB
Python
41 lines
1.1 KiB
Python
import os
|
|
from contextlib import contextmanager
|
|
|
|
import httpx
|
|
|
|
|
|
ANODE_URL = os.environ.get("TUXFLOTTE_ANODE_URL", "http://127.0.0.1:8080")
|
|
KUNDENPLATTFORM_TOKEN = os.environ.get("TUXFLOTTE_KUNDENPLATTFORM_TOKEN", "")
|
|
|
|
|
|
def anode_request(method: str, path: str, **kwargs) -> dict:
|
|
response = httpx.request(
|
|
method,
|
|
ANODE_URL + path,
|
|
headers={"Authorization": f"Bearer {KUNDENPLATTFORM_TOKEN}"},
|
|
timeout=15,
|
|
**kwargs,
|
|
)
|
|
response.raise_for_status()
|
|
|
|
return response.json()
|
|
|
|
|
|
@contextmanager
|
|
def anode_stream(method: str, path: str):
|
|
"""
|
|
Fuer den ISO-Download (Phase 5): der Browser hat kein
|
|
KUNDENPLATTFORM_TOKEN, daher muss Kundenplattform als Proxy streamen
|
|
statt einen direkten Link auf anode auszugeben. Kein Read-Timeout - eine
|
|
mehrere GB grosse Datei ueber die WireGuard-Verbindung zu anode kann
|
|
laenger dauern als der sonst genutzte 15s-Timeout erlaubt.
|
|
"""
|
|
|
|
with httpx.stream(
|
|
method,
|
|
ANODE_URL + path,
|
|
headers={"Authorization": f"Bearer {KUNDENPLATTFORM_TOKEN}"},
|
|
timeout=httpx.Timeout(15, read=None),
|
|
) as response:
|
|
yield response
|