"""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 import select 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 from app.models.user import User 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"])