From 2a734497ed180cc0cfc23863b36789edf69443bc Mon Sep 17 00:00:00 2001 From: Thomas Stallinger Date: Sun, 25 Oct 2026 10:36:06 +0100 Subject: [PATCH] Passwort vergessen: Konto aus dem QR-Code (ADR-0041, 25.10.2026) Co-Authored-By: Claude Opus 5.5 --- app.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/app.py b/app.py index 67a3475..817c546 100644 --- a/app.py +++ b/app.py @@ -6273,7 +6273,8 @@ def personalisierung_modus(device_id: str) -> str: return "passwort" if konto_aktiv else "aus" -# Der Kiosk "Passwort vergessen?" weist sich mit Gerät und diesem Token aus. +# Der Kiosk "Passwort vergessen?" und seit 25.10.2026 der QR-Code am +# Anmeldebildschirm weisen sich mit Gerät und diesem Token aus. # Abgeleitet statt gespeichert; lesen kann es jedes lokale Konto des Geräts, # es erlaubt aber nur, dem Besitzer einen Link an seine eigene Adresse zu # schicken (begrenzt, siehe VERGESSEN_*). @@ -6296,7 +6297,7 @@ def email_maskieren(email: str | None) -> str: return f"{lokal[:1]}•••@{domain}" -def _vergessen_konto(cur, device_id: str, token: str): +def _vergessen_konto(cur, device_id: str, token: str, anmeldename: str | None = None): """Das aktive persönliche Konto des Geräts, wenn das Kiosk-Token passt.""" erwartet = personalisierung_kiosk_token(device_id) @@ -6309,25 +6310,26 @@ def _vergessen_konto(cur, device_id: str, token: str): JOIN devices d ON d.id = p.device_id JOIN organizations o ON o.id = p.organization_id WHERE p.device_id::text = %s AND p.status = 'angelegt' AND NOT p.entfernen AND NOT p.gesperrt + AND (%s::text IS NULL OR p.anmeldename = %s) ORDER BY p.angelegt_am DESC LIMIT 1 """, - (str(device_id),), + (str(device_id), anmeldename or None, anmeldename or None), ) return cur.fetchone() -def vergessen_pruefen(device_id: str, token: str) -> dict | None: +def vergessen_pruefen(device_id: str, token: str, anmeldename: str | None = None) -> dict | None: with get_database_connection() as conn: with conn.cursor() as cur: - row = _vergessen_konto(cur, device_id, token) + row = _vergessen_konto(cur, device_id, token, anmeldename) if row is None: return None _pid, anmeldename, email, _mails, hostname, org_name = row return {"anmeldename": anmeldename, "email_maskiert": email_maskieren(email), "hostname": hostname, "organisation": org_name} -def vergessen_anfordern(device_id: str, token: str): +def vergessen_anfordern(device_id: str, token: str, anmeldename: str | None = None): """ Erzeugt den Link für "Passwort vergessen?". Gibt (Ergebnis, Fehler) zurück; das Ergebnis enthält die volle Adresse und das Token für die @@ -6336,7 +6338,7 @@ def vergessen_anfordern(device_id: str, token: str): with get_database_connection() as conn: with conn.cursor() as cur: - row = _vergessen_konto(cur, device_id, token) + row = _vergessen_konto(cur, device_id, token, anmeldename) if row is None: return None, "Auf diesem Gerät ist kein persönliches Konto eingerichtet." return _vergessen_link(cur, row) @@ -6944,6 +6946,10 @@ class StandRequest(BaseModel): class VergessenRequest(BaseModel): device_id: str token: str + # Seit dem QR-Code am Anmeldebildschirm (Ergänzung 25.10.2026) nennt der + # Link das Konto, dessen Anmeldung fehlschlug; ohne Angabe das zuletzt + # eingerichtete wie beim alten Kiosk. + anmeldename: str | None = None class NeuesPasswortRequest(BaseModel): @@ -7092,7 +7098,7 @@ def post_vergessen_pruefen(payload: VergessenRequest, authorization: str | None if not require_service_token(authorization): return {"success": False, "error": "unauthorized", "message": "Fehlendes oder ungültiges Service-Token."} - ergebnis = vergessen_pruefen(payload.device_id, payload.token) + ergebnis = vergessen_pruefen(payload.device_id, payload.token, payload.anmeldename) if ergebnis is None: return {"success": False, "error": "kein_konto", "message": "Auf diesem Gerät ist kein persönliches Konto eingerichtet."} return {"success": True, **ergebnis} @@ -7103,7 +7109,7 @@ def post_vergessen_anfordern(payload: VergessenRequest, authorization: str | Non if not require_service_token(authorization): return {"success": False, "error": "unauthorized", "message": "Fehlendes oder ungültiges Service-Token."} - ergebnis, fehler = vergessen_anfordern(payload.device_id, payload.token) + ergebnis, fehler = vergessen_anfordern(payload.device_id, payload.token, payload.anmeldename) if ergebnis is None: return {"success": False, "error": "abgelehnt", "message": fehler} return {"success": True, **ergebnis}