Source code for CryptographicFields.fields

from typing import Any
from django.db import models
from django.core import exceptions, validators
from django.db.models.fields import PositiveIntegerRelDbTypeMixin
from .cryptography import encrypt, decrypt
from ast import literal_eval
import datetime
from django import forms
from django.utils import timezone
from uuid import UUID
from django.utils.translation import gettext_lazy as _
from django.db.models.lookups import StartsWith as StartWith, FieldGetDbPrepValueMixin
"""
to_python() make validations & checks type of the data
get_db_prep_value() encrypts the data
from_db_value() decrypts the data returned from the db
pre_save() generates date ,datetime,time for the respestive fields
get_db_prep_save() saves the value into db
"""


[docs]class StartsWith(FieldGetDbPrepValueMixin, StartWith): pass
[docs]class CharField(models.CharField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return decrypt(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(value)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class BooleanField(models.BooleanField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return literal_eval(decrypt(value))
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class DateField(models.DateField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): # Casts dates into the format expected by the backend if not prepared: value = self.get_prep_value(value) return encrypt(connection.ops.adapt_datefield_value(value))
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: try: return datetime.date.fromisoformat(decrypt(value)) except AttributeError: from timestring import Date return Date(decrypt(value)).date.date()
[docs] def pre_save(self, model_instance, add): if self.auto_now or (self.auto_now_add and add): value = datetime.date.today() setattr(model_instance, self.attname, value) return value else: return super().pre_save(model_instance, add)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class DateTimeField(models.DateTimeField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return super().get_prep_value(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): # Casts dates into the format expected by the backend if not prepared: value = self.get_prep_value(value) return encrypt(connection.ops.adapt_datetimefield_value(value))
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: try: return datetime.datetime.fromisoformat(decrypt(value)) except AttributeError: from timestring import Date return Date(decrypt(value)).date
[docs] def pre_save(self, model_instance, add): if self.auto_now or (self.auto_now_add and add): value = timezone.now() setattr(model_instance, self.attname, value) return value else: return super().pre_save(model_instance, add)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class TimeField(models.TimeField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): # Casts dates into the format expected by the backend if not prepared: value = self.get_prep_value(value) return encrypt(connection.ops.adapt_timefield_value(value))
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: try: return datetime.time.fromisoformat(decrypt(value)) except AttributeError: from timestring import Date return Date(decrypt(value)).date.time()
[docs] def pre_save(self, model_instance, add): if self.auto_now or (self.auto_now_add and add): value = datetime.datetime.now().time() setattr(model_instance, self.attname, value) return value else: return super().pre_save(model_instance, add)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class DecimalField(models.DecimalField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return super().to_python(value)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def get_db_prep_save(self, value, connection,): return encrypt(connection.ops.adapt_decimalfield_value(self.get_prep_value(value), self.max_digits, self.decimal_places))
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return float(decrypt(value))
# return value
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class EmailField(CharField): default_validators = [validators.validate_email] description = _("Email address") def __init__(self, *args, **kwargs): # max_length=254 to be compliant with RFCs 3696 and 5321 kwargs.setdefault('max_length', 254) super().__init__(*args, **kwargs)
[docs] def formfield(self, **kwargs): # As with CharField, this will cause email validation to be performed # twice. return super().formfield(**{ 'form_class': forms.EmailField, **kwargs, })
[docs]class FloatField(models.FloatField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return float(decrypt(value))
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(value)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class IntegerField(models.IntegerField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return int(decrypt(value))
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(value)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class BigIntegerField(IntegerField): error_messages = { 'invalid': _('“%(value)s” value must be less than %(max) & greater than %(min).'), } description = _("Big (8 byte) integer") MAX_BIGINT = 9223372036854775807
[docs] def to_python(self, value: Any) -> Any: value = self.clean(super().to_python(value), None) if value < (-self.MAX_BIGINT-1) or value > self.MAX_BIGINT: raise exceptions.ValidationError(self.error_messages['invalid'], code='invalid', params={'value': value, 'max': self.MAX_BIGINT, 'min': (-self.MAX_BIGINT-1)}) return value
[docs] def formfield(self, **kwargs): return super().formfield(**{ 'min_value': -self.MAX_BIGINT - 1, 'max_value': self.MAX_BIGINT, **kwargs, })
[docs]class GenericIPAddressField(models.GenericIPAddressField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(connection.ops.adapt_ipaddressfield_value(value))
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return decrypt(value)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class PositiveBigIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): description = _('Positive big integer') error_messages = { 'invalid': _('“%(value)s” value must be less than greater than %(min).'), }
[docs] def to_python(self, value: Any) -> Any: value = self.clean(super().to_python(value), None) if value < 0: raise exceptions.ValidationError(self.error_messages['invalid'], code='invalid', params={'value': value, 'min': 0}) return value
[docs] def formfield(self, **kwargs): return super().formfield(**{ 'min_value': 0, **kwargs, })
[docs]class PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): description = _("Positive integer") error_messages = { 'invalid': _('“%(value)s” value must be less than greater than %(min).'), }
[docs] def to_python(self, value: Any) -> Any: value = self.clean(super().to_python(value), None) if value < 0: raise exceptions.ValidationError(self.error_messages['invalid'], code='invalid', params={'value': value, 'min': 0}) return value
[docs] def formfield(self, **kwargs): return super().formfield(**{ 'min_value': 0, **kwargs, })
[docs]class PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField): description = _("Positive small integer") error_messages = { 'invalid': _('“%(value)s” value must be less than greater than %(min).'), }
[docs] def to_python(self, value: Any) -> Any: value = self.clean(super().to_python(value), None) if value < 0: raise exceptions.ValidationError(self.error_messages['invalid'], code='invalid', params={'value': value, 'min': 0}) return value
[docs] def formfield(self, **kwargs): return super().formfield(**{ 'min_value': 0, **kwargs, })
[docs]class SlugField(CharField): default_validators = [validators.validate_slug] description = _("Slug (up to %(max_length)s)") def __init__(self, *args, max_length=50, db_index=True, allow_unicode=False, **kwargs): self.allow_unicode = allow_unicode if self.allow_unicode: self.default_validators = [validators.validate_unicode_slug] super().__init__(*args, max_length=max_length, db_index=db_index, **kwargs)
[docs] def deconstruct(self): name, path, args, kwargs = super().deconstruct() if kwargs.get("max_length") == 50: del kwargs['max_length'] if self.db_index is False: kwargs['db_index'] = False else: del kwargs['db_index'] if self.allow_unicode is not False: kwargs['allow_unicode'] = self.allow_unicode return name, path, args, kwargs
[docs] def get_internal_type(self): return "SlugField"
[docs] def formfield(self, **kwargs): return super().formfield(**{ 'form_class': forms.SlugField, 'allow_unicode': self.allow_unicode, **kwargs, })
[docs]class SmallIntegerField(IntegerField): description = _("Small integer")
[docs]class TextField(models.TextField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return decrypt(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(value)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class URLField(CharField): default_validators = [validators.URLValidator()] description = _("URL") def __init__(self, verbose_name=None, name=None, **kwargs): kwargs.setdefault('max_length', 200) super().__init__(verbose_name, name, **kwargs)
[docs] def deconstruct(self): name, path, args, kwargs = super().deconstruct() if kwargs.get("max_length") == 200: del kwargs['max_length'] return name, path, args, kwargs
[docs] def formfield(self, **kwargs): # As with CharField, this will cause URL validation to be performed # twice. return super().formfield(**{ 'form_class': forms.URLField, **kwargs, })
[docs]class BinaryField(models.BinaryField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: if isinstance(value, str): value = bytes(value, "UTF-8") elif isinstance(value, memoryview): value = bytes(value) value = self.clean(value, None) return value
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return bytes.fromhex(decrypt(value))
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(bytes(connection.Database.Binary(value)).hex())
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class UUIDField(models.UUIDField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return UUID(hex=decrypt(value))
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(value.hex)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
[docs]class FilePathField(models.FilePathField):
[docs] def get_internal_type(self) -> str: return "TextField"
[docs] def to_python(self, value: Any) -> Any: return self.clean(super().to_python(value), None)
[docs] def get_prep_value(self, value: Any) -> Any: return self.to_python(value)
[docs] def from_db_value(self, value: Any, expression: Any, connection: Any) -> Any: return decrypt(value)
[docs] def get_db_prep_value(self, value, connection, prepared=False): if not prepared: value = self.get_prep_value(value) return encrypt(value)
[docs] def clean(self, value, model_instance): """ Convert the value's type and run validation. Validation errors from to_python() and validate() are propagated. Return the correct value if no error is raised. """ self.validate(value, model_instance) self.run_validators(value) return value
CharField.register_lookup(StartsWith) BooleanField.register_lookup(StartsWith) DateField.register_lookup(StartsWith) DateTimeField.register_lookup(StartsWith) EmailField.register_lookup(StartsWith) GenericIPAddressField.register_lookup(StartsWith) SlugField.register_lookup(StartsWith) TextField.register_lookup(StartsWith) URLField.register_lookup(StartsWith) BinaryField.register_lookup(StartsWith) UUIDField.register_lookup(StartsWith) FilePathField.register_lookup(StartsWith) DateField.register_lookup(StartsWith, lookup_name="date") DateTimeField.register_lookup(StartsWith, lookup_name="date") TimeField.register_lookup(StartsWith, lookup_name="time")