diff options
| author | Michael Farrell <micolous+git@gmail.com> | 2012-07-12 11:13:15 +0930 |
|---|---|---|
| committer | Preston Holmes <preston@ptone.com> | 2012-09-30 22:34:50 -0700 |
| commit | 7cc4068c4470876c526830778cbdac2fdfd6dc26 (patch) | |
| tree | ee837a8ccbbece0e83e2846f1ad5343f5e3d6b93 /django | |
| parent | 8bd7b598b6de1be1e3f72f3a1ee62803b1c02010 (diff) | |
Fixed #18616 -- added user_login_fail signal to contrib.auth
Thanks to Brad Pitcher for documentation
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/auth/__init__.py | 23 | ||||
| -rw-r--r-- | django/contrib/auth/signals.py | 1 | ||||
| -rw-r--r-- | django/contrib/auth/tests/signals.py | 16 |
3 files changed, 38 insertions, 2 deletions
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 1050d1d1bb..dd4a8484f5 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -1,6 +1,8 @@ +import re + from django.core.exceptions import ImproperlyConfigured from django.utils.importlib import import_module -from django.contrib.auth.signals import user_logged_in, user_logged_out +from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed SESSION_KEY = '_auth_user_id' BACKEND_SESSION_KEY = '_auth_user_backend' @@ -33,6 +35,21 @@ def get_backends(): return backends +def _clean_credentials(credentials): + """ + Cleans a dictionary of credentials of potentially sensitive info before + sending to less secure functions. + + Not comprehensive - intended for user_login_failed signal + """ + SENSITIVE_CREDENTIALS = re.compile('api|token|key|secret|password|signature', re.I) + CLEANSED_SUBSTITUTE = '********************' + for key in credentials: + if SENSITIVE_CREDENTIALS.search(key): + credentials[key] = CLEANSED_SUBSTITUTE + return credentials + + def authenticate(**credentials): """ If the given credentials are valid, return a User object. @@ -49,6 +66,10 @@ def authenticate(**credentials): user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__) return user + # The credentials supplied are invalid to all backends, fire signal + user_login_failed.send(sender=__name__, + credentials=_clean_credentials(credentials)) + def login(request, user): """ diff --git a/django/contrib/auth/signals.py b/django/contrib/auth/signals.py index 4f0b2c235c..71ab6a11d1 100644 --- a/django/contrib/auth/signals.py +++ b/django/contrib/auth/signals.py @@ -1,4 +1,5 @@ from django.dispatch import Signal user_logged_in = Signal(providing_args=['request', 'user']) +user_login_failed = Signal(providing_args=['credentials']) user_logged_out = Signal(providing_args=['request', 'user']) diff --git a/django/contrib/auth/tests/signals.py b/django/contrib/auth/tests/signals.py index c597aa9ed0..024f44f547 100644 --- a/django/contrib/auth/tests/signals.py +++ b/django/contrib/auth/tests/signals.py @@ -18,27 +18,41 @@ 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 setUp(self): """Set up the listeners and reset the logged in/logged out counters""" self.logged_in = [] self.logged_out = [] + self.login_failed = [] signals.user_logged_in.connect(self.listener_login) signals.user_logged_out.connect(self.listener_logout) + signals.user_login_failed.connect(self.listener_login_failed) def tearDown(self): """Disconnect the listeners""" signals.user_logged_in.disconnect(self.listener_login) signals.user_logged_out.disconnect(self.listener_logout) + signals.user_login_failed.disconnect(self.listener_login_failed) def test_login(self): - # Only a successful login will trigger the signal. + # Only a successful login will trigger the success signal. 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') + # verify the password is cleansed + self.assertTrue('***' in self.login_failed[0]['password']) + # Like this: self.client.login(username='testclient', password='password') self.assertEqual(len(self.logged_in), 1) self.assertEqual(self.logged_in[0].username, 'testclient') + # Ensure there were no more failures. + self.assertEqual(len(self.login_failed), 1) + def test_logout_anonymous(self): # The log_out function will still trigger the signal for anonymous # users. |
