from typing import List, Optional from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from app.core.auth.models import UserSession from app.core.auth.security import decode_session_token security_scheme = HTTPBearer(auto_error=False) async def get_current_user(credentials: Optional[HTTPAuthorizationCredentials] = Depends(security_scheme)) -> UserSession: """Extract and validate current authenticated user from session token.""" if not credentials or not credentials.credentials: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Otentikasi diperlukan. Silakan login terlebih dahulu.", headers={"WWW-Authenticate": "Bearer"}, ) payload = decode_session_token(credentials.credentials) if not payload: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token sesi tidak valid atau telah kedaluwarsa. Silakan login kembali.", headers={"WWW-Authenticate": "Bearer"}, ) user_data = payload.get("user") if not user_data: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Struktur data sesi tidak valid.", headers={"WWW-Authenticate": "Bearer"}, ) return UserSession(**user_data) async def get_optional_current_user(credentials: Optional[HTTPAuthorizationCredentials] = Depends(security_scheme)) -> Optional[UserSession]: """Extract current user if token is present, otherwise return None.""" if not credentials or not credentials.credentials: return None payload = decode_session_token(credentials.credentials) if not payload or not payload.get("user"): return None try: return UserSession(**payload["user"]) except Exception: return None def require_capabilities(allowed_caps: List[str]): """Enforce that the current user possesses one of the allowed capabilities.""" async def capability_checker(current_user: UserSession = Depends(get_current_user)) -> UserSession: if current_user.capability not in allowed_caps and not current_user.is_admin: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=f"Akses ditolak: Akun Anda memiliki peran '{current_user.capability}'. Tindakan ini memerlukan hak akses '{'/'.join(allowed_caps)}'." ) return current_user return capability_checker # Shortcuts async def require_admin(current_user: UserSession = Depends(get_current_user)) -> UserSession: """Enforce Administrator role (Full access + Settings control).""" if not current_user.is_admin and current_user.capability != "admin": raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Akses ditolak: Menu Pengaturan dan kontrol IAM hanya dapat diakses oleh Administrator / Superadmin." ) return current_user async def require_editor(current_user: UserSession = Depends(get_current_user)) -> UserSession: """Enforce Editor capability (Data Engineer / Full Canvas Access).""" if not current_user.can_edit and not current_user.is_admin: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Akses ditolak: Akun Anda memiliki peran Read-Only (Pimpinan/Auditor). Perubahan atau deployment DAG hanya dapat dilakukan oleh Data Engineer." ) return current_user