From e12f2206a4aa0a6cdfa67591dbea060f24e0b22d Mon Sep 17 00:00:00 2001 From: Thomas Stallinger Date: Thu, 15 Oct 2026 15:32:08 +0200 Subject: [PATCH] =?UTF-8?q?Personalisierung:=20Code=20pr=C3=BCfen=20ohne?= =?UTF-8?q?=20Verbrauch=20(f=C3=BCr=20das=20zweistufige=20Formular)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- app.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/app.py b/app.py index 40ba668..4aaa0ab 100644 --- a/app.py +++ b/app.py @@ -6034,6 +6034,32 @@ def withdraw_personalisierung(device_id: str) -> bool: return cur.rowcount > 0 +def check_personalisierung_code(code: str): + """ + Erster Schritt des Formulars: zeigt Einrichtung, Gerät und erlaubte + Domänen an, ohne den Code zu verbrauchen. + """ + + with get_database_connection() as conn: + with conn.cursor() as cur: + personalisierung_verfallene_markieren(cur) + cur.execute( + """ + SELECT o.name, o.email_domaenen, d.hostname + FROM personalisierungen p + JOIN organizations o ON o.id = p.organization_id + JOIN devices d ON d.id = p.device_id + WHERE p.code_hash = %s AND p.status = 'code_offen' + """, + (personalisierung_code_hash(code),), + ) + row = cur.fetchone() + + if row is None: + return None + return {"organisation": row[0], "domaenen": list(row[1] or []), "hostname": row[2]} + + def submit_personalisierung(code: str, email: str, anmeldename: str, passwort_hash: str): """ Gibt (ergebnis, fehler) zurück. Unbekannte, verbrauchte und abgelaufene @@ -6189,6 +6215,25 @@ def get_personalisierungen_organisation(organization_id: str, authorization: str return {"success": True, "geraete": fetch_personalisierungen_organisation(organization_id)} +class CheckPersonalisierungRequest(BaseModel): + code: str + + +@app.post("/api/v1/personalisierung/pruefen") +def post_personalisierung_pruefen( + payload: CheckPersonalisierungRequest, + authorization: str | None = Header(default=None), +): + if not require_service_token(authorization): + return {"success": False, "error": "unauthorized", "message": "Fehlendes oder ungültiges Service-Token."} + + ergebnis = check_personalisierung_code(payload.code) + if ergebnis is None: + return {"success": False, "error": "abgelehnt", "message": "Der Aktivierungscode ist ungültig, schon verwendet oder abgelaufen."} + + return {"success": True, **ergebnis} + + @app.post("/api/v1/personalisierung/einreichen") def post_personalisierung_einreichen( payload: SubmitPersonalisierungRequest,