Initial commit
This commit is contained in:
75
auth.py
Normal file
75
auth.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hmac
|
||||
import hashlib
|
||||
import os
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
COOKIE_NAME = "voley_admin"
|
||||
|
||||
|
||||
def _get_secret() -> bytes:
|
||||
secret = os.getenv("SECRET_KEY", "").strip()
|
||||
if not secret:
|
||||
# Разрешаем запуск без секрета, но с сильно сниженной безопасностью.
|
||||
# Для реального деплоя SECRET_KEY обязателен.
|
||||
secret = "dev-unsafe-secret"
|
||||
return secret.encode("utf-8")
|
||||
|
||||
|
||||
def _b64url(b: bytes) -> str:
|
||||
return base64.urlsafe_b64encode(b).decode("ascii").rstrip("=")
|
||||
|
||||
|
||||
def _b64url_decode(s: str) -> bytes:
|
||||
pad = "=" * (-len(s) % 4)
|
||||
return base64.urlsafe_b64decode((s + pad).encode("ascii"))
|
||||
|
||||
|
||||
def _sign(payload: bytes) -> str:
|
||||
mac = hmac.new(_get_secret(), payload, hashlib.sha256).digest()
|
||||
return _b64url(mac)
|
||||
|
||||
|
||||
def make_session(ttl_seconds: int = 60 * 60 * 12) -> str:
|
||||
exp = int(time.time()) + int(ttl_seconds)
|
||||
payload = f"exp={exp}".encode("utf-8")
|
||||
sig = _sign(payload)
|
||||
return f"{_b64url(payload)}.{sig}"
|
||||
|
||||
|
||||
def verify_session(token: str) -> bool:
|
||||
try:
|
||||
payload_b64, sig = token.split(".", 1)
|
||||
payload = _b64url_decode(payload_b64)
|
||||
except Exception:
|
||||
return False
|
||||
if not hmac.compare_digest(_sign(payload), sig):
|
||||
return False
|
||||
try:
|
||||
parts = payload.decode("utf-8").split("=", 1)
|
||||
if len(parts) != 2 or parts[0] != "exp":
|
||||
return False
|
||||
exp = int(parts[1])
|
||||
except Exception:
|
||||
return False
|
||||
return time.time() < exp
|
||||
|
||||
|
||||
def check_admin_password(password: str) -> bool:
|
||||
expected = os.getenv("ADMIN_PASSWORD", "")
|
||||
if not expected:
|
||||
# Чтобы админка не осталась открытой по умолчанию.
|
||||
return False
|
||||
return hmac.compare_digest(password or "", expected)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AdminAuth:
|
||||
is_admin: bool
|
||||
token: Optional[str] = None
|
||||
|
||||
Reference in New Issue
Block a user