File size: 1,567 Bytes
b4f2238 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | """Module for secret codec."""
from __future__ import annotations
import base64
from dataclasses import dataclass
from cryptography.fernet import Fernet, InvalidToken
from django.conf import settings
@dataclass(frozen=True)
class SecretCodec:
prefix: str = "enc:v1:"
def _get_cipher(self) -> Fernet:
key = getattr(settings, "CREDENTIAL_ENCRYPTION_KEY", None) or getattr(settings, "SCRAPER_ENCRYPTION_KEY", None)
if not key:
raise RuntimeError("missing encryption key")
if isinstance(key, str):
key = key.encode()
return Fernet(key)
def is_encrypted(self, value: str | None) -> bool:
return bool(value) and str(value).startswith(self.prefix)
def encrypt(self, plain_text: str) -> str:
if plain_text.startswith(self.prefix):
return plain_text
token = self._get_cipher().encrypt(plain_text.encode())
return f"{self.prefix}{base64.urlsafe_b64encode(token).decode()}"
def decrypt(self, encrypted_value: str) -> str:
if not encrypted_value.startswith(self.prefix):
return encrypted_value
token_b64 = encrypted_value[len(self.prefix) :]
token = base64.urlsafe_b64decode(token_b64.encode())
plain = self._get_cipher().decrypt(token)
return plain.decode()
def try_decrypt(self, value: str) -> str:
try:
return self.decrypt(value)
except (InvalidToken, ValueError):
if getattr(settings, "DEBUG", False):
return value
raise
|