From d33c3993c0c735f23cbedc60fa59fce69354f19d Mon Sep 17 00:00:00 2001 From: Tom Wojcik Date: Fri, 3 May 2024 22:32:45 +0200 Subject: [PATCH] rollback 8f65bfff16577c7fb0f52bbabf5fb69f6809ba62, add support for ModelBackend.user_can_authenticate --- djoser/serializers.py | 10 +- .../testapp/tests/test_token_create.py | 234 ++++++++++++++++-- 2 files changed, 220 insertions(+), 24 deletions(-) diff --git a/djoser/serializers.py b/djoser/serializers.py index 805bf01c..129c9f70 100644 --- a/djoser/serializers.py +++ b/djoser/serializers.py @@ -118,17 +118,13 @@ def __init__(self, *args, **kwargs): def validate(self, attrs): password = attrs.get("password") - params = {settings.LOGIN_FIELD: attrs.get(settings.LOGIN_FIELD)} + params = {"username": attrs.get(settings.LOGIN_FIELD)} self.user = authenticate( request=self.context.get("request"), **params, password=password ) if not self.user: - self.user = User.objects.filter(**params).first() - if self.user and not self.user.check_password(password): - self.fail("invalid_credentials") - if self.user and self.user.is_active: - return attrs - self.fail("invalid_credentials") + self.fail("invalid_credentials") + return attrs class UserFunctionsMixin: diff --git a/testproject/testapp/tests/test_token_create.py b/testproject/testapp/tests/test_token_create.py index ad1f94ea..16498a7c 100644 --- a/testproject/testapp/tests/test_token_create.py +++ b/testproject/testapp/tests/test_token_create.py @@ -1,6 +1,9 @@ import django +from unittest import mock + from django.conf import settings as django_settings from django.contrib.auth import user_logged_in, user_login_failed +from django.contrib.auth.backends import ModelBackend from django.test import override_settings from djet import assertions from rest_framework import status @@ -37,9 +40,221 @@ def test_post_should_login_user(self): self.assertNotEqual(user.last_login, previous_last_login) self.assertTrue(self.signal_sent) + @override_settings( + AUTHENTICATION_BACKENDS=[ + "django.contrib.auth.backends.ModelBackend", + ] + ) + @override_settings( + DJOSER=dict(django_settings.DJOSER, **{"LOGIN_FIELD": "username"}) + ) + def test_post_should_not_login_if_model_backend_user_can_authenticate__LOGIN_FIELD_username__USERNAME_FIELD_username( # noqa: E501 + self, + ): + user = create_user() + user_logged_in.connect(self.signal_receiver) + previous_last_login = user.last_login + + with mock.patch("djoser.serializers.User.USERNAME_FIELD", "username"): + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_200_OK) + + user.refresh_from_db() + self.assertEqual(response.data["auth_token"], user.auth_token.key) + self.assertNotEqual(user.last_login, previous_last_login) + self.assertTrue(self.signal_sent) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + @override_settings( + AUTHENTICATION_BACKENDS=[ + "django.contrib.auth.backends.ModelBackend", + ] + ) + @override_settings(DJOSER=dict(django_settings.DJOSER, **{"LOGIN_FIELD": "email"})) + def test_post_should_not_login_if_model_backend_user_can_authenticate__LOGIN_FIELD_email__USERNAME_FIELD_username( # noqa: E501 + self, + ): + user = create_user() + user_logged_in.connect(self.signal_receiver) + previous_last_login = user.last_login + + with mock.patch("djoser.serializers.User.USERNAME_FIELD", "username"): + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + user.refresh_from_db() + self.assertEqual(user.last_login, previous_last_login) + self.assertFalse(self.signal_sent) + + @override_settings( + AUTHENTICATION_BACKENDS=[ + "django.contrib.auth.backends.ModelBackend", + ] + ) + @override_settings( + DJOSER=dict(django_settings.DJOSER, **{"LOGIN_FIELD": "username"}) + ) + def test_post_should_not_login_if_model_backend_user_can_authenticate__LOGIN_FIELD_username__USERNAME_FIELD_email( # noqa: E501 + self, + ): + user = create_user() + user_logged_in.connect(self.signal_receiver) + previous_last_login = user.last_login + + with mock.patch("djoser.serializers.User.USERNAME_FIELD", "email"): + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + user.refresh_from_db() + self.assertEqual(user.last_login, previous_last_login) + self.assertFalse(self.signal_sent) + + @override_settings( + AUTHENTICATION_BACKENDS=[ + "django.contrib.auth.backends.ModelBackend", + ] + ) + @override_settings(DJOSER=dict(django_settings.DJOSER, **{"LOGIN_FIELD": "email"})) + def test_post_should_not_login_if_model_backend_user_can_authenticate__LOGIN_FIELD_email__USERNAME_FIELD_email( # noqa: E501 + self, + ): + user = create_user() + user_logged_in.connect(self.signal_receiver) + previous_last_login = user.last_login + + with mock.patch("djoser.serializers.User.USERNAME_FIELD", "email"): + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, + {"username": user.username, "password": user.raw_password}, + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=True + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_200_OK) + + user.refresh_from_db() + self.assertEqual(response.data["auth_token"], user.auth_token.key) + self.assertNotEqual(user.last_login, previous_last_login) + self.assertTrue(self.signal_sent) + + with mock.patch.object( + ModelBackend, "user_can_authenticate", return_value=False + ): + response = self.client.post( + self.base_url, {"email": user.email, "password": user.raw_password} + ) + self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST) + def test_post_should_not_login_if_user_is_not_active(self): - """In Django >= 1.10 authenticate() returns None if user is inactive, - while in Django < 1.10 authenticate() succeeds if user is inactive.""" user = create_user() data = {"username": user.username, "password": user.raw_password} user.is_active = False @@ -81,18 +296,3 @@ def test_post_should_not_login_if_empty_request(self): response.data["non_field_errors"], [settings.CONSTANTS.messages.INVALID_CREDENTIALS_ERROR], ) - - @override_settings(DJOSER=dict(django_settings.DJOSER, **{"LOGIN_FIELD": "email"})) - def test_login_using_email(self): - user = create_user() - previous_last_login = user.last_login - data = {"email": user.email, "password": user.raw_password} - user_logged_in.connect(self.signal_receiver) - - response = self.client.post(self.base_url, data) - user.refresh_from_db() - - self.assert_status_equal(response, status.HTTP_200_OK) - self.assertEqual(response.data["auth_token"], user.auth_token.key) - self.assertNotEqual(user.last_login, previous_last_login) - self.assertTrue(self.signal_sent)