2026-01-25 18:01:48 +01:00

79 lines
2.1 KiB
Python

"""
Security utilities: JWT, password hashing, encryption
CRITICAL: Handles sensitive credentials
"""
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from cryptography.fernet import Fernet
import os
from app.core.config import get_settings
settings = get_settings()
# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# SSH credential encryption (for in-memory use only)
# WARNING: Never store the cipher key in plaintext in production
# Use AWS KMS, HashiCorp Vault, or similar
ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY", Fernet.generate_key())
cipher_suite = Fernet(ENCRYPTION_KEY)
def hash_password(password: str) -> str:
"""Hash password with bcrypt"""
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verify password against hash"""
return pwd_context.verify(plain_password, hashed_password)
def create_access_token(
data: dict,
expires_delta: Optional[timedelta] = None
) -> str:
"""Create JWT access token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=settings.access_token_expire_minutes
)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(
to_encode,
settings.secret_key,
algorithm=settings.algorithm
)
return encoded_jwt
def decrypt_credential(encrypted: str) -> str:
"""
Decrypt SSH credential from storage.
WARNING: Credentials should only be decrypted for immediate use.
"""
try:
decrypted = cipher_suite.decrypt(encrypted.encode())
return decrypted.decode()
except Exception:
raise ValueError("Failed to decrypt credential")
def encrypt_credential(credential: str) -> str:
"""
Encrypt SSH credential for storage.
Use only if absolutely necessary. Prefer not storing passwords.
"""
encrypted = cipher_suite.encrypt(credential.encode())
return encrypted.decode()