from typing import Annotated from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError from sqlalchemy import select from sqlalchemy.orm import Session from app.core.security import decode_token from app.db.session import get_db from app.models.domain import User oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") DbSession = Annotated[Session, Depends(get_db)] def current_user(db: DbSession, token: Annotated[str, Depends(oauth2_scheme)]) -> User: try: payload = decode_token(token) except JWTError as exc: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") from exc if payload.get("typ") != "access": raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token type") user = db.scalar(select(User).where(User.id == payload["sub"], User.is_active.is_(True))) if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Inactive or missing user") return user CurrentUser = Annotated[User, Depends(current_user)] def require_permission(permission: str): def dependency(user: CurrentUser) -> User: permissions = {permission for role in user.roles for permission in role.permissions} if "*" not in permissions and permission not in permissions: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Missing permission") return user return dependency