summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/auth/__init__.py5
-rw-r--r--django/contrib/auth/tests/auth_backends.py37
-rw-r--r--docs/releases/1.6.txt6
-rw-r--r--docs/topics/auth.txt6
4 files changed, 52 insertions, 2 deletions
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index dd4a8484f5..5dbda44501 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -1,6 +1,6 @@
import re
-from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.utils.importlib import import_module
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
@@ -60,6 +60,9 @@ def authenticate(**credentials):
except TypeError:
# This backend doesn't accept these credentials as arguments. Try the next one.
continue
+ except PermissionDenied:
+ # This backend says to stop in our tracks - this user should not be allowed in at all.
+ return None
if user is None:
continue
# Annotate the user object with the path of the backend.
diff --git a/django/contrib/auth/tests/auth_backends.py b/django/contrib/auth/tests/auth_backends.py
index e92f159ff9..2ab0bd0efa 100644
--- a/django/contrib/auth/tests/auth_backends.py
+++ b/django/contrib/auth/tests/auth_backends.py
@@ -6,7 +6,8 @@ from django.contrib.auth.models import User, Group, Permission, AnonymousUser
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.auth.tests.custom_user import ExtensionUser
from django.contrib.contenttypes.models import ContentType
-from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import ImproperlyConfigured, PermissionDenied
+from django.contrib.auth import authenticate
from django.test import TestCase
from django.test.utils import override_settings
@@ -323,3 +324,37 @@ class InActiveUserBackendTest(TestCase):
def test_has_module_perms(self):
self.assertEqual(self.user1.has_module_perms("app1"), False)
self.assertEqual(self.user1.has_module_perms("app2"), False)
+
+
+class PermissionDeniedBackend(object):
+ """
+ Always raises PermissionDenied.
+ """
+ supports_object_permissions = True
+ supports_anonymous_user = True
+ supports_inactive_user = True
+
+ def authenticate(self, username=None, password=None):
+ raise PermissionDenied
+
+
+class PermissionDeniedBackendTest(TestCase):
+ """
+ Tests that other backends are not checked once a backend raises PermissionDenied
+ """
+ backend = 'django.contrib.auth.tests.auth_backends.PermissionDeniedBackend'
+
+ def setUp(self):
+ self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
+ self.user1.save()
+
+ @override_settings(AUTHENTICATION_BACKENDS=(backend, ) +
+ tuple(settings.AUTHENTICATION_BACKENDS))
+ def test_permission_denied(self):
+ "user is not authenticated after a backend raises permission denied #2550"
+ self.assertEqual(authenticate(username='test', password='test'), None)
+
+ @override_settings(AUTHENTICATION_BACKENDS=tuple(
+ settings.AUTHENTICATION_BACKENDS) + (backend, ))
+ def test_authenticates(self):
+ self.assertEqual(authenticate(username='test', password='test'), self.user1)
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index ef162a8de3..49649bc6b8 100644
--- a/docs/releases/1.6.txt
+++ b/docs/releases/1.6.txt
@@ -17,6 +17,12 @@ deprecation process for some features`_.
What's new in Django 1.6
========================
+Minor features
+~~~~~~~~~~~~~~
+
+* Authentication backends can raise ``PermissionDenied`` to immediately fail
+ the authentication chain.
+
Backwards incompatible changes in 1.6
=====================================
diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
index aed482f710..6d8e3c66c3 100644
--- a/docs/topics/auth.txt
+++ b/docs/topics/auth.txt
@@ -2391,6 +2391,12 @@ processing at the first positive match.
you need to force users to re-authenticate using different methods. A simple
way to do that is simply to execute ``Session.objects.all().delete()``.
+.. versionadded:: 1.6
+
+If a backend raises a :class:`~django.core.exceptions.PermissionDenied`
+exception, authentication will immediately fail. Django won't check the
+backends that follow.
+
Writing an authentication backend
---------------------------------