Files
2026-09-16 06:08:37 +02:00

77 lines
2.4 KiB
Python

"""API dependencies: current user, RBAC guards, correlation IDs."""
from __future__ import annotations
import uuid
from datetime import datetime
from typing import Annotated
from fastapi import Depends, HTTPException, Request, status
from sqlalchemy.orm import Session
from app.db import get_db
from app.models import ManagementRole, ManagementSession, ManagementUser
SESSION_COOKIE = "mqtt_mgmt_session"
def _expires_at_aware(mgmt_session: ManagementSession) -> datetime:
"""Normalize expiry to aware UTC (sqlite returns naive datetimes)."""
from datetime import UTC
expires_at = mgmt_session.expires_at
if expires_at.tzinfo is None:
return expires_at.replace(tzinfo=UTC)
return expires_at
def _get_request_session(
request: Request, db: Session
) -> tuple[ManagementUser, ManagementSession]:
token = request.cookies.get(SESSION_COOKIE)
if not token:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "not authenticated")
try:
session_id = uuid.UUID(token)
except ValueError as exc:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "invalid session") from exc
from datetime import UTC, datetime
mgmt_session = db.get(ManagementSession, session_id)
if (
mgmt_session is None
or mgmt_session.revoked
or _expires_at_aware(mgmt_session) < datetime.now(UTC)
):
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "session expired")
user = db.get(ManagementUser, mgmt_session.user_id)
if user is None or not user.enabled:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "user disabled")
return user, mgmt_session
def get_current_user(
request: Request, db: Annotated[Session, Depends(get_db)]
) -> ManagementUser:
user, _ = _get_request_session(request, db)
return user
def require_roles(*roles: ManagementRole):
"""Dependency factory enforcing one of the given management roles."""
def _guard(
user: Annotated[ManagementUser, Depends(get_current_user)],
) -> ManagementUser:
if user.role == ManagementRole.SUPER_ADMIN:
return user
if user.role not in roles:
raise HTTPException(status.HTTP_403_FORBIDDEN, "insufficient role")
return user
return _guard
CurrentUser = Annotated[ManagementUser, Depends(get_current_user)]
DbSession = Annotated[Session, Depends(get_db)]