65 lines
2.2 KiB
Python
65 lines
2.2 KiB
Python
"""Shared API dependencies: current user, admin guard, db session, settings."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
from typing import Any
|
|
|
|
from fastapi import Depends, HTTPException, status
|
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.security import decode_token
|
|
from app.core.settings_service import get_all_settings
|
|
from app.db import get_db
|
|
from app.models import User
|
|
|
|
_bearer = HTTPBearer(auto_error=False)
|
|
|
|
|
|
async def get_current_user(
|
|
creds: HTTPAuthorizationCredentials | None = Depends(_bearer),
|
|
db: AsyncSession = Depends(get_db),
|
|
) -> User:
|
|
"""Resolve the JWT bearer token to a User row.
|
|
|
|
Raises 401 on missing/invalid/expired token.
|
|
"""
|
|
if creds is None or creds.scheme.lower() != "bearer":
|
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Missing bearer token")
|
|
try:
|
|
payload = decode_token(creds.credentials)
|
|
except Exception as e: # noqa: BLE001
|
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED, f"Invalid token: {e}")
|
|
if payload.get("type") != "access":
|
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Wrong token type")
|
|
user_id_str = payload.get("sub")
|
|
if not user_id_str:
|
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Token missing sub")
|
|
try:
|
|
user_id = uuid.UUID(user_id_str)
|
|
except ValueError:
|
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid user id in token")
|
|
|
|
user = (
|
|
await db.execute(select(User).where(User.id == user_id))
|
|
).scalar_one_or_none()
|
|
if user is None:
|
|
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "User not found")
|
|
if not user.is_active:
|
|
raise HTTPException(status.HTTP_403_FORBIDDEN, "Account disabled")
|
|
return user
|
|
|
|
|
|
async def require_admin(user: User = Depends(get_current_user)) -> User:
|
|
"""Require an admin user."""
|
|
if not user.is_admin:
|
|
raise HTTPException(status.HTTP_403_FORBIDDEN, "Admin only")
|
|
return user
|
|
|
|
|
|
async def get_settings_dict(db: AsyncSession = Depends(get_db)) -> dict[str, Any]:
|
|
"""FastAPI dependency: returns the full settings dict."""
|
|
return await get_all_settings(db)
|