from typing import Generator from fastapi import Depends, HTTPException, status, Request from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from sqlalchemy.orm import Session from app.db.session import SessionLocal from app.core.security import verify_token from app.models.user import User, UserRole from app.models.tenant import Organization from app.services.audit import AuditService security = HTTPBearer() def get_db() -> Generator: try: db = SessionLocal() yield db finally: db.close() async def get_current_user( request: Request, credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db) ) -> User: credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) payload = verify_token(credentials.credentials) if payload is None: raise credentials_exception user_id: int = payload.get("sub") if user_id is None: raise credentials_exception user = db.query(User).filter(User.id == user_id).first() if user is None: raise credentials_exception if not user.is_active: raise HTTPException(status_code=400, detail="Inactive user") # Log user activity audit_service = AuditService(db) audit_service.log_user_activity( user=user, action="view", resource_type="authentication", ip_address=request.client.host, user_agent=request.headers.get("user-agent") ) return user async def get_current_active_user(current_user: User = Depends(get_current_user)) -> User: if not current_user.is_active: raise HTTPException(status_code=400, detail="Inactive user") return current_user def require_roles(allowed_roles: list[UserRole]): def role_checker(current_user: User = Depends(get_current_active_user)) -> User: if current_user.role not in allowed_roles: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions" ) return current_user return role_checker async def get_current_organization( current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db) ) -> Organization: organization = db.query(Organization).filter( Organization.id == current_user.organization_id ).first() if not organization: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Organization not found" ) if not organization.is_active: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Organization is not active" ) return organization def get_tenant_db(organization: Organization = Depends(get_current_organization)): """Tenant isolation decorator - ensures queries are scoped to the current organization""" def tenant_filter(db: Session = Depends(get_db)): return db, organization.id return tenant_filter