Browse Source

Fixed #23155 -- Added request argument to user_login_failed signal.

Gavin Wahl 9 years ago
parent
commit
f0f3de3c96

+ 1 - 1
django/contrib/auth/__init__.py

@@ -94,7 +94,7 @@ def authenticate(request=None, **credentials):
         return user
 
     # The credentials supplied are invalid to all backends, fire signal
-    user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials))
+    user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request)
 
 
 def login(request, user, backend=None):

+ 1 - 1
django/contrib/auth/signals.py

@@ -1,5 +1,5 @@
 from django.dispatch import Signal
 
 user_logged_in = Signal(providing_args=['request', 'user'])
-user_login_failed = Signal(providing_args=['credentials'])
+user_login_failed = Signal(providing_args=['credentials', 'request'])
 user_logged_out = Signal(providing_args=['request', 'user'])

+ 8 - 0
docs/ref/contrib/auth.txt

@@ -479,6 +479,14 @@ can be used for notification when a user logs in or out.
         authentication backend. Credentials matching a set of 'sensitive' patterns,
         (including password) will not be sent in the clear as part of the signal.
 
+    ``request``
+        The :class:`~django.http.HttpRequest` object, if one was provided to
+        :func:`~django.contrib.auth.authenticate`.
+
+    .. versionchanged:: 1.11
+
+        The ``request`` argument was added.
+
 .. _authentication-backends-reference:
 
 Authentication backends

+ 3 - 0
docs/releases/1.11.txt

@@ -117,6 +117,9 @@ Minor features
   which in turn passes it to the authentication backend if it accepts a
   ``request`` argument.
 
+* The :func:`~django.contrib.auth.signals.user_login_failed` signal now
+  receives a ``request`` argument.
+
 :mod:`django.contrib.contenttypes`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 19 - 0
tests/auth_tests/test_forms.py

@@ -12,6 +12,7 @@ from django.contrib.auth.forms import (
     SetPasswordForm, UserChangeForm, UserCreationForm,
 )
 from django.contrib.auth.models import User
+from django.contrib.auth.signals import user_login_failed
 from django.contrib.sites.models import Site
 from django.core import mail
 from django.core.mail import EmailMultiAlternatives
@@ -279,6 +280,24 @@ class AuthenticationFormTest(TestDataMixin, TestCase):
         self.assertFalse(form.is_valid())
         self.assertEqual(form.non_field_errors(), [force_text(form.error_messages['inactive'])])
 
+    def test_login_failed(self):
+        signal_calls = []
+
+        def signal_handler(**kwargs):
+            signal_calls.append(kwargs)
+
+        user_login_failed.connect(signal_handler)
+        fake_request = object()
+        try:
+            form = AuthenticationForm(fake_request, {
+                'username': 'testclient',
+                'password': 'incorrect',
+            })
+            self.assertFalse(form.is_valid())
+            self.assertIs(signal_calls[0]['request'], fake_request)
+        finally:
+            user_login_failed.disconnect(signal_handler)
+
     def test_inactive_user_i18n(self):
         with self.settings(USE_I18N=True), translation.override('pt-br', deactivate=True):
             # The user is inactive.

+ 10 - 5
tests/auth_tests/test_signals.py

@@ -1,4 +1,4 @@
-from django.contrib.auth import signals
+from django.contrib.auth import authenticate, signals
 from django.contrib.auth.models import User
 from django.test import TestCase, override_settings
 from django.test.client import RequestFactory
@@ -18,8 +18,8 @@ class SignalTestCase(TestCase):
     def listener_logout(self, user, **kwargs):
         self.logged_out.append(user)
 
-    def listener_login_failed(self, sender, credentials, **kwargs):
-        self.login_failed.append(credentials)
+    def listener_login_failed(self, sender, **kwargs):
+        self.login_failed.append(kwargs)
 
     def setUp(self):
         """Set up the listeners and reset the logged in/logged out counters"""
@@ -41,9 +41,10 @@ class SignalTestCase(TestCase):
         self.client.login(username='testclient', password='bad')
         self.assertEqual(len(self.logged_in), 0)
         self.assertEqual(len(self.login_failed), 1)
-        self.assertEqual(self.login_failed[0]['username'], 'testclient')
+        self.assertEqual(self.login_failed[0]['credentials']['username'], 'testclient')
         # verify the password is cleansed
-        self.assertIn('***', self.login_failed[0]['password'])
+        self.assertIn('***', self.login_failed[0]['credentials']['password'])
+        self.assertIn('request', self.login_failed[0])
 
         # Like this:
         self.client.login(username='testclient', password='password')
@@ -77,3 +78,7 @@ class SignalTestCase(TestCase):
         user.refresh_from_db()
         self.assertEqual(user.username, 'staff')
         self.assertNotEqual(user.last_login, old_last_login)
+
+    def test_failed_login_without_request(self):
+        authenticate(username='testclient', password='bad')
+        self.assertIsNone(self.login_failed[0]['request'])