Files
MapMil/centers/analytics/api/app/auth.py
T
gitrusprusandCursor 8ab606747f Add public map with JWT admin login and protect admin APIs.
Keep map reads open; gate admin UI/nav and object mutations behind env-based admin credentials, and default parser batch limit to 10.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 23:04:21 +03:00

75 lines
2.1 KiB
Python

"""Admin auth: single env-based user + JWT bearer tokens."""
from __future__ import annotations
import hmac
import os
import time
from typing import Any
import jwt
from fastapi import HTTPException
ALGORITHM = "HS256"
DEFAULT_TTL_SECONDS = 60 * 60 * 24 # 24h
def _admin_user() -> str:
return os.getenv("ADMIN_USER", "admin").strip() or "admin"
def _admin_password() -> str:
return os.getenv("ADMIN_PASSWORD", "").strip()
def _jwt_secret() -> str:
secret = os.getenv("ADMIN_JWT_SECRET", "").strip()
if not secret:
# Dev fallback: derive from password so local stacks boot without extra secret.
password = _admin_password()
if not password:
raise HTTPException(
status_code=503,
detail="ADMIN_PASSWORD is not configured",
)
return f"mapmil-dev:{password}"
return secret
def admin_credentials_configured() -> bool:
return bool(_admin_password())
def verify_credentials(username: str, password: str) -> bool:
expected_user = _admin_user()
expected_password = _admin_password()
if not expected_password:
return False
user_ok = hmac.compare_digest(username.strip(), expected_user)
pass_ok = hmac.compare_digest(password, expected_password)
return user_ok and pass_ok
def create_access_token(*, username: str, ttl_seconds: int = DEFAULT_TTL_SECONDS) -> str:
now = int(time.time())
payload: dict[str, Any] = {
"sub": username,
"role": "admin",
"iat": now,
"exp": now + ttl_seconds,
}
return jwt.encode(payload, _jwt_secret(), algorithm=ALGORITHM)
def decode_access_token(token: str) -> dict[str, Any]:
try:
payload = jwt.decode(token, _jwt_secret(), algorithms=[ALGORITHM])
except jwt.ExpiredSignatureError as exc:
raise HTTPException(status_code=401, detail="Token expired") from exc
except jwt.InvalidTokenError as exc:
raise HTTPException(status_code=401, detail="Invalid token") from exc
if payload.get("role") != "admin" or not payload.get("sub"):
raise HTTPException(status_code=401, detail="Invalid token")
return payload