"""Service secrets — chiffrement Fernet pour credentials en base""" import os import base64 from cryptography.fernet import Fernet from sqlalchemy import text from ..config import SECRET_KEY # Derive une cle Fernet 32 bytes depuis SECRET_KEY _raw = SECRET_KEY.encode()[:32].ljust(32, b'\0') _fernet_key = base64.urlsafe_b64encode(_raw) _fernet = Fernet(_fernet_key) def encrypt(value: str) -> str: return _fernet.encrypt(value.encode()).decode() def decrypt(value: str) -> str: return _fernet.decrypt(value.encode()).decode() def get_secret(db, key: str) -> str | None: """Recupere et dechiffre un secret depuis app_secrets""" row = db.execute(text("SELECT value FROM app_secrets WHERE key = :k"), {"k": key}).fetchone() if not row: return None try: return decrypt(row.value) except Exception: return None def set_secret(db, key: str, value: str, description: str = ""): """Chiffre et stocke un secret dans app_secrets""" enc = encrypt(value) db.execute(text(""" INSERT INTO app_secrets (key, value, description, updated_at) VALUES (:k, :v, :d, now()) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, description = EXCLUDED.description, updated_at = now() """), {"k": key, "v": enc, "d": description}) db.commit() def list_secrets(db): """Liste les cles (sans valeurs) des secrets""" rows = db.execute(text( "SELECT key, description, updated_at FROM app_secrets ORDER BY key" )).fetchall() return rows def init_secrets_from_config(db): """Initialise les secrets depuis config si pas encore en base""" from ..config import QUALYS_URL, QUALYS_USER, QUALYS_PASS defaults = { "qualys_url": (QUALYS_URL, "URL API Qualys"), "qualys_user": (QUALYS_USER, "Utilisateur Qualys"), "qualys_pass": (QUALYS_PASS, "Mot de passe Qualys"), "qualys_proxy": ("http://proxy.sanef.fr:8080", "Proxy Qualys"), } for key, (val, desc) in defaults.items(): if val and not get_secret(db, key): set_secret(db, key, val, desc)