Commit 745c255a authored by Jannis Leidel's avatar Jannis Leidel
Browse files

Fixed #14249 -- Added support for inactive users to the auth backend system....

Fixed #14249 -- Added support for inactive users to the auth backend system. Thanks, Harro van der Klauw.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15010 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 5830477e
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -30,6 +30,11 @@ def load_backend(path):
        warn("Authentication backends without a `supports_anonymous_user` attribute are deprecated. Please define it in %s." % cls,
             DeprecationWarning)
        cls.supports_anonymous_user = False

    if not hasattr(cls, 'supports_inactive_user'):
        warn("Authentication backends without a `supports_inactive_user` attribute are deprecated. Please define it in %s." % cls,
             DeprecationWarning)
        cls.supports_inactive_user = False
    return cls()

def get_backends():
+5 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ class ModelBackend(object):
    """
    supports_object_permissions = False
    supports_anonymous_user = True
    supports_inactive_user = True

    # TODO: Model, login attribute name and password attribute name should be
    # configurable.
@@ -42,12 +43,16 @@ class ModelBackend(object):
        return user_obj._perm_cache

    def has_perm(self, user_obj, perm):
        if not user_obj.is_active:
            return False
        return perm in self.get_all_permissions(user_obj)

    def has_module_perms(self, user_obj, app_label):
        """
        Returns True if user_obj has any permissions in the given app_label.
        """
        if not user_obj.is_active:
            return False
        for perm in self.get_all_permissions(user_obj):
            if perm[:perm.index('.')] == app_label:
                return True
+10 −11
Original line number Diff line number Diff line
@@ -170,8 +170,10 @@ def _user_get_all_permissions(user, obj):

def _user_has_perm(user, perm, obj):
    anon = user.is_anonymous()
    active = user.is_active
    for backend in auth.get_backends():
        if not anon or backend.supports_anonymous_user:
        if (not active and not anon and backend.supports_inactive_user) or \
                    (not anon or backend.supports_anonymous_user):
            if hasattr(backend, "has_perm"):
                if obj is not None:
                    if (backend.supports_object_permissions and
@@ -185,8 +187,10 @@ def _user_has_perm(user, perm, obj):

def _user_has_module_perms(user, app_label):
    anon = user.is_anonymous()
    active = user.is_active
    for backend in auth.get_backends():
        if not anon or backend.supports_anonymous_user:
        if (not active and not anon and backend.supports_inactive_user) or \
                    (not anon or backend.supports_anonymous_user):
            if hasattr(backend, "has_module_perms"):
                if backend.has_module_perms(user, app_label):
                    return True
@@ -310,12 +314,9 @@ class User(models.Model):
        auth backend is assumed to have permission in general. If an object
        is provided, permissions for this specific object are checked.
        """
        # Inactive users have no permissions.
        if not self.is_active:
            return False

        # Superusers have all permissions.
        if self.is_superuser:
        # Active superusers have all permissions.
        if self.is_active and self.is_superuser:
            return True

        # Otherwise we need to check the backends.
