Download apps/core/model_fields/encrypted.py from jun2024/fc: direct link, hf CLI and curl.
- Browser
- Download file 3.19 kB
-
https://huggingface.co/spaces/jun2024/fc/resolve/main/apps/core/model_fields/encrypted.py
- Command line
-
hf download hf://spaces/jun2024/fc/apps/core/model_fields/encrypted.py
-
curl -L -o encrypted.py https://huggingface.co/spaces/jun2024/fc/resolve/main/apps/core/model_fields/encrypted.py
3.19 kB
| """Module for encrypted.""" | |
| from __future__ import annotations | |
| import base64 | |
| import logging | |
| from typing import Any | |
| from cryptography.fernet import Fernet, InvalidToken | |
| from django.conf import settings | |
| from django.db import models | |
| logger = logging.getLogger(__name__) | |
| class EncryptedTextField(models.TextField[str, str]): | |
| prefix: str = "enc:v1:" | |
| def __init__(self, *args: Any, **kwargs: Any) -> None: | |
| super().__init__(*args, **kwargs) | |
| def _resolve_keys(self) -> list[bytes]: | |
| primary = getattr(settings, "CREDENTIAL_ENCRYPTION_KEY", None) or getattr( | |
| settings, "SCRAPER_ENCRYPTION_KEY", None | |
| ) | |
| previous = getattr(settings, "CREDENTIAL_ENCRYPTION_KEY_PREVIOUS", None) or getattr( | |
| settings, "SCRAPER_ENCRYPTION_KEY_PREVIOUS", None | |
| ) | |
| keys: list[bytes] = [] | |
| for k in (primary, previous): | |
| if not k: | |
| continue | |
| if isinstance(k, str): | |
| k = k.encode() | |
| keys.append(k) | |
| return keys | |
| def _get_ciphers(self) -> list[Fernet]: | |
| keys = self._resolve_keys() | |
| if not keys: | |
| raise RuntimeError("missing encryption key") | |
| return [Fernet(k) for k in keys] | |
| def _encrypt(self, plain_text: str) -> str: | |
| cipher = self._get_ciphers()[0] | |
| token = cipher.encrypt(plain_text.encode()) | |
| return f"{self.prefix}{base64.urlsafe_b64encode(token).decode()}" | |
| def _decrypt(self, encrypted_value: str) -> str: | |
| token_b64 = encrypted_value[len(self.prefix) :] | |
| token = base64.urlsafe_b64decode(token_b64.encode()) | |
| last_error: Exception | None = None | |
| for cipher in self._get_ciphers(): | |
| try: | |
| decrypted: bytes = cipher.decrypt(token) | |
| return decrypted.decode() | |
| except Exception as e: | |
| logger.debug("解密尝试失败,继续下一个密钥: %s", e) | |
| last_error = e | |
| continue | |
| if last_error: | |
| raise last_error | |
| raise InvalidToken() | |
| def get_prep_value(self, value: Any) -> Any: | |
| plain_value = super().get_prep_value(value) | |
| if plain_value is None or plain_value == "": | |
| return plain_value | |
| if isinstance(plain_value, str) and plain_value.startswith(self.prefix): | |
| return plain_value | |
| if not isinstance(plain_value, str): | |
| plain_value = str(plain_value) | |
| return self._encrypt(plain_value) | |
| def to_python(self, value: Any) -> Any: | |
| if value is None or value == "": | |
| return value | |
| if not isinstance(value, str): | |
| value = str(value) | |
| if not value.startswith(self.prefix): | |
| return value | |
| try: | |
| return self._decrypt(value) | |
| except (InvalidToken, ValueError): | |
| if getattr(settings, "DEBUG", False): | |
| logger.warning("解密失败,返回原始加密值(DEBUG 模式): %s...", value[:20]) | |
| return value | |
| raise | |
| def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: | |
| return self.to_python(value) | |