2016-04-04 17 views
5

Próbuję zintegrować django validators 1.9 z serializerami frameworku django rest. Ale zserializowany "użytkownik" (struktury django rest) nie jest zgodny z walidatorami django.integracja weryfikatorów haseł django z frameworkiem django validate_password

Oto 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 

udało mi się dostać MinimumLengthValidator i NumericPasswordValidator prawidłowe, ponieważ zarówno validate funkcja nie używać „użytkownik” w walidacji. Kod źródłowy jest here

Fragment kodu źródłowego Django:

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

Dla innych walidatorami jak UserAttributeSimilarityValidator, funkcja używa innego jeden argument 'user' w walidacji ('user' jest Django modelu użytkownika, jeśli I” m nie źle)

Fragment kodu źródłowego Django:

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

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

Jak mogę zmienić serializ ed użytkownika do tego, co django walidatorami (UserAttributeSimilarityValidator) można zobaczyć

Wyciąg z kodu źródłowego Django:

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 

Edycja

Django Reszta Framework można uzyskać wszystkie Django wbudowanego sprawdzanie poprawności hasła (ale to jak hack). Oto problem:

validationError jest tak

[ValidationError ([ 'To hasło jest zbyt krótkie, musi zawierać co najmniej 8 znaków..']), ValidationError ([ 'To hasło jest całkowicie numeryczna. '])]

Walidacja nie zawiera pola. Django reszta ramy zobaczyć go jako

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

Jak mogę wstrzyknąć pole na raise ValidationError

Odpowiedz

8

Jak wspomniałeś, gdy zatwierdzanie password w validate_password metoda z użyciem UserAttributeSimilarityValidator walidatora, nie mają na celu user.

What I sugerują, że zamiast robić walidacji pola poziomie, powinien wykonać object-level validation wdrażając validate sposób na serializatora:

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) 
+0

Istnieje kilka literówka w kodzie. Nawiasem mówiąc, twoje rozwiązanie działa poprawnie podczas sprawdzania poprawności, ale struktura django rest nie może uzyskać nazwy pola stąd non_field_errors ---> { "non_field_errors": [ "Hasło jest zbyt podobne do wiadomości e-mail.", " To hasło jest za krótkie i musi zawierać co najmniej 8 znaków. " ] } – momokjaaaaa

+0

} Tak naprawdę nie przetestowałem kodu, więc może być tu kilka literówek, ale to nie powinno powstrzymać cię od prawidłowego używania i testowania tego kodu. O "NON_FIELD_ERRORS" Zaktualizowałem swoją odpowiedź, a teraz podniesione błędy powinny zawierać nazwę pola 'password' jako klucz dla tych błędów. – AKS

+0

Należy pamiętać, że '** data' może nie zawierać wszystkich wymaganych pól do utworzenia obiektu użytkownika podczas wykonywania aktualizacji częściowych (' PATCH'). – faph

6

można uzyskać dostęp do obiektu użytkownika przez self.instance na obiekcie serializer, nawet gdy przeprowadzanie walidacji na poziomie pola.Coś takiego powinno zadziałać:

from django.contrib.auth import password_validation 

def validate_password(self, value): 
    password_validation.validate_password(value, self.instance) 
    return value 
+0

Ta opcja będzie dostępna tylko wtedy, gdy użytkownik został utworzony, co nie ma miejsca w przypadku rejestracji nowego użytkownika i sprawdzania wstępnego tworzenia hasła. – tbm

1

Użyj serializatorów! Zastosuj metodę validate_fieldname!

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): 
     validated_data = self.check_for_unique_email(validated_data) 
     validated_data.setdefault('username', validated_data['email']) 
     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): 
     validated_data = self.check_for_unique_email(validated_data) 
     user = super().update(instance, validated_data) 
     if 'password' in validated_data: 
      user.set_password(validated_data['password']) 
      user.save() 
     return user 
0

W momencie tworzenia nowego użytkownika (rejestracja) następnie self.instance będzie nic, to będzie działać, gdy swój spoczywają hasło, Zmień hasło lub aktualizowanie danych użytkownika z hasłem. Ale jeśli chcesz sprawdzić hasło nie powinna być podobna do wiadomości e-mail lub nazwa użytkownika to trzeba to „SequenceMatcher” w walidacji

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.")