Logpy-AgentOne/pipe/testzugang.py
Jeuner af67ee89e3 feat: signierte 1h-Testzugangslinks fuer den Leitstand ("agent-one test")
Gegenstueck zum "agent-one test"-Kommando in Alice (dillenberg.net,
dilles-agent.php). Kein Live-Aufruf zwischen den Systemen noetig - beide
teilen sich TESTZUGANG_SECRET, das Token ist ein HMAC-signierter
Ablauf-Zeitstempel ("ablauf.signatur"), stateless geprueft.

pipe/testzugang.py: gueltig(token) prueft Signatur (hmac.compare_digest)
und Ablauf. _angemeldet() in server.py akzeptiert das Token zusaetzlich zum
Passwort - per ?zugang=... in der URL oder per zugang-Cookie fuer
Folgeanfragen (fetch()-Aufrufe der Seite tragen die URL nicht mit). Ein
frisches URL-Token setzt das Cookie (HttpOnly, Secure, SameSite=Lax) fuer
den Rest der Stunde.

End-to-End getestet: PHP-generiertes Token wird von Python akzeptiert
(Cross-Language-HMAC-Kompatibilitaet bestaetigt), ungueltige Signatur und
abgelaufenes Token werden korrekt abgelehnt (401), Cookie-Folgeanfrage ohne
erneutes Token/Passwort funktioniert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015vtvcyNkapuDh583643hNs
2026-08-25 01:33:46 +02:00

35 lines
1.2 KiB
Python

"""Signierte, zeitlich begrenzte Testzugangs-Links zum Leitstand.
Gegenstueck zu dilles_agent_one_test_command() in dillenberg.net's
dilles-agent.php ("agent-one test"-Kommando im Alice-Chat). Beide Seiten
teilen sich TESTZUGANG_SECRET (config.py) - der Link wird komplett auf der
PHP-Seite erzeugt, ohne Live-Aufruf hierher; hier wird nur die Signatur
geprueft. Stateless: kein Server-seitiger Speicher fuer ausgegebene Tokens,
die Gueltigkeit steckt komplett im signierten Ablauf-Zeitstempel.
Token-Format: "<ablauf-unixzeit>.<hmac-sha256-hexdigest>"
"""
from __future__ import annotations
import hmac
import time
from hashlib import sha256
from . import config
def gueltig(token: str) -> bool:
"""Prueft ein Token aus ?zugang=... oder dem zugang-Cookie. Wirft nie."""
if not config.TESTZUGANG_SECRET or not token:
return False
try:
ablauf_str, signatur = token.split(".", 1)
ablauf = int(ablauf_str)
except (ValueError, AttributeError):
return False
erwartet = hmac.new(
config.TESTZUGANG_SECRET.encode("utf-8"), ablauf_str.encode("utf-8"), sha256
).hexdigest()
if not hmac.compare_digest(erwartet, signatur):
return False
return time.time() < ablauf