import jwt from fastapi import Depends, HTTPException from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy.ext.asyncio import AsyncSession from app.core.database.connection import get_session from app.core.database.repository import TrainingRepository from app.core.database.token_repository import RefreshTokenRepository from app.core.database.user_repository import UserRepository from app.core.dto.user import UserDTO from app.core.security import decode_access_token # auto_error=False: a missing header gets the same 401 as a bad token bearer_scheme = HTTPBearer(auto_error=False) def get_training_repository( session: AsyncSession = Depends(get_session), ) -> TrainingRepository: return TrainingRepository(session) def get_user_repository( session: AsyncSession = Depends(get_session), ) -> UserRepository: return UserRepository(session) def get_token_repository( session: AsyncSession = Depends(get_session), ) -> RefreshTokenRepository: return RefreshTokenRepository(session) async def get_current_user( credentials: HTTPAuthorizationCredentials | None = Depends(bearer_scheme), repository: UserRepository = Depends(get_user_repository), ) -> UserDTO: """The user of the access token. The user is read from the database on every request, so a deactivated user loses access at once, not when the token expires.""" unauthorized = HTTPException( status_code=401, detail="Not authenticated", headers={"WWW-Authenticate": "Bearer"}, ) if credentials is None: raise unauthorized try: user_id = int(decode_access_token(credentials.credentials)["sub"]) except (jwt.PyJWTError, ValueError): raise unauthorized user = await repository.get(user_id) if user is None or not user.is_active: raise unauthorized return user async def require_superuser(user: UserDTO = Depends(get_current_user)) -> UserDTO: if not user.is_superuser: raise HTTPException(status_code=403, detail="Superuser only") return user