"""Säkerhetsfunktioner: lösenordshashning, JWT-hantering, roller.""" from datetime import datetime, timedelta from typing import Optional, Union from uuid import UUID from jose import jwt, JWTError from passlib.context import CryptContext from app.config import get_settings from app.models import UserRole settings = get_settings() pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def verify_password(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password: str) -> str: return pwd_context.hash(password) def create_access_token(subject: Union[str, UUID], expires_delta: Optional[timedelta] = None) -> str: if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) to_encode = {"exp": expire, "sub": str(subject), "type": "access"} encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) return encoded_jwt def create_refresh_token(subject: Union[str, UUID]) -> str: expire = datetime.utcnow() + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS) to_encode = {"exp": expire, "sub": str(subject), "type": "refresh"} encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) return encoded_jwt def decode_token(token: str) -> Optional[dict]: try: payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) return payload except JWTError: return None # ─── Role-based access helpers ──────────────────────── ROLE_HIERARCHY = { UserRole.VIEWER: 0, UserRole.ANALYST: 1, UserRole.MANAGER: 2, UserRole.ADMIN: 3, } def role_level(role: UserRole) -> int: return ROLE_HIERARCHY.get(role, 0) def can_manage(manager_role: UserRole, target_role: UserRole) -> bool: """Returnerar True om manager_role har högre eller lika nivå som target_role.""" return role_level(manager_role) >= role_level(target_role) def require_role(min_role: UserRole): """Factory för roll-krav. Används i dependency-kedjan.""" from fastapi import HTTPException, status def checker(current_user_role: UserRole): if role_level(current_user_role) < role_level(min_role): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=f"Requires role {min_role.value} or higher", ) return True return checker