Files
2026-03-21 16:41:14 +11:00

40 lines
1.3 KiB
Python

import base64
import hashlib
from cryptography.fernet import Fernet
from django.conf import settings
from django.db import models
def _get_fernet():
"""Derive a Fernet key from Django's SECRET_KEY."""
raw_key = hashlib.sha256(settings.SECRET_KEY.encode()).digest()
return Fernet(base64.urlsafe_b64encode(raw_key))
class EncryptedCharField(models.TextField):
"""
Stores values encrypted at rest using Fernet symmetric encryption.
The encryption key is derived from Django's SECRET_KEY so no extra
secrets management is required — if the SECRET_KEY is set, values
are protected.
From the application's perspective this behaves like a plain text field:
you read/write plaintext; encryption/decryption happens transparently.
"""
def from_db_value(self, value, expression, connection):
if not value:
return value
try:
return _get_fernet().decrypt(value.encode()).decode()
except Exception:
# Gracefully return raw value if decryption fails
# (e.g. migrating plaintext rows that were saved before encryption)
return value
def get_prep_value(self, value):
if not value:
return value
return _get_fernet().encrypt(value.encode()).decode()