38c8e35a51
- FIELD_ENCRYPTION_KEY: production требует явный ключ (ImproperlyConfigured при пустом), local — fallback на SECRET_KEY с предупреждением (crypto.py) - MATTERMOST_INTERACTIVE_TOKEN: fail-closed токен для интерактивных кнопок Mattermost (bot_service task-done URL + проверка в mattermost views) - Ужесточение прав API (employees/visit_manager) + тесты (employees, mattermost_connector, visit_manager) - .env.example с описанием переменных; правки шаблонов (index/dislocation/pharmacies) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
33 lines
960 B
Python
33 lines
960 B
Python
import base64
|
|
import hashlib
|
|
import logging
|
|
|
|
from django.conf import settings
|
|
from cryptography.fernet import Fernet
|
|
|
|
logger = logging.getLogger(__name__)
|
|
_warned_fallback = False
|
|
|
|
|
|
def _get_fernet() -> Fernet:
|
|
global _warned_fallback
|
|
key_source = getattr(settings, 'FIELD_ENCRYPTION_KEY', None) or ''
|
|
if not key_source:
|
|
key_source = settings.SECRET_KEY
|
|
if not _warned_fallback:
|
|
logger.warning(
|
|
'FIELD_ENCRYPTION_KEY is not set — falling back to SECRET_KEY. '
|
|
'Set a dedicated key in .env (production requires it).'
|
|
)
|
|
_warned_fallback = True
|
|
derived_key = base64.urlsafe_b64encode(hashlib.sha256(key_source.encode()).digest())
|
|
return Fernet(derived_key)
|
|
|
|
|
|
def encrypt_secret(plain: str) -> str:
|
|
return _get_fernet().encrypt(plain.encode()).decode()
|
|
|
|
|
|
def decrypt_secret(cipher: str) -> str:
|
|
return _get_fernet().decrypt(cipher.encode()).decode()
|