"""Authentication dependencies for FastAPI route protection. Provides dependency functions that verify the admin session cookie and return the authenticated User, or redirect to /login. """ from typing import Optional import structlog from fastapi import Depends, Request from fastapi.responses import RedirectResponse from sqlmodel import Session from app.database import get_db_session from app.models.user import User from app.services.auth_service import AuthService logger = structlog.get_logger(__name__) class NotAuthenticatedError(Exception): """Raised when a request lacks valid authentication.""" # Cookie name for the admin session SESSION_COOKIE_NAME = "session" def get_current_admin_user(request: Request, session: Session = Depends(get_db_session)) -> User: """FastAPI dependency that extracts and validates the admin session. Reads the session cookie, validates the token, and returns the authenticated admin User. Redirects to /login (303) if not authenticated. Args: request: The incoming HTTP request. session: Database session (injected by FastAPI). Returns: The authenticated admin User. Raises: RedirectResponse: 303 redirect to /login if no valid admin session. """ token = request.cookies.get(SESSION_COOKIE_NAME) if not token: raise _login_redirect() secret_key = getattr(request.app.state, "secret_key", "") auth_service = AuthService(session, secret_key=secret_key) user_id = auth_service.validate_session_token(token) if user_id is None: raise _login_redirect() user = session.get(User, user_id) if user is None or not user.is_admin: raise _login_redirect() return user def get_active_profile_id(request: Request) -> Optional[int]: """Extract the active profile ID from the session cookie. The admin selects which user profile to view/log as. This is stored in a separate cookie called 'active_profile_id'. Args: request: The incoming HTTP request. Returns: The active profile user ID, or None if not set. """ profile_id = request.cookies.get("active_profile_id") if profile_id and profile_id.isdigit(): return int(profile_id) return None def _login_redirect(): """Create a redirect exception to the login page. Returns: A NotAuthenticatedError handled by a registered exception handler in main.py that sends a 302 redirect to /login. """ return NotAuthenticatedError()