Skip to content

Commit

Permalink
Fixed django#2550 -- Allow the auth backends to raise the PermissionD…
Browse files Browse the repository at this point in the history
…enied exception to completely stop the authentication chain. Many thanks to namn, danielr, Dan Julius, Łukasz Rekucki, Aashu Dwivedi and umbrae for working this over the years.
  • Loading branch information
jezdez committed Nov 17, 2012
1 parent 7058b59 commit 1520748
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 2 deletions.
5 changes: 4 additions & 1 deletion django/contrib/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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.
Expand Down
37 changes: 36 additions & 1 deletion django/contrib/auth/tests/auth_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
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

Expand Down Expand Up @@ -323,3 +324,37 @@ def test_has_perm(self):
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', '[email protected]', '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)
6 changes: 6 additions & 0 deletions docs/releases/1.6.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
=====================================

Expand Down
6 changes: 6 additions & 0 deletions docs/topics/auth.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------------------------------

Expand Down

0 comments on commit 1520748

Please sign in to comment.