Code Monkey home page Code Monkey logo

Comments (4)

victorouttes avatar victorouttes commented on June 1, 2024 1

When I submit a form in Django Admin, it calls default "clean()" function which is:

  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.
      """
      value = self.to_python(value)
      self.validate(value, model_instance)
      self.run_validators(value)
      return value

The problem is in self.to_python(value). Variable value is the input from form (can be any text by app user).

This call self.to_python(value) is going to encrypted_fields/fields.py:

    def to_python(self, value):
        if value is None or not isinstance(value, str):
            return value
        value = self.f.decrypt(bytes(value, 'utf-8')).decode('utf-8')
        return super(EncryptedFieldMixin, self).to_python(value)

raising error in value = self.f.decrypt(bytes(value, 'utf-8')).decode('utf-8').

So, any Django Admin form input (like "Hello World!") is going to be decrypted in self.f.decrypt(bytes('Hello World!', 'utf-8')). That's the error cause, because decrypt is expecting an encrypted string.

The encrypted_fields to_python(self, value) may be overriding Django's (from django.db.models.fields) to_python(self, value)? Any tip on this?

Thanks in advance.

from django-fernet-encrypted-fields.

StevenMapes avatar StevenMapes commented on June 1, 2024

What format is your token? The InvalidToken is not being raise by this package but it being raised by the main pyca cryptography package -
https://github.com/pyca/cryptography/blob/main/src/cryptography/fernet.py

If you look at that package the line raising the validation is this

        if not data or data[0] != 0x80:
            raise InvalidToken

from django-fernet-encrypted-fields.

StevenMapes avatar StevenMapes commented on June 1, 2024

Here's how I've added a work around for my project

  1. Create a custom Admin class for the model you are using and set that class against the form property of the ModelAdmin or inline form
  2. With the custom form you've created import the EncryptedFieldMixin and overload the _get_validation_exclusions method with the following:
    def _get_validation_exclusions(self):
        """Overload this method to add the encrypted fields to the list of exclusions used by full_clean"""
        exclude = super()._get_validation_exclusions()
        for field in self.fields:
            if isinstance(getattr(self._meta.model, field).field, EncryptedFieldMixin):
                exclude.append(field)
        return exclude

This will add any Encrypted fields on the model that you are using into the exclusion list created by the _post_clean method which then calls the full_clean method on the instance.

You can still add custom validation to the clean_* methods on the form to validate and raise any errors.

I need to check but it may be possible to move this into the EncryptedFieldMixin class against it's full_clean method as well but I have not tested that far down yet.

Here's an example where my model UserProfile has two encrypted fields

admin.py

class UserProfileInline(admin.StackedInline):
    model = UserProfile
    form = UserProfileAdminForm

forms.py

from encrypted_fields.fields import EncryptedFieldMixin


class UserProfileAdminForm(ModelForm):
    class Meta:
        model = UserProfile
        
    def _get_validation_exclusions(self):
        """Overload this method to add the encrypted fields to the list of exclusions used by full_clean"""
        exclude = super()._get_validation_exclusions()
        for field in self.fields:
            if isinstance(getattr(self._meta.model, field).field, EncryptedFieldMixin):
                exclude.append(field)
        return exclude

from django-fernet-encrypted-fields.

StevenMapes avatar StevenMapes commented on June 1, 2024

Okay I tested the model level and this can be fixed by overloading the clean method at the base model field level by simply running the actions of django.db.models.fields.init.clean without calling the to_python

    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

However a better method, IMO, would be to use a temporary instance property which we create within the clean method, then call the super, then remove it from the instance. This means the to_python method can be updated to check for that property and, if it exists, can skip decryption. It's more code but means it's less likely to break with future changes to Django's field's clean method as we know it will run as it's suppose to.

    def to_python(self, value):
        if value is None or not isinstance(value, str) or hasattr(self, '_already_decrypted'):
            return value
        value = self.f.decrypt(bytes(value, 'utf-8')).decode('utf-8')
        return super(EncryptedFieldMixin, self).to_python(value)

    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._already_decrypted = True
        ret = super().clean(value, model_instance)
        del self._already_decrypted
        return ret

I'll fork and create a new PR based on the latter

from django-fernet-encrypted-fields.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.