web-dev-qa-db-fra.com

intégrer Django validateurs de mot de passe avec Django reste framework validate_password

J'essaie d'intégrer validateurs Django 1.9 avec Django reste des sérialiseurs de framework. Mais le "utilisateur" sérialisé (de Django reste framework) n'est pas compatible avec les validateurs Django.

Voici le serializers.py

import Django.contrib.auth.password_validation as validators
from rest_framework import serializers

    class RegisterUserSerializer(serializers.ModelSerializer):

        password = serializers.CharField(style={'input_type': 'password'}, write_only=True)

        class Meta:
            model = User
            fields = ('id', 'username', 'email, 'password')

        def validate_password(self, data):
            validators.validate_password(password=data, user=User)
            return data

        def create(self, validated_data):
            user = User.objects.create_user(**validated_data)
            user.is_active = False
            user.save()
            return user

J'ai réussi à obtenir des valeurs MinimumLengthValidator et NumericPasswordValidator correctes, car les deux fonctions valident n'utilisent pas "utilisateur" dans la validation. Le code source est ici

Extrait de Django code source:

def validate(self, password, user=None):
        if password.isdigit():
            raise ValidationError(
                _("This password is entirely numeric."),
                code='password_entirely_numeric',
            )

Pour d'autres validateurs comme UserAttributeSimilarityValidator, la fonction utilise un autre argument 'user' dans la validation ('user' est Django User model, si je ne me trompe pas)

Extrait de Django code source:

 def validate(self, password, user=None):
        if not user:
            return

        for attribute_name in self.user_attributes:
            value = getattr(user, attribute_name, None)

Comment puis-je changer un utilisateur sérialisé en ce que Django (UserAttributeSimilarityValidator) peuvent voir

Extrait de Django code source:

def validate(self, password, user=None):
        if not user:
            return

        for attribute_name in self.user_attributes:
            value = getattr(user, attribute_name, None)
            if not value or not isinstance(value, string_types):
                continue

Modifier

Django Rest Framework peut obtenir toute la validation de mot de passe intégrée de Django (mais c'est comme un hack). Voici un problème:

La validationError est comme ça

[ValidationError (['Ce mot de passe est trop court. Il doit contenir au moins 8 caractères.']), ValidationError (['Ce mot de passe est entièrement numérique.'])]

La validation ne contient pas de champ. Django reste le framework le voit comme

{
    "non_field_errors": [
        "This password is too short. It must contain at least 8 characters.",
        "This password is entirely numeric."
    ]
}

Comment puis-je injecter un champ à raise ValidationError

17
momokjaaaaa

Comme vous l'avez mentionné, lorsque vous validez le password dans validate_password utilisant le validateur UserAttributeSimilarityValidator, vous n'avez pas l'objet user.

Ce que je suggère qu'au lieu de faire une validation au niveau du champ, vous devez effectuer validation au niveau de l'objet en implémentant la méthode validate sur le sérialiseur:

import sys
from Django.core import exceptions
import Django.contrib.auth.password_validation as validators

class RegisterUserSerializer(serializers.ModelSerializer):

     # rest of the code

     def validate(self, data):
         # here data has all the fields which have validated values
         # so we can create a User instance out of it
         user = User(**data)

         # get the password from the data
         password = data.get('password')

         errors = dict() 
         try:
             # validate the password and catch the exception
             validators.validate_password(password=password, user=User)

         # the exception raised here is different than serializers.ValidationError
         except exceptions.ValidationError as e:
             errors['password'] = list(e.messages)

         if errors:
             raise serializers.ValidationError(errors)

         return super(RegisterUserSerializer, self).validate(data)
23
AKS

Vous pouvez accéder à l'objet utilisateur via self.instance sur l'objet sérialiseur, même lors de la validation au niveau du champ. Quelque chose comme ça devrait fonctionner:

 from Django.contrib.auth import password_validation

 def validate_password(self, value):
    password_validation.validate_password(value, self.instance)
    return value
14
faph

Utilisez des sérialiseurs! Avoir un validate_fieldname méthode!

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = (
            'id', 'username', 'password', 'first_name', 'last_name', 'email'
        )
        extra_kwargs = {
            'password': {'write_only': True},
            'username': {'read_only': True}
        }

    def validate_password(self, value):
        try:
            validate_password(value)
        except ValidationError as exc:
            raise serializers.ValidationError(str(exc))
        return value

    def create(self, validated_data):
        user = super().create(validated_data)
        user.set_password(validated_data['password'])

        user.is_active = False
        user.save()
        return user

    def update(self, instance, validated_data):
        user = super().update(instance, validated_data)
        if 'password' in validated_data:
            user.set_password(validated_data['password'])
            user.save()
        return user
4
shredding

Au moment de la création d'un nouvel utilisateur (enregistrement), self.instance ne sera aucun, cela fonctionnera lorsque vous reposez le mot de passe, changez le mot de passe ou mettez à jour les données utilisateur avec le mot de passe. Mais si vous souhaitez vérifier que le mot de passe ne doit pas être similaire à votre adresse e-mail ou nom d'utilisateur, vous devez inclure "SequenceMatcher" dans votre validation

data = self.get_initial()
username = data.get("username")
email = data.get("email")
password = data.get("password") 
max_similarity = 0.7
if SequenceMatcher(a=password.lower(), b=username.lower()).quick_ratio() > max_similarity:
    raise serializers.ValidationError("The password is too similar to the username.")
if SequenceMatcher(a=password.lower(), b=email.lower()).quick_ratio() > max_similarity:
    raise serializers.ValidationError("The password is too similar to the email.")
0
Sanjay Bharti