dj-rest-auth icon indicating copy to clipboard operation
dj-rest-auth copied to clipboard

Blank, short, not match and common password validation errors for change password are all 'password required' rather than the appropriate error

Open brian-e-haley opened this issue 5 years ago • 5 comments

Title. If needed more information can be provided upon request.

brian-e-haley avatar Aug 27 '20 17:08 brian-e-haley

Can you show me an example HTTP requests that would reproduce the error? We have tests for this and they're passing. The code itself looks right too.

It sounds like you're not sending a valid request or you're missing headers.

iMerica avatar Aug 28 '20 04:08 iMerica

This currently passes with the expected failure decorators. I am going to switch from using force_authenticate to credentials which may be causing the unexpected behaviour.

Edit1: I tried switching from force_authenticate to passing the access token from the login route using credentials and there was no change.

    def setUp(self):
        response = client.post(reverse('rest_login'),
                               {'username': self.USER['username'],
                               'password': self.USER['password1']})
        client.credentials(
            HTTP_AUTHORIZATION=f"Bearer {response.data['access_token']}")

    def tearDown(self):
        client.credentials()
import re
from unittest import expectedFailure

from django.contrib.auth.models import User
from django.core import mail
from rest_framework import exceptions, status
from rest_framework.reverse import reverse
from rest_framework.test import APIClient, APITestCase

client = APIClient()


class PasswordTestCase(APITestCase):

    @classmethod
    def setUpTestData(cls):
        cls.PATTERN = re.compile(r'^.*email/(.*)/.*$',
                                 re.DOTALL)
        cls.USER = {'username': 'user',
                    'email': '[email protected]',
                    'password1': 'complex_password',
                    'password2': 'complex_password'}
        cls.URL = reverse('rest_password_change')
        client.post(reverse('rest_register'),
                    cls.USER)
        key = re.match(cls.PATTERN, mail.outbox[0].body).group(1)
        client.post(reverse('account_email_verification_sent'),
                    {'key': key})

    def setUp(self):
        user = User.objects.get(username=self.USER['username'])
        client.force_authenticate(user)  # This may be the cause of the behaviour.

    def tearDown(self):
        client.force_authenticate(None)

    def test_missing_password(self):
        payload = {}
        response = client.post(self.URL,
                               payload)

        self.assertEqual(response.status_code,
                         status.HTTP_400_BAD_REQUEST)
        self.assertDictEqual(
            response.data,
            {'new_password1': [exceptions.ErrorDetail('This field is required.',
                                                      'required')],
             'new_password2': [exceptions.ErrorDetail('This field is required.',
                                                      'required')]})

    @expectedFailure
    def test_blank_password(self):
        payload = {'password1': '',
                   'password2': ''}
        response = client.post(self.URL,
                               payload)

        self.assertEqual(response.status_code,
                         status.HTTP_400_BAD_REQUEST)
        self.assertDictEqual(
            response.data,
            {
                'password1': [exceptions.ErrorDetail('This field may not be '
                                                     'blank.',
                                                     'blank')],
                'password2': [exceptions.ErrorDetail('This field may not be '
                                                     'blank.',
                                                     'blank')]})

    @expectedFailure
    def test_short_password(self):
        payload = {'password1': 'pass',
                   'password2': 'pass'}
        response = client.post(self.URL,
                               payload)

        self.assertEqual(response.status_code,
                         status.HTTP_400_BAD_REQUEST)
        self.assertDictEqual(
            response.data,
            {'new_password1': [exceptions.ErrorDetail('This password is too '
                                                      'short. It must contain '
                                                      'at least 8 characters.',
                                                      'password_too_short')],
             'new_password2': [exceptions.ErrorDetail('This password is too '
                                                      'short. It must contain '
                                                      'at least 8 characters.',
                                                      'password_too_short')]})

    @expectedFailure
    def test_password_not_match(self):
        payload = {'password1': 'pass',
                   'password2': 'word'}
        response = client.post(self.URL,
                               payload)

        self.assertEqual(response.status_code,
                         status.HTTP_400_BAD_REQUEST)
        self.assertDictEqual(
            response.data,
            {'non_field_errors': [exceptions.ErrorDetail("The two password "
                                                         "fields didn't match.",
                                                         'invalid')]})

    @expectedFailure
    def test_common_password(self):
        payload = {'password1': 'password',
                   'password2': 'password'}
        response = client.post(self.URL,
                               payload)

        self.assertEqual(response.status_code,
                         status.HTTP_400_BAD_REQUEST)
        self.assertDictEqual(
            response.data,
            {'password1': [exceptions.ErrorDetail('This password is too '
                                                  'common.',
                                                  'password_too_common')]})

brian-e-haley avatar Aug 28 '20 13:08 brian-e-haley

@brian-e-haley This is because of the force authentication technique.

I have made another piece of test which seems passing.

from django.test import TestCase
from rest_framework.exceptions import ErrorDetail

from dj_rest_auth.registration.serializers import RegisterSerializer


class TestRegisterSerializerSerializer(TestCase):
    def test_short_password(self):
        serializer = RegisterSerializer(
            data={
                "username": "test",
                "email": "[email protected]",
                "password1": "pass",
                "password2": "pass"

            }
        )
        self.assertEqual(serializer.is_valid(), False)
        self.assertEqual(
            serializer.errors,
            {
                'password1': [
                    ErrorDetail(
                        string='Password must be a minimum of 6 characters.',
                        code='invalid'
                    )
                ]
            }
        )

jerinpetergeorge avatar Nov 26 '20 19:11 jerinpetergeorge

@iMerica I hope this issue can be closed now

jerinpetergeorge avatar Nov 26 '20 19:11 jerinpetergeorge

@jerinpetergeorge As I mentioned in my Edit1: which I left here in August using client.credentials() in place of client.force_authenticate() does not change anything so I don't think it is fair to say that client.force_authenticate() is the cause.

I posted this months ago though so my memory is a bit fuzzy so I doubt I will be of much help.

brian-e-haley avatar Nov 26 '20 20:11 brian-e-haley