feat(phase5): integracion auth hibrida, email dinamico y moodle 4.1 backend con tolerancia a fallos

This commit is contained in:
2026-09-23 13:34:59 -03:00
parent 7ad4e3f099
commit 1aae432bba
22 changed files with 2993 additions and 49 deletions
+46
View File
@@ -0,0 +1,46 @@
import base64
import hashlib
from cryptography.fernet import Fernet
from flask import current_app
class CryptoService:
"""
Servicio de cifrado simétrico seguro para contraseñas y tokens sensibles
almacenados en la base de datos (credenciales SMTP, Client Secrets, Moodle Tokens).
"""
@staticmethod
def _get_fernet() -> Fernet:
# Derivar clave válida para Fernet (32 bytes urlsafe base64) desde SECRET_KEY
try:
secret = current_app.config.get('SECRET_KEY', 'default-unicaba-edu-space-secret-key-32b!')
except RuntimeError:
secret = 'default-unicaba-edu-space-secret-key-32b!'
# Hash SHA-256 para obtener 32 bytes y codificar en base64 seguro para URL
key = base64.urlsafe_b64encode(hashlib.sha256(secret.encode('utf-8')).digest())
return Fernet(key)
@classmethod
def encrypt(cls, plain_text: str) -> str:
"""Cifra un texto plano y retorna el string cifrado."""
if not plain_text:
return ''
fernet = cls._get_fernet()
encrypted_bytes = fernet.encrypt(plain_text.encode('utf-8'))
return encrypted_bytes.decode('utf-8')
@classmethod
def decrypt(cls, cipher_text: str) -> str:
"""Descifra un texto cifrado y retorna el texto original. Si falla, retorna vacío."""
if not cipher_text:
return ''
try:
fernet = cls._get_fernet()
decrypted_bytes = fernet.decrypt(cipher_text.encode('utf-8'))
return decrypted_bytes.decode('utf-8')
except Exception:
# Si no era un texto cifrado con Fernet o fue alterado, retornar el texto tal cual o vacío
return cipher_text
crypto_service = CryptoService()