assitantchatbot / dependencies.py
aghaai's picture
Initial deployment of Unified Assistant with OpenAI and Hugging Face integration
bd161ec
"""
FastAPI dependencies for authentication and database access.
"""
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
import logging
from database import get_async_db
from models import User, Project, ProjectMember, ProjectRole
from auth import verify_token
from schemas import TokenData
logger = logging.getLogger(__name__)
security = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: AsyncSession = Depends(get_async_db)
) -> User:
"""Get the current authenticated user (async)."""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = verify_token(credentials.credentials)
user_id: str | None = payload.get("sub")
if user_id is None:
raise credentials_exception
token_data = TokenData(user_id=user_id)
except Exception as e:
logger.error(f"Token validation error: {e}")
raise credentials_exception
# Get user from database
user_result = await db.execute(select(User).where(User.id == token_data.user_id))
user = user_result.scalar_one_or_none()
if user is None:
raise credentials_exception
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Inactive user"
)
return user
async def get_current_active_user(current_user: User = Depends(get_current_user)) -> User:
"""Get the current active user (async)."""
if not current_user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
async def check_project_access(
project_id: str,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_async_db),
required_role: Optional[ProjectRole] = None
) -> Project:
"""Check if user has access to a project (async)."""
project_result = await db.execute(
select(Project)
.where(Project.id == project_id)
)
project = project_result.scalar_one_or_none()
if not project:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Project not found"
)
# Check if user is owner
if project.owner_id == current_user.id:
return project
# Check if user is a member
member_result = await db.execute(
select(ProjectMember).where(
ProjectMember.project_id == project_id,
ProjectMember.user_id == current_user.id
)
)
member = member_result.scalar_one_or_none()
if not member:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Access denied to this project"
)
# Check role requirements
if required_role:
if project.owner_id != current_user.id and member.role != required_role:
if required_role == ProjectRole.OWNER:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only project owner can perform this action"
)
return project
async def require_project_owner(
project_id: str,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_async_db)
):
"""Dependency that requires project owner access (async)."""
return await check_project_access(project_id, current_user, db, ProjectRole.OWNER)
async def require_project_editor(
project_id: str,
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_async_db)
):
"""Dependency that requires at least editor access to project (async)."""
return await check_project_access(project_id, current_user, db)