from __future__ import annotations import base64 import html import secrets from uuid import uuid4 from .redis_session import AdminSessionStore CAPTCHA_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" def _captcha_code(length: int = 4) -> str: return "".join(secrets.choice(CAPTCHA_ALPHABET) for _ in range(length)) def _captcha_image(code: str) -> str: lines = "".join( f'' for _ in range(5) ) safe_code = html.escape(code) svg = ( '' '' f'{lines}' f'' f"{safe_code}" ) encoded = base64.b64encode(svg.encode("utf-8")).decode("ascii") return f"data:image/svg+xml;base64,{encoded}" def create_captcha(store: AdminSessionStore, ttl_seconds: int) -> dict[str, object]: captcha_id = uuid4().hex code = _captcha_code() store.create_captcha(captcha_id, code, ttl_seconds) return { "captchaEnabled": True, "captchaId": captcha_id, "image": _captcha_image(code), "expiresIn": ttl_seconds, } def verify_captcha(store: AdminSessionStore, captcha_id: str, code: str) -> bool: return store.consume_captcha(captcha_id, code)