Files
merica/backend/app/core/auth/dependencies.py
T

83 lines
3.5 KiB
Python
Raw Normal View History

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