"""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)]