Newer
Older
gn-synapse / app / auth / deps.py
"""Аутентификация через gnexus-auth и гейт админки по ролям.

Токены gnexus-auth — opaque (не JWT), валидация только «call home»:
GET /oauth/userinfo с Bearer-токеном. Отзыв токена и блокировка
пользователя учитываются сервером auth мгновенно. Сам OAuth-поток
(state, PKCE, обмен кода) — в app/api/auth_routes.py через gnexus-gauth.

Доступ к админке — system_role in ("admin", "superadmin").
"""

from fastapi import Depends, HTTPException, Request, status
from gnexus_gauth.dto import AuthenticatedUser

from app.auth.client import get_gauth_client
from app.config import get_settings

ADMIN_ROLES = ("admin", "superadmin")


def get_current_user(
    request: Request,
    settings=Depends(get_settings),
) -> AuthenticatedUser:
    """Валидирует Bearer-токен через userinfo gnexus-auth."""
    auth = request.headers.get("Authorization", "")
    if not auth.startswith("Bearer "):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Требуется Bearer-токен gnexus-auth",
            headers={"WWW-Authenticate": "Bearer"},
        )
    try:
        # TODO: короткий TTL-кэш userinfo по токену (до 60 c) — после появления нагрузки.
        return get_gauth_client().fetch_user(auth.removeprefix("Bearer "))
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Токен невалиден или истёк",
            headers={"WWW-Authenticate": "Bearer"},
        ) from None


def require_admin(
    user: AuthenticatedUser = Depends(get_current_user),
) -> AuthenticatedUser:
    """Гейт админки: system_role admin или выше, остальным — 403."""
    if (user.system_role or "") not in ADMIN_ROLES:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Недостаточно прав: админ-панель доступна роли admin и выше",
        )
    return user