Ticket #11010: rowlevel_perms@12123_#11010.diff

File rowlevel_perms@12123_#11010.diff, 16.6 KB (added by Florian Apolloner, 14 years ago)
  • django/contrib/auth/backends.py

    diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py
    index 05f9835..df73a29 100644
    a b class ModelBackend(object):  
    1111    """
    1212    Authenticates against django.contrib.auth.models.User.
    1313    """
     14    supports_object_perms = False
     15
    1416    # TODO: Model, login attribute name and password attribute name should be
    1517    # configurable.
    1618    def authenticate(self, username=None, password=None):
  • django/contrib/auth/models.py

    diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
    index e337bec..e969cdd 100644
    a b  
    11import datetime
    22import urllib
    33
     4from warnings import warn as warn_
     5
    46from django.contrib import auth
    57from django.core.exceptions import ImproperlyConfigured
    68from django.db import models
    from django.utils.translation import ugettext_lazy as _  
    1214
    1315UNUSABLE_PASSWORD = '!' # This will never be a valid hash
    1416
     17def check_and_warn(backend):
     18    if getattr(backend, 'supports_object_perms', False):
     19        return True
     20    else:
     21        warn_("%s should define `supports_object_perms`." % backend,
     22          PendingDeprecationWarning, stacklevel=3)
     23        return False
     24
    1525try:
    1626    set
    1727except NameError:
    class User(models.Model):  
    194204    def has_usable_password(self):
    195205        return self.password != UNUSABLE_PASSWORD
    196206
    197     def get_group_permissions(self):
     207    def get_group_permissions(self, obj=None):
    198208        """
    199209        Returns a list of permission strings that this user has through
    200210        his/her groups. This method queries all available auth backends.
     211        If an object is passed in, only permissions matching this object
     212        are returned.
    201213        """
    202214        permissions = set()
    203215        for backend in auth.get_backends():
    204216            if hasattr(backend, "get_group_permissions"):
    205                 permissions.update(backend.get_group_permissions(self))
     217                if obj:
     218                    if check_and_warn(backend):
     219                        permissions.update(backend.get_group_permissions(self, obj))
     220                else:
     221                    permissions.update(backend.get_group_permissions(self))
    206222        return permissions
    207223
    208     def get_all_permissions(self):
     224    def get_all_permissions(self, obj=None):
    209225        permissions = set()
    210226        for backend in auth.get_backends():
    211227            if hasattr(backend, "get_all_permissions"):
    212                 permissions.update(backend.get_all_permissions(self))
     228                if obj:
     229                    if check_and_warn(backend):
     230                        permissions.update(backend.get_all_permissions(self, obj))
     231                else:
     232                    permissions.update(backend.get_all_permissions(self))
    213233        return permissions
    214234
    215     def has_perm(self, perm):
     235    def has_perm(self, perm, obj=None):
    216236        """
    217237        Returns True if the user has the specified permission. This method
    218238        queries all available auth backends, but returns immediately if any
    219239        backend returns True. Thus, a user who has permission from a single
    220         auth backend is assumed to have permission in general.
     240        auth backend is assumed to have permission in general. If an object
     241        is provided, permissions for this specific object are checked.
    221242        """
    222243        # Inactive users have no permissions.
    223244        if not self.is_active:
    class User(models.Model):  
    230251        # Otherwise we need to check the backends.
    231252        for backend in auth.get_backends():
    232253            if hasattr(backend, "has_perm"):
    233                 if backend.has_perm(self, perm):
    234                     return True
     254                if obj:
     255                    if check_and_warn(backend):
     256                        if backend.has_perm(self, perm, obj):
     257                            return True
     258                else:
     259                    if backend.has_perm(self, perm):
     260                        return True
    235261        return False
    236262
    237     def has_perms(self, perm_list):
    238         """Returns True if the user has each of the specified permissions."""
     263    def has_perms(self, perm_list, obj=None):
     264        """Returns True if the user has each of the specified permissions.
     265        If object is passed, it checks if the user has all required perms
     266        for this object.
     267        """
    239268        for perm in perm_list:
    240             if not self.has_perm(perm):
     269            if not self.has_perm(perm, obj):
    241270                return False
    242271        return True
    243272
  • django/contrib/auth/tests/__init__.py

    diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py
    index 14428d0..9a078cf 100644
    a b from django.contrib.auth.tests.views \  
    44from django.contrib.auth.tests.forms import FORM_TESTS
    55from django.contrib.auth.tests.remote_user \
    66        import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest
     7from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest
    78from django.contrib.auth.tests.tokens import TOKEN_GENERATOR_TESTS
    89
    910# The password for the fixture data users is 'password'
  • new file django/contrib/auth/tests/auth_backends.py

    diff --git a/django/contrib/auth/tests/auth_backends.py b/django/contrib/auth/tests/auth_backends.py
    new file mode 100644
    index 0000000..3ee07a2
    - +  
     1from django.conf import settings
     2from django.contrib.auth.backends import ModelBackend
     3from django.contrib.auth.models import User, Group, Permission, AnonymousUser
     4from django.contrib.contenttypes.models import ContentType
     5
     6from django.test import TestCase
     7
     8try:
     9    set
     10except NameError:
     11    from sets import Set as set     # Python 2.3 fallback
     12
     13class BackendTest(TestCase):
     14
     15    backend = 'django.contrib.auth.backends.ModelBackend'
     16
     17    def setUp(self):
     18        self.curr_auth = settings.AUTHENTICATION_BACKENDS
     19        settings.AUTHENTICATION_BACKENDS = (self.backend,)
     20        User.objects.create_user('test', 'test@example.com', 'test')
     21
     22
     23    def tearDown(self):
     24        settings.AUTHENTICATION_BACKENDS = self.curr_auth
     25
     26    def test_has_perm(self):
     27        user = User.objects.get(username="test")
     28        self.assertEqual(user.has_perm("auth.test"), False)
     29        user.is_staff = True
     30        user.save()
     31        self.assertEqual(user.has_perm("auth.test"), False)
     32        user.is_superuser = True
     33        user.save()
     34        self.assertEqual(user.has_perm("auth.test"), True)
     35        user.is_staff = False
     36        user.is_superuser = False
     37        user.save()
     38        self.assertEqual(user.has_perm("auth.test"), False)
     39
     40    def test_custom_perms(self):
     41        user = User.objects.get(username="test")
     42        content_type=ContentType.objects.get_for_model(Group)
     43        perm = Permission.objects.create(name="test", content_type=content_type, codename="test")
     44        user.user_permissions.add(perm)
     45        user.save()
     46
     47        # reloading user to purge the _perm_cache
     48
     49        user = User.objects.get(username="test")
     50        self.assertEqual(user.get_all_permissions() == set([u'auth.test']), True)
     51        self.assertEqual(user.get_group_permissions(), set([]))
     52        self.assertEqual(user.has_module_perms("Group"), False)
     53        self.assertEqual(user.has_module_perms("auth"), True)
     54        perm = Permission.objects.create(name="test2", content_type=content_type, codename="test2")
     55        user.user_permissions.add(perm)
     56        user.save()
     57        perm = Permission.objects.create(name="test3", content_type=content_type, codename="test3")
     58        user.user_permissions.add(perm)
     59        user.save()
     60        user = User.objects.get(username="test")
     61        self.assertEqual(user.get_all_permissions(), set([u'auth.test2', u'auth.test', u'auth.test3']))
     62        self.assertEqual(user.has_perm('test'), False)
     63        self.assertEqual(user.has_perm('auth.test'), True)
     64        self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True)
     65        perm = Permission.objects.create(name="test_group", content_type=content_type, codename="test_group")
     66        group = Group.objects.create(name='test_group')
     67        group.permissions.add(perm)
     68        group.save()
     69        user.groups.add(group)
     70        user = User.objects.get(username="test")
     71        exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
     72        self.assertEqual(user.get_all_permissions(), exp)
     73        self.assertEqual(user.get_group_permissions(), set([u'auth.test_group']))
     74        self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True)
     75
     76        user = AnonymousUser()
     77        self.assertEqual(user.has_perm('test'), False)
     78        self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), False)
     79
     80class TestObj(object):
     81    pass
     82
     83class SimpleRowlevelBackend(object):
     84    supports_object_perms = True
     85
     86    def has_perm(self, user, perm, obj=None):
     87        if not obj:
     88            return # We only support row level perms
     89
     90        if isinstance(obj, TestObj) and user.username == "apollo13":
     91            return True
     92        else:
     93            return False
     94
     95    def get_all_permissions(self, user, obj=None):
     96        if not obj:
     97            return [] # We only support row level perms
     98
     99        if not isinstance(obj, TestObj):
     100            return ["none"]
     101
     102        if user.username == "apollo13":
     103            return ["simple", "advanced"]
     104        else:
     105            return ["simple"]
     106
     107class RowlevelBackendTest(TestCase):
     108
     109    backend = 'django.contrib.auth.tests.auth_backends.SimpleRowlevelBackend'
     110
     111    def setUp(self):
     112        self.curr_auth = settings.AUTHENTICATION_BACKENDS
     113        settings.AUTHENTICATION_BACKENDS = self.curr_auth + (self.backend,)
     114        self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
     115        self.user2 = User.objects.create_user('apollo13', 'test2@example.com', 'test')
     116
     117    def tearDown(self):
     118        settings.AUTHENTICATION_BACKENDS = self.curr_auth
     119
     120    def test_has_perm(self):
     121        self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
     122        self.assertEqual(self.user2.has_perm('perm', TestObj()), True)
     123        self.assertEqual(self.user2.has_perm('perm'), False)
     124
     125    def test_get_all_permissions(self):
     126        self.assertEqual(self.user1.get_all_permissions(TestObj()), set(["simple"]))
     127        self.assertEqual(self.user2.get_all_permissions(TestObj()), set(["simple", "advanced"]))
     128        self.assertEqual(self.user2.get_all_permissions(), set([]))
  • docs/topics/auth.txt

    diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
    index 33461a0..9baa732 100644
    a b Methods  
    199199        :meth:`~django.contrib.auth.models.User.set_unusable_password()` has
    200200        been called for this user.
    201201
    202     .. method:: models.User.get_group_permissions()
     202    .. method:: models.User.get_group_permissions(obj=None)
    203203
    204204        Returns a list of permission strings that the user has, through his/her
    205         groups.
     205        groups. If ``obj`` is passed in, only returns the group permissions for
     206        this specific object.
    206207
    207     .. method:: models.User.get_all_permissions()
     208    .. method:: models.User.get_all_permissions(obj=None)
    208209
    209210        Returns a list of permission strings that the user has, both through
    210         group and user permissions.
     211        group and user permissions. If ``obj`` is passed in, only returns the
     212        permissions for this specific object.
    211213
    212     .. method:: models.User.has_perm(perm)
     214
     215    .. method:: models.User.has_perm(perm, obj=None)
    213216
    214217        Returns ``True`` if the user has the specified permission, where perm is
    215218        in the format ``"<app label>.<permission codename>"``.
    216         If the user is inactive, this method will always return ``False``.
     219        If the user is inactive, this method will always return ``False``. If
     220        ``obj`` is passed in this method won't check the permissions for the model,
     221        but the object.
    217222
    218     .. method:: models.User.has_perms(perm_list)
     223    .. method:: models.User.has_perms(perm_list, obj=None)
    219224
    220225        Returns ``True`` if the user has each of the specified permissions,
    221226        where each perm is in the format
    222227        ``"<app label>.<permission codename>"``. If the user is inactive,
    223         this method will always return ``False``.
     228        this method will always return ``False``. If ``obj`` is passed in
     229        this method won't check the permissions for the model, but the object.
    224230
    225231    .. method:: models.User.has_module_perms(package_name)
    226232
    A full authorization implementation can be found in  
    15101516the ``auth_permission`` table most of the time.
    15111517
    15121518.. _django/contrib/auth/backends.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py
     1519
     1520Handling object permissions
     1521---------------------------
     1522
     1523Django's permission framework has a foundation for object permissions, though
     1524there is no implementation for it in the core. This means, that checking for
     1525object permissions will always return ``False`` or an empty list (depending on
     1526the check performed). To enable object permissions you will have to write your
     1527own Backend which does support them, the only change you have to do is to add
     1528an ``obj`` parameter to the permission functions and set ``supports_objects_perms``
     1529to ``True``. In Django 1.2 a non existing ``supports_objects_perms`` will raise a
     1530``PendingDeprecationWarning``. In 1.3 a non existant ``support_object_perms``
     1531attribute will raise an error and setting it to ``False`` will raise a
     1532``DeprecationWarning``. In 1.4 it's assumed that every backend supports object
     1533permissions and no checking is performed, which means not supporting ``obj`` as
     1534parameter will raise a ``TypeError``.
  • deleted file tests/regressiontests/auth_backends/tests.py

    diff --git a/tests/regressiontests/auth_backends/__init__.py b/tests/regressiontests/auth_backends/__init__.py
    deleted file mode 100644
    index e69de29..0000000
    diff --git a/tests/regressiontests/auth_backends/models.py b/tests/regressiontests/auth_backends/models.py
    deleted file mode 100644
    index e69de29..0000000
    diff --git a/tests/regressiontests/auth_backends/tests.py b/tests/regressiontests/auth_backends/tests.py
    deleted file mode 100644
    index d22f0bf..0000000
    + -  
    1 try:
    2     set
    3 except NameError:
    4     from sets import Set as set     # Python 2.3 fallback
    5 
    6 __test__ = {'API_TESTS': """
    7 >>> from django.contrib.auth.models import User, Group, Permission, AnonymousUser
    8 >>> from django.contrib.contenttypes.models import ContentType
    9 
    10 # No Permissions assigned yet, should return False except for superuser
    11 
    12 >>> user = User.objects.create_user('test', 'test@example.com', 'test')
    13 >>> user.has_perm("auth.test")
    14 False
    15 >>> user.is_staff=True
    16 >>> user.save()
    17 >>> user.has_perm("auth.test")
    18 False
    19 >>> user.is_superuser=True
    20 >>> user.save()
    21 >>> user.has_perm("auth.test")
    22 True
    23 >>> user.is_staff = False
    24 >>> user.is_superuser = False
    25 >>> user.save()
    26 >>> user.has_perm("auth.test")
    27 False
    28 >>> content_type=ContentType.objects.get_for_model(Group)
    29 >>> perm = Permission.objects.create(name="test", content_type=content_type, codename="test")
    30 >>> user.user_permissions.add(perm)
    31 >>> user.save()
    32 
    33 # reloading user to purge the _perm_cache
    34 
    35 >>> user = User.objects.get(username="test")
    36 >>> user.get_all_permissions() == set([u'auth.test'])
    37 True
    38 >>> user.get_group_permissions() == set([])
    39 True
    40 >>> user.has_module_perms("Group")
    41 False
    42 >>> user.has_module_perms("auth")
    43 True
    44 >>> perm = Permission.objects.create(name="test2", content_type=content_type, codename="test2")
    45 >>> user.user_permissions.add(perm)
    46 >>> user.save()
    47 >>> perm = Permission.objects.create(name="test3", content_type=content_type, codename="test3")
    48 >>> user.user_permissions.add(perm)
    49 >>> user.save()
    50 >>> user = User.objects.get(username="test")
    51 >>> user.get_all_permissions() == set([u'auth.test2', u'auth.test', u'auth.test3'])
    52 True
    53 >>> user.has_perm('test')
    54 False
    55 >>> user.has_perm('auth.test')
    56 True
    57 >>> user.has_perms(['auth.test2', 'auth.test3'])
    58 True
    59 >>> perm = Permission.objects.create(name="test_group", content_type=content_type, codename="test_group")
    60 >>> group = Group.objects.create(name='test_group')
    61 >>> group.permissions.add(perm)
    62 >>> group.save()
    63 >>> user.groups.add(group)
    64 >>> user = User.objects.get(username="test")
    65 >>> exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
    66 >>> user.get_all_permissions() == exp
    67 True
    68 >>> user.get_group_permissions() == set([u'auth.test_group'])
    69 True
    70 >>> user.has_perms(['auth.test3', 'auth.test_group'])
    71 True
    72 
    73 >>> user = AnonymousUser()
    74 >>> user.has_perm('test')
    75 False
    76 >>> user.has_perms(['auth.test2', 'auth.test3'])
    77 False
    78 """}
Back to Top