Ticket #14249: 14249.diff

File 14249.diff, 12.6 KB (added by Harro, 8 years ago)

Add support for in active user permissions

  • django/contrib/auth/__init__.py

    diff -r 72e58bf8e48c django/contrib/auth/__init__.py
    a b  
    2929        warn("Authentication backends without a `supports_anonymous_user` attribute are deprecated. Please define it in %s." % cls,
    3030             DeprecationWarning)
    3131        cls.supports_anonymous_user = False
     32       
     33    if not hasattr(cls, 'supports_inactive_user'):
     34        warn("Authentication backends without a `supports_inactive_user` attribute are deprecated. Please define it in %s." % cls,
     35             DeprecationWarning)
     36        cls.supports_inactive_user = False
    3237    return cls()
    3338
    3439def get_backends():
  • django/contrib/auth/backends.py

    diff -r 72e58bf8e48c django/contrib/auth/backends.py
    a b  
    88    """
    99    supports_object_permissions = False
    1010    supports_anonymous_user = True
     11    supports_inactive_user = True
    1112
    1213    # TODO: Model, login attribute name and password attribute name should be
    1314    # configurable.
     
    4041        return user_obj._perm_cache
    4142
    4243    def has_perm(self, user_obj, perm):
     44        if not user_obj.is_active:
     45            return False
    4346        return perm in self.get_all_permissions(user_obj)
    4447
    4548    def has_module_perms(self, user_obj, app_label):
    4649        """
    4750        Returns True if user_obj has any permissions in the given app_label.
    4851        """
     52        if not user_obj.is_active:
     53            return False
    4954        for perm in self.get_all_permissions(user_obj):
    5055            if perm[:perm.index('.')] == app_label:
    5156                return True
  • django/contrib/auth/models.py

    diff -r 72e58bf8e48c django/contrib/auth/models.py
    a b  
    160160
    161161def _user_has_perm(user, perm, obj):
    162162    anon = user.is_anonymous()
     163    active = user.is_active
    163164    for backend in auth.get_backends():
    164         if not anon or backend.supports_anonymous_user:
     165        if (not active and not anon and backend.supports_inactive_user) or \
     166                    (not anon or backend.supports_anonymous_user):
    165167            if hasattr(backend, "has_perm"):
    166168                if obj is not None:
    167169                    if (backend.supports_object_permissions and
     
    175177
    176178def _user_has_module_perms(user, app_label):
    177179    anon = user.is_anonymous()
     180    active = user.is_active
    178181    for backend in auth.get_backends():
    179         if not anon or backend.supports_anonymous_user:
     182        if (not active and not anon and backend.supports_inactive_user) or \
     183                    (not anon or backend.supports_anonymous_user):
    180184            if hasattr(backend, "has_module_perms"):
    181185                if backend.has_module_perms(user, app_label):
    182186                    return True
     
    300304        auth backend is assumed to have permission in general. If an object
    301305        is provided, permissions for this specific object are checked.
    302306        """
    303         # Inactive users have no permissions.
    304         if not self.is_active:
    305             return False
    306 
    307         # Superusers have all permissions.
    308         if self.is_superuser:
     307       
     308        # Active superusers have all permissions.
     309        if self.is_active and self.is_superuser:
    309310            return True
    310311
    311312        # Otherwise we need to check the backends.
     
    327328        Returns True if the user has any permissions in the given app
    328329        label. Uses pretty much the same logic as has_perm, above.
    329330        """
    330         if not self.is_active:
    331             return False
    332 
    333         if self.is_superuser:
     331        # Active superusers have all permissions.
     332        if self.is_active and self.is_superuser:
    334333            return True
    335334
    336335        return _user_has_module_perms(self, app_label)
  • django/contrib/auth/tests/__init__.py

    diff -r 72e58bf8e48c django/contrib/auth/tests/__init__.py
    a b  
    1 from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest
     1from django.contrib.auth.tests.auth_backends \
     2    import BackendTest, RowlevelBackendTest, AnonymousUserBackendTest, NoAnonymousUserBackendTest, InActiveUserBackendTest, NoInActiveUserBackendTest
    23from django.contrib.auth.tests.basic import BasicTestCase
    34from django.contrib.auth.tests.decorators import LoginRequiredTestCase
    45from django.contrib.auth.tests.forms import UserCreationFormTest, AuthenticationFormTest, SetPasswordFormTest, PasswordChangeFormTest, UserChangeFormTest, PasswordResetFormTest
  • django/contrib/auth/tests/auth_backends.py

    diff -r 72e58bf8e48c django/contrib/auth/tests/auth_backends.py
    a b  
    9696
    9797class SimpleRowlevelBackend(object):
    9898    supports_object_permissions = True
     99    supports_inactive_user = False
    99100
    100     # This class also supports tests for anonymous user permissions,
    101     # via subclasses which just set the 'supports_anonymous_user' attribute.
     101    # This class also supports tests for anonymous user permissions, and
     102    # inactive user permissions via subclasses which just set the
     103    # 'supports_anonymous_user' or 'supports_inactive_user' attribute.
     104   
    102105
    103106    def has_perm(self, user, perm, obj=None):
    104107        if not obj:
     
    110113            elif user.is_anonymous() and perm == 'anon':
    111114                # not reached due to supports_anonymous_user = False
    112115                return True
     116            elif not user.is_active and perm == 'inactive':
     117                return True
    113118        return False
    114119
    115120    def has_module_perms(self, user, app_label):
     121        if not user.is_anonymous() and not user.is_active:
     122            return False
    116123        return app_label == "app1"
    117124
    118125    def get_all_permissions(self, user, obj=None):
     
    186193class AnonymousUserBackend(SimpleRowlevelBackend):
    187194
    188195    supports_anonymous_user = True
     196    supports_inactive_user = False
    189197
    190198
    191199class NoAnonymousUserBackend(SimpleRowlevelBackend):
    192200
    193201    supports_anonymous_user = False
     202    supports_inactive_user = False
    194203
    195204
    196205class AnonymousUserBackendTest(TestCase):
     
    251260
    252261    def test_get_all_permissions(self):
    253262        self.assertEqual(self.user1.get_all_permissions(TestObj()), set())
     263       
     264       
     265       
     266class InActiveUserBackend(SimpleRowlevelBackend):
     267   
     268    supports_anonymous_user = False
     269    supports_inactive_user = True
     270
     271
     272class NoInActiveUserBackend(SimpleRowlevelBackend):
     273   
     274    supports_anonymous_user = False
     275    supports_inactive_user = False
     276
     277
     278class InActiveUserBackendTest(TestCase):
     279    """
     280    Tests for a inactive user delegating to backend if it has 'supports_inactive_user' = True
     281    """
     282
     283    backend = 'django.contrib.auth.tests.auth_backends.InActiveUserBackend'
     284
     285    def setUp(self):
     286        self.curr_auth = settings.AUTHENTICATION_BACKENDS
     287        settings.AUTHENTICATION_BACKENDS = (self.backend,)
     288        self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
     289        self.user1.is_active = False
     290        self.user1.save()
     291
     292    def tearDown(self):
     293        settings.AUTHENTICATION_BACKENDS = self.curr_auth
     294
     295    def test_has_perm(self):
     296        self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
     297        self.assertEqual(self.user1.has_perm('inactive', TestObj()), True)
     298       
     299    def test_has_module_perms(self):
     300        self.assertEqual(self.user1.has_module_perms("app1"), False)
     301        self.assertEqual(self.user1.has_module_perms("app2"), False)
     302
     303
     304class NoInActiveUserBackendTest(TestCase):
     305    """
     306    Tests that an inactive user does not delegate to backend if it has 'supports_inactive_user' = False
     307    """
     308    backend = 'django.contrib.auth.tests.auth_backends.NoInActiveUserBackend'
     309
     310    def setUp(self):
     311        self.curr_auth = settings.AUTHENTICATION_BACKENDS
     312        settings.AUTHENTICATION_BACKENDS = tuple(self.curr_auth) + (self.backend,)
     313        self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
     314        self.user1.is_active = False
     315        self.user1.save()
     316
     317    def tearDown(self):
     318        settings.AUTHENTICATION_BACKENDS = self.curr_auth
     319
     320    def test_has_perm(self):
     321        self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
     322        self.assertEqual(self.user1.has_perm('inactive', TestObj()), True)
     323       
     324    def test_has_module_perms(self):
     325        self.assertEqual(self.user1.has_module_perms("app1"), False)
     326        self.assertEqual(self.user1.has_module_perms("app2"), False)
  • docs/internals/deprecation.txt

    diff -r 72e58bf8e48c docs/internals/deprecation.txt
    a b  
    9898        * The ``no`` language code has been deprecated in favor of the ``nb``
    9999          language code.
    100100
     101        * Authentication backends need to define the boolean attribute
     102          ``supports_inactive_user``.
     103
    101104    * 1.5
    102105        * The ``mod_python`` request handler has been deprecated since the 1.3
    103106          release. The ``mod_wsgi`` handler should be used instead.
     
    136139          template variable, not an implied string. The new-style
    137140          behavior is provided in the ``future`` template tag library.
    138141
     142        * Authentication backends need to support a inactive user
     143          being passed to all methods dealing with permissions.
     144          The ``supports_inactive_user`` variable is not checked any
     145          longer and can be removed.
     146
    139147    * 2.0
    140148        * ``django.views.defaults.shortcut()``. This function has been moved
    141149          to ``django.contrib.contenttypes.views.shortcut()`` as part of the
  • docs/releases/1.3-alpha-2.txt

    diff -r 72e58bf8e48c docs/releases/1.3-alpha-2.txt
    a b  
    5555
    5656For more information, see :ref:`translator-comments`.
    5757
     58Permissions for inactive users
     59~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     60
     61If you provide a custom auth backend with ``supports_inactive_user`` set to
     62``True``, an inactive user model will check the backend for permissions.
     63This is useful for further centralizing the permission handling. See the
     64:ref:`authentication docs <topics-auth>` for more details.
     65
    5866Backwards-incompatible changes in 1.3 alpha 2
    5967=============================================
    6068
  • docs/releases/1.3.txt

    diff -r 72e58bf8e48c docs/releases/1.3.txt
    a b  
    133133For more information, see :ref:`contextual-markers` and
    134134:ref:`translator-comments`.
    135135
     136Permissions for inactive users
     137~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     138
     139If you provide a custom auth backend with ``supports_inactive_user`` set to
     140``True``, an inactive user model will check the backend for permissions.
     141This is useful for further centralizing the permission handling. See the
     142:ref:`authentication docs <topics-auth>` for more details.
     143
    136144Everything else
    137145~~~~~~~~~~~~~~~
    138146
  • docs/topics/auth.txt

    diff -r 72e58bf8e48c docs/topics/auth.txt
    a b  
    16061606Django 1.4 will assume that every backend supports anonymous users being
    16071607passed to the authorization methods.
    16081608
     1609Authorization for inactive users
     1610~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     1611
     1612.. versionchanged:: 1.3
     1613
     1614An inactive user is a one that is authenticated but has it's attribute
     1615``is_active`` set to ``False``. However this does not mean they are not
     1616authrozied to do anything. For example they are allowed to activate their
     1617account.
     1618
     1619The change to support anonymous users in the permission system allowed for
     1620anonymous users to have permissions to do something while inactive
     1621authenticated users did not.
     1622
     1623To enable this on your own backend, you must set the class attribute
     1624``supports_inactive_user`` to ``True``.
     1625
     1626A nonexisting ``supports_inactive_user`` attrivute will raise a hidden
     1627``PendingDeprecationWarning`` if used in Django 1.3. In Django 1.3, this
     1628warning will be updated to a ``DeprecationWarning`` which will be displayed
     1629loudly. Additionally ``supports_inactive_user`` will be set to ``False``.
     1630Django 1.5 will assyme that every backend supports inactive users being
     1631passed to the authorization methods.
     1632
     1633
    16091634Handling object permissions
    16101635---------------------------
    16111636
Back to Top