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

This commit is contained in:
Gavin Wahl 2016-02-18 17:58:30 -07:00 committed by Tim Graham
parent 4b9330ccc0
commit f0f3de3c96
6 changed files with 42 additions and 7 deletions

View File

@ -94,7 +94,7 @@ def authenticate(request=None, **credentials):
return user return user
# The credentials supplied are invalid to all backends, fire signal # 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): def login(request, user, backend=None):

View File

@ -1,5 +1,5 @@
from django.dispatch import Signal from django.dispatch import Signal
user_logged_in = Signal(providing_args=['request', 'user']) 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']) user_logged_out = Signal(providing_args=['request', 'user'])

View File

@ -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, authentication backend. Credentials matching a set of 'sensitive' patterns,
(including password) will not be sent in the clear as part of the signal. (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-reference:
Authentication backends Authentication backends

View File

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

View File

@ -12,6 +12,7 @@ from django.contrib.auth.forms import (
SetPasswordForm, UserChangeForm, UserCreationForm, SetPasswordForm, UserChangeForm, UserCreationForm,
) )
from django.contrib.auth.models import User 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.contrib.sites.models import Site
from django.core import mail from django.core import mail
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
@ -279,6 +280,24 @@ class AuthenticationFormTest(TestDataMixin, TestCase):
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(), [force_text(form.error_messages['inactive'])]) 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): def test_inactive_user_i18n(self):
with self.settings(USE_I18N=True), translation.override('pt-br', deactivate=True): with self.settings(USE_I18N=True), translation.override('pt-br', deactivate=True):
# The user is inactive. # The user is inactive.

View File

@ -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.contrib.auth.models import User
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.test.client import RequestFactory from django.test.client import RequestFactory
@ -18,8 +18,8 @@ class SignalTestCase(TestCase):
def listener_logout(self, user, **kwargs): def listener_logout(self, user, **kwargs):
self.logged_out.append(user) self.logged_out.append(user)
def listener_login_failed(self, sender, credentials, **kwargs): def listener_login_failed(self, sender, **kwargs):
self.login_failed.append(credentials) self.login_failed.append(kwargs)
def setUp(self): def setUp(self):
"""Set up the listeners and reset the logged in/logged out counters""" """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.client.login(username='testclient', password='bad')
self.assertEqual(len(self.logged_in), 0) self.assertEqual(len(self.logged_in), 0)
self.assertEqual(len(self.login_failed), 1) 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 # 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: # Like this:
self.client.login(username='testclient', password='password') self.client.login(username='testclient', password='password')
@ -77,3 +78,7 @@ class SignalTestCase(TestCase):
user.refresh_from_db() user.refresh_from_db()
self.assertEqual(user.username, 'staff') self.assertEqual(user.username, 'staff')
self.assertNotEqual(user.last_login, old_last_login) 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'])