@@ -337,10 +338,8 @@ class User(models.Model):
        Returns True if the user has any permissions in the given app
        label. Uses pretty much the same logic as has_perm, above.
        """
        if not self.is_active:
            return False

        if self.is_superuser:
        # Active superusers have all permissions.
        if self.is_active and self.is_superuser:
            return True

        return _user_has_module_perms(self, app_label)
+10 −6
Original line number Diff line number Diff line
from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest, NoBackendsTest
from django.contrib.auth.tests.auth_backends import (BackendTest,
    RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest,
    NoBackendsTest, InActiveUserBackendTest, NoInActiveUserBackendTest)
from django.contrib.auth.tests.basic import BasicTestCase
from django.contrib.auth.tests.decorators import LoginRequiredTestCase
from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest
from django.contrib.auth.tests.remote_user \
        import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest
from django.contrib.auth.tests.forms import (UserCreationFormTest,
    AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest,
    UserChangeFormTest, PasswordResetFormTest)
from django.contrib.auth.tests.remote_user import (RemoteUserTest,
    RemoteUserNoCreateTest, RemoteUserCustomTest)
from django.contrib.auth.tests.models import ProfileTestCase
from django.contrib.auth.tests.signals import SignalTestCase
from django.contrib.auth.tests.tokens import TokenGeneratorTest
from django.contrib.auth.tests.views import PasswordResetTest, \
    ChangePasswordTest, LoginTest, LogoutTest, LoginURLSettings
from django.contrib.auth.tests.views import (PasswordResetTest,
    ChangePasswordTest, LoginTest, LogoutTest, LoginURLSettings)
from django.contrib.auth.tests.permissions import TestAuthPermissions

# The password for the fixture data users is 'password'
+76 −2
Original line number Diff line number Diff line
@@ -102,9 +102,12 @@ class TestObj(object):

class SimpleRowlevelBackend(object):
    supports_object_permissions = True
    supports_inactive_user = False

    # This class also supports tests for anonymous user permissions, and
    # inactive user permissions via subclasses which just set the
    # 'supports_anonymous_user' or 'supports_inactive_user' attribute.

    # This class also supports tests for anonymous user permissions,
    # via subclasses which just set the 'supports_anonymous_user' attribute.

    def has_perm(self, user, perm, obj=None):
        if not obj:
@@ -116,9 +119,13 @@ class SimpleRowlevelBackend(object):
            elif user.is_anonymous() and perm == 'anon':
                # not reached due to supports_anonymous_user = False
                return True
            elif not user.is_active and perm == 'inactive':
                return True
        return False

    def has_module_perms(self, user, app_label):
        if not user.is_anonymous() and not user.is_active:
            return False
        return app_label == "app1"

    def get_all_permissions(self, user, obj=None):
@@ -192,11 +199,13 @@ class RowlevelBackendTest(TestCase):
class AnonymousUserBackend(SimpleRowlevelBackend):

    supports_anonymous_user = True
    supports_inactive_user = False


class NoAnonymousUserBackend(SimpleRowlevelBackend):

    supports_anonymous_user = False
    supports_inactive_user = False


class AnonymousUserBackendTest(TestCase):
@@ -258,6 +267,7 @@ class NoAnonymousUserBackendTest(TestCase):
    def test_get_all_permissions(self):
        self.assertEqual(self.user1.get_all_permissions(TestObj()), set())


class NoBackendsTest(TestCase):
    """
    Tests that an appropriate error is raised if no auth backends are provided.
@@ -272,3 +282,67 @@ class NoBackendsTest(TestCase):

    def test_raises_exception(self):
        self.assertRaises(ImproperlyConfigured, self.user.has_perm, ('perm', TestObj(),))


class InActiveUserBackend(SimpleRowlevelBackend):

    supports_anonymous_user = False
    supports_inactive_user = True


class NoInActiveUserBackend(SimpleRowlevelBackend):

    supports_anonymous_user = False
    supports_inactive_user = False


class InActiveUserBackendTest(TestCase):
    """
    Tests for a inactive user delegating to backend if it has 'supports_inactive_user' = True
    """

    backend = 'django.contrib.auth.tests.auth_backends.InActiveUserBackend'

    def setUp(self):
        self.curr_auth = settings.AUTHENTICATION_BACKENDS
        settings.AUTHENTICATION_BACKENDS = (self.backend,)
        self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
        self.user1.is_active = False
        self.user1.save()

    def tearDown(self):
        settings.AUTHENTICATION_BACKENDS = self.curr_auth

    def test_has_perm(self):
        self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
        self.assertEqual(self.user1.has_perm('inactive', TestObj()), True)

    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 NoInActiveUserBackendTest(TestCase):
    """
    Tests that an inactive user does not delegate to backend if it has 'supports_inactive_user' = False
    """
    backend = 'django.contrib.auth.tests.auth_backends.NoInActiveUserBackend'

    def setUp(self):
        self.curr_auth = settings.AUTHENTICATION_BACKENDS
        settings.AUTHENTICATION_BACKENDS = tuple(self.curr_auth) + (self.backend,)
        self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
        self.user1.is_active = False
        self.user1.save()

    def tearDown(self):
        settings.AUTHENTICATION_BACKENDS = self.curr_auth

    def test_has_perm(self):
        self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
        self.assertEqual(self.user1.has_perm('inactive', TestObj()), True)

    def test_has_module_perms(self):
        self.assertEqual(self.user1.has_module_perms("app1"), False)
        self.assertEqual(self.user1.has_module_perms("app2"), False)
Loading