59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
import secrets
|
||
|
|
import time
|
||
|
|
from collections import OrderedDict
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
id_label = "matrixUserId"
|
||
|
|
|
||
|
|
_pairing_codes: OrderedDict[str, tuple[str, float]] = OrderedDict()
|
||
|
|
_PAIRING_MAX_SIZE = 10000
|
||
|
|
_PAIRING_TTL_SECONDS = 600
|
||
|
|
|
||
|
|
|
||
|
|
async def generate_code(peer_id: str, config: dict = None, account_id: str = None) -> str:
|
||
|
|
alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
||
|
|
code = "".join(secrets.choice(alphabet) for _ in range(8))
|
||
|
|
_evict_expired()
|
||
|
|
_pairing_codes[peer_id] = (code, time.monotonic() + _PAIRING_TTL_SECONDS)
|
||
|
|
if len(_pairing_codes) > _PAIRING_MAX_SIZE:
|
||
|
|
_pairing_codes.popitem(last=False)
|
||
|
|
logger.info("Generated pairing code for peer %s", peer_id)
|
||
|
|
return code
|
||
|
|
|
||
|
|
|
||
|
|
async def verify_code(peer_id: str, code: str, config: dict = None, account_id: str = None) -> bool:
|
||
|
|
_evict_expired()
|
||
|
|
stored = _pairing_codes.get(peer_id)
|
||
|
|
if stored is None:
|
||
|
|
logger.info("Pairing verify failed: no code for peer %s", peer_id)
|
||
|
|
return False
|
||
|
|
stored_code, expires_at = stored
|
||
|
|
if time.monotonic() > expires_at:
|
||
|
|
del _pairing_codes[peer_id]
|
||
|
|
logger.info("Pairing verify failed: code expired for peer %s", peer_id)
|
||
|
|
return False
|
||
|
|
if code.upper() != stored_code:
|
||
|
|
logger.info("Pairing verify failed: code mismatch for peer %s", peer_id)
|
||
|
|
return False
|
||
|
|
del _pairing_codes[peer_id]
|
||
|
|
logger.info("Pairing verified for peer %s", peer_id)
|
||
|
|
return True
|
||
|
|
|
||
|
|
|
||
|
|
def _evict_expired() -> None:
|
||
|
|
now = time.monotonic()
|
||
|
|
expired = [k for k, (_, exp) in _pairing_codes.items() if now > exp]
|
||
|
|
for k in expired:
|
||
|
|
del _pairing_codes[k]
|
||
|
|
|
||
|
|
|
||
|
|
def normalize_allow_entry(entry: str) -> str:
|
||
|
|
return entry.strip().lower()
|
||
|
|
|
||
|
|
|
||
|
|
async def notify_approval(config: dict, peer_id: str, account_id: str | None = None) -> None:
|
||
|
|
logger.info("Matrix pairing approved for %s", peer_id)
|