fix(redis): Pool-Rollout vervollständigen + TOTP-Lockout fail-closed mit 503
Security Audit / Python Dependency Audit (push) Canceled after 0s
Security Audit / Node.js Dependency Audit (push) Canceled after 0s
Security Audit / Frontend Build (tsc + vite) (push) Canceled after 0s

Redis-Review deckte auf, dass der Pool-Fix vom letzten Commit nur
totp_login/kiosk_security erreichte. Login/Refresh (auth_service.py) und
PIN/NFC/QR-Kiosk-Login (kiosk_auth_service.py) öffneten weiterhin pro
Request eine neue aioredis-Verbindung. Zusätzlich nutzten
kiosk_session_service.py und public_stamp_session_service.py den
*sync* Redis-Client aus async-Code – blockierender Socket-Call im
Event-Loop bei jedem Kiosk-/Stempel-Request.

Alle auf get_async_redis() umgestellt. TOTP-Lockout wirft jetzt 503
statt eines ungefangenen 500 bei Redis-Ausfall (RedisError explizit
gefangen, eigene HTTPExceptions unberührt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Ahyx6D3r7G1EuAc42nezn
This commit is contained in:
2026-09-03 00:09:35 +02:00
co-authored by Claude Sonnet 5
parent 5ba5a99e02
commit 56f6f16e27
5 changed files with 211 additions and 243 deletions
@@ -23,14 +23,8 @@ SESSION_KEY_PREFIX = "public_stamp_session:"
class PublicStampSessionService:
def _get_redis(self):
from app.core.redis import get_redis_client
client = get_redis_client()
if client is None:
raise HTTPException(
status_code=503,
detail="Stempel-Service nicht verfügbar (Redis nicht erreichbar).",
)
return client
from app.core.redis import get_async_redis
return get_async_redis()
async def create_session(self, user_id: uuid.UUID, company_id: uuid.UUID) -> str:
redis = self._get_redis()
@@ -42,7 +36,7 @@ class PublicStampSessionService:
"created_at": datetime.now(timezone.utc).isoformat(),
}
try:
redis.setex(key, PUBLIC_STAMP_SESSION_TTL, json.dumps(payload))
await redis.set(key, json.dumps(payload), ex=PUBLIC_STAMP_SESSION_TTL)
except Exception as exc:
raise HTTPException(status_code=503, detail=f"Session konnte nicht erstellt werden: {exc}")
return session_token
@@ -50,7 +44,7 @@ class PublicStampSessionService:
async def get_session(self, session_token: str) -> Optional[dict]:
redis = self._get_redis()
try:
data = redis.get(SESSION_KEY_PREFIX + session_token)
data = await redis.get(SESSION_KEY_PREFIX + session_token)
except Exception as exc:
raise HTTPException(status_code=503, detail=f"Session-Lookup fehlgeschlagen: {exc}")
if data is None:
@@ -69,7 +63,7 @@ class PublicStampSessionService:
async def invalidate_session(self, session_token: str) -> None:
redis = self._get_redis()
try:
redis.delete(SESSION_KEY_PREFIX + session_token)
await redis.delete(SESSION_KEY_PREFIX + session_token)
except Exception:
pass # Best-effort