Add serializer validation & tests

This commit is contained in:
Jeremy Stretch 2024-07-25 21:08:50 -04:00
parent d3a918357c
commit 968770b825
4 changed files with 70 additions and 4 deletions

View File

@ -1,4 +1,4 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model, password_validation
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers from rest_framework import serializers
@ -61,6 +61,14 @@ class UserSerializer(ValidatedModelSerializer):
'password': {'write_only': True} 'password': {'write_only': True}
} }
def validate(self, data):
# Enforce password validation rules (if configured)
if not self.nested and data.get('password'):
password_validation.validate_password(data['password'], self.instance)
return super().validate(data)
def create(self, validated_data): def create(self, validated_data):
""" """
Extract the password from validated data and set it separately to ensure proper hash generation. Extract the password from validated data and set it separately to ensure proper hash generation.

View File

@ -226,8 +226,10 @@ class UserForm(forms.ModelForm):
# Check that password confirmation matches if password is set # Check that password confirmation matches if password is set
if self.cleaned_data['password'] and self.cleaned_data['password'] != self.cleaned_data['confirm_password']: if self.cleaned_data['password'] and self.cleaned_data['password'] != self.cleaned_data['confirm_password']:
raise forms.ValidationError(_("Passwords do not match! Please check your input and try again.")) raise forms.ValidationError(_("Passwords do not match! Please check your input and try again."))
if self.cleaned_data['password'] and self.cleaned_data['confirm_password']:
password_validation.validate_password(self.cleaned_data['confirm_password'], self.instance) # Enforce password validation rules (if configured)
if self.cleaned_data['password']:
password_validation.validate_password(self.cleaned_data['password'], self.instance)
class GroupForm(forms.ModelForm): class GroupForm(forms.ModelForm):

View File

@ -1,4 +1,5 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import override_settings
from django.urls import reverse from django.urls import reverse
from core.models import ObjectType from core.models import ObjectType
@ -93,6 +94,31 @@ class UserTest(APIViewTestCases.APIViewTestCase):
user.refresh_from_db() user.refresh_from_db()
self.assertTrue(user.check_password(data['password'])) self.assertTrue(user.check_password(data['password']))
@override_settings(AUTH_PASSWORD_VALIDATORS=[{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 8}
}])
def test_password_validation_enforced(self):
"""
Test that any configured password validation rules (AUTH_PASSWORD_VALIDATORS) are enforced.
"""
self.add_permissions('users.add_user')
data = {
'username': 'new_user',
'password': 'foo',
}
url = reverse('users-api:user-list')
# Password too short
response = self.client.post(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 400)
# Password long enough
data['password'] = 'foobar123'
response = self.client.post(url, data, format='json', **self.header)
self.assertEqual(response.status_code, 201)
class GroupTest(APIViewTestCases.APIViewTestCase): class GroupTest(APIViewTestCases.APIViewTestCase):
model = Group model = Group

View File

@ -1,6 +1,8 @@
from django.test import override_settings
from core.models import ObjectType from core.models import ObjectType
from users.models import * from users.models import *
from utilities.testing import ViewTestCases, create_test_user from utilities.testing import ViewTestCases, create_test_user, extract_form_failures
class UserTestCase( class UserTestCase(
@ -58,6 +60,34 @@ class UserTestCase(
'last_name': 'newlastname', 'last_name': 'newlastname',
} }
@override_settings(AUTH_PASSWORD_VALIDATORS=[{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {'min_length': 8}
}])
def test_password_validation_enforced(self):
"""
Test that any configured password validation rules (AUTH_PASSWORD_VALIDATORS) are enforced.
"""
self.add_permissions('users.add_user')
data = {
'username': 'new_user',
'password': 'foo',
'confirm_password': 'foo',
}
# Password too short
request = {
'path': self._get_url('add'),
'data': data,
}
response = self.client.post(**request)
self.assertHttpStatus(response, 200)
# Password long enough
data['password'] = 'foobar123'
data['confirm_password'] = 'foobar123'
self.assertHttpStatus(self.client.post(**request), 302)
class GroupTestCase( class GroupTestCase(
ViewTestCases.GetObjectViewTestCase, ViewTestCases.GetObjectViewTestCase,