5d06d4d3ce
Co-authored-by: Cursor <cursoragent@cursor.com>
71 lines
2.7 KiB
Python
71 lines
2.7 KiB
Python
import logging
|
|
|
|
from django.db import models
|
|
|
|
from core.utils.crypto import encrypt_secret, decrypt_secret
|
|
from core.utils.validators import (
|
|
normalize_ukrainian_phone,
|
|
phone_input_attrs,
|
|
validate_ukrainian_phone,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class UkrainianPhoneField(models.CharField):
|
|
"""Украинский телефон: в БД всегда +380XXXXXXXXX, в форме — маска."""
|
|
|
|
default_validators = [validate_ukrainian_phone]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
kwargs.setdefault('max_length', 20)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def to_python(self, value):
|
|
value = super().to_python(value)
|
|
if value in (None, ''):
|
|
return value
|
|
return normalize_ukrainian_phone(value)
|
|
|
|
def get_prep_value(self, value):
|
|
value = super().get_prep_value(value)
|
|
if value in (None, ''):
|
|
return value
|
|
return normalize_ukrainian_phone(value)
|
|
|
|
def formfield(self, **kwargs):
|
|
from django import forms
|
|
kwargs.setdefault('widget', forms.TextInput(attrs=phone_input_attrs()))
|
|
return super().formfield(**kwargs)
|
|
|
|
|
|
class EncryptedCharField(models.CharField):
|
|
"""CharField, который хранит значение в БД в зашифрованном виде (Fernet).
|
|
|
|
Для приложения поле ведёт себя как обычная строка: чтение отдаёт открытый
|
|
текст, запись сохраняет шифртекст. Шифрование выполняется на границе с БД,
|
|
поэтому читатели (`cfg.password`, `cfg.token`) и админка изменений не требуют.
|
|
|
|
Шифртекст длиннее исходного текста (~1.4×) плюс постоянный оверхед Fernet,
|
|
поэтому колонку нужно объявлять с запасом по `max_length`.
|
|
"""
|
|
|
|
def get_db_prep_save(self, value, connection):
|
|
value = super().get_db_prep_save(value, connection)
|
|
if value in (None, ''):
|
|
return value
|
|
return encrypt_secret(value)
|
|
|
|
def from_db_value(self, value, expression, connection):
|
|
if value in (None, ''):
|
|
return value
|
|
try:
|
|
return decrypt_secret(value)
|
|
except Exception:
|
|
# Значение записано до включения шифрования — вернуть как есть.
|
|
logger.warning(
|
|
'EncryptedCharField: decrypt failed, returning raw DB value '
|
|
'(check FIELD_ENCRYPTION_KEY rotation)'
|
|
)
|
|
return value
|