"""FastAPI dependencies: auth, db, tenant context, RBAC.""" from __future__ import annotations import uuid from typing import Any import redis.asyncio as aioredis from fastapi import Depends, HTTPException, Request, status from sqlalchemy.ext.asyncio import AsyncSession from app.config import get_settings from app.core.auth import get_redis, get_session_data from app.core.db import get_db, set_tenant_context async def get_redis_dep() -> aioredis.Redis: """FastAPI dependency for Redis client.""" return get_redis() async def get_current_user( request: Request, db: AsyncSession = Depends(get_db), redis: aioredis.Redis = Depends(get_redis_dep), ) -> dict[str, Any]: """Get the current authenticated user from session cookie. Returns session data dict with user_id, tenant_id, email, name, role. """ settings = get_settings() session_id = request.cookies.get(settings.session_cookie_name) if not session_id: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail={"detail": "Not authenticated", "code": "not_authenticated"}, ) session_data = await get_session_data(redis, session_id) if session_data is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail={"detail": "Session expired or invalid", "code": "session_invalid"}, ) if not session_data.get("is_active", True): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail={"detail": "User account deactivated", "code": "user_inactive"}, ) # Set RLS tenant context tenant_id = uuid.UUID(session_data["tenant_id"]) await set_tenant_context(db, tenant_id) return session_data async def require_admin( current_user: dict[str, Any] = Depends(get_current_user), ) -> dict[str, Any]: """Require admin role.""" if current_user.get("role") != "admin": raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail={"detail": "Admin access required", "code": "forbidden"}, ) return current_user async def require_write( current_user: dict[str, Any] = Depends(get_current_user), ) -> dict[str, Any]: """Require write permission (admin or editor).""" role = current_user.get("role", "viewer") if role not in ("admin", "editor"): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail={"detail": "Write access required", "code": "forbidden"}, ) return current_user async def get_tenant_id( current_user: dict[str, Any] = Depends(get_current_user), ) -> uuid.UUID: """Extract tenant_id from current user session.""" return uuid.UUID(current_user["tenant_id"]) async def get_current_user_id( current_user: dict[str, Any] = Depends(get_current_user), ) -> uuid.UUID: """Extract user_id from current user session.""" return uuid.UUID(current_user["user_id"])