Author: jacob
Date: 2007-09-19 11:50:30 -0500 (Wed, 19 Sep 2007)
New Revision: 6375

Added:
   django/trunk/tests/regressiontests/auth_backends/
   django/trunk/tests/regressiontests/auth_backends/__init__.py
   django/trunk/tests/regressiontests/auth_backends/models.py
   django/trunk/tests/regressiontests/auth_backends/tests.py
Modified:
   django/trunk/AUTHORS
   django/trunk/django/contrib/auth/backends.py
   django/trunk/django/contrib/auth/models.py
   django/trunk/docs/authentication.txt
Log:
Fixed $5457 - the auth system now delegates permission checking to auth 
backend(s). As an added bonus, the auth backends now have some unit tests! 
Thanks, Florian Apolloner.

Modified: django/trunk/AUTHORS
===================================================================
--- django/trunk/AUTHORS        2007-09-19 13:26:56 UTC (rev 6374)
+++ django/trunk/AUTHORS        2007-09-19 16:50:30 UTC (rev 6375)
@@ -49,6 +49,7 @@
     [EMAIL PROTECTED]
     Fabrice Aneche <[EMAIL PROTECTED]>
     [EMAIL PROTECTED]
+    Florian Apolloner 
     David Ascher <http://ascher.ca/>
     [EMAIL PROTECTED]
     Arthur <[EMAIL PROTECTED]>

Modified: django/trunk/django/contrib/auth/backends.py
===================================================================
--- django/trunk/django/contrib/auth/backends.py        2007-09-19 13:26:56 UTC 
(rev 6374)
+++ django/trunk/django/contrib/auth/backends.py        2007-09-19 16:50:30 UTC 
(rev 6375)
@@ -1,3 +1,4 @@
+from django.db import connection
 from django.contrib.auth.models import User
 
 class ModelBackend:
@@ -14,6 +15,49 @@
         except User.DoesNotExist:
             return None
 
+    def get_group_permissions(self, user_obj):
+        "Returns a list of permission strings that this user has through 
his/her groups."
+        if not hasattr(user_obj, '_group_perm_cache'):
+            cursor = connection.cursor()
+            # The SQL below works out to the following, after DB quoting:
+            # cursor.execute("""
+            #     SELECT ct."app_label", p."codename"
+            #     FROM "auth_permission" p, "auth_group_permissions" gp, 
"auth_user_groups" ug, "django_content_type" ct
+            #     WHERE p."id" = gp."permission_id"
+            #         AND gp."group_id" = ug."group_id"
+            #         AND ct."id" = p."content_type_id"
+            #         AND ug."user_id" = %s, [self.id])
+            qn = connection.ops.quote_name
+            sql = """
+                SELECT ct.%s, p.%s
+                FROM %s p, %s gp, %s ug, %s ct
+                WHERE p.%s = gp.%s
+                    AND gp.%s = ug.%s
+                    AND ct.%s = p.%s
+                    AND ug.%s = %%s""" % (
+                qn('app_label'), qn('codename'),
+                qn('auth_permission'), qn('auth_group_permissions'),
+                qn('auth_user_groups'), qn('django_content_type'),
+                qn('id'), qn('permission_id'),
+                qn('group_id'), qn('group_id'),
+                qn('id'), qn('content_type_id'),
+                qn('user_id'),)
+            cursor.execute(sql, [user_obj.id])
+            user_obj._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for 
row in cursor.fetchall()])
+        return user_obj._group_perm_cache
+    
+    def get_all_permissions(self, user_obj):
+        if not hasattr(user_obj, '_perm_cache'):
+            user_obj._perm_cache = set([u"%s.%s" % (p.content_type.app_label, 
p.codename) for p in user_obj.user_permissions.select_related()])
+            user_obj._perm_cache.update(self.get_group_permissions(user_obj))
+        return user_obj._perm_cache
+
+    def has_perm(self, user_obj, perm):
+        return perm in self.get_all_permissions(user_obj)
+
+    def has_module_perms(self, user_obj, app_label):
+        return bool(len([p for p in self.get_all_permissions(user_obj) if 
p[:p.index('.')] == app_label]))
+
     def get_user(self, user_id):
         try:
             return User.objects.get(pk=user_id)

Modified: django/trunk/django/contrib/auth/models.py
===================================================================
--- django/trunk/django/contrib/auth/models.py  2007-09-19 13:26:56 UTC (rev 
6374)
+++ django/trunk/django/contrib/auth/models.py  2007-09-19 16:50:30 UTC (rev 
6375)
@@ -1,6 +1,7 @@
+from django.contrib import auth
 from django.core import validators
 from django.core.exceptions import ImproperlyConfigured
-from django.db import connection, models
+from django.db import models
 from django.db.models.manager import EmptyManager
 from django.contrib.contenttypes.models import ContentType
 from django.utils.encoding import smart_str
@@ -210,65 +211,69 @@
         return self.password != UNUSABLE_PASSWORD
 
     def get_group_permissions(self):
-        "Returns a list of permission strings that this user has through 
his/her groups."
-        if not hasattr(self, '_group_perm_cache'):
-            cursor = connection.cursor()
-            # The SQL below works out to the following, after DB quoting:
-            # cursor.execute("""
-            #     SELECT ct."app_label", p."codename"
-            #     FROM "auth_permission" p, "auth_group_permissions" gp, 
"auth_user_groups" ug, "django_content_type" ct
-            #     WHERE p."id" = gp."permission_id"
-            #         AND gp."group_id" = ug."group_id"
-            #         AND ct."id" = p."content_type_id"
-            #         AND ug."user_id" = %s, [self.id])
-            qn = connection.ops.quote_name
-            sql = """
-                SELECT ct.%s, p.%s
-                FROM %s p, %s gp, %s ug, %s ct
-                WHERE p.%s = gp.%s
-                    AND gp.%s = ug.%s
-                    AND ct.%s = p.%s
-                    AND ug.%s = %%s""" % (
-                qn('app_label'), qn('codename'),
-                qn('auth_permission'), qn('auth_group_permissions'),
-                qn('auth_user_groups'), qn('django_content_type'),
-                qn('id'), qn('permission_id'),
-                qn('group_id'), qn('group_id'),
-                qn('id'), qn('content_type_id'),
-                qn('user_id'),)
-            cursor.execute(sql, [self.id])
-            self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row 
in cursor.fetchall()])
-        return self._group_perm_cache
+        """
+        Returns a list of permission strings that this user has through
+        his/her groups. This method queries all available auth backends.
+        """
+        permissions = set()
+        for backend in auth.get_backends():
+            if hasattr(backend, "get_group_permissions"):
+                permissions.update(backend.get_group_permissions(self))
+        return permissions
 
     def get_all_permissions(self):
-        if not hasattr(self, '_perm_cache'):
-            self._perm_cache = set([u"%s.%s" % (p.content_type.app_label, 
p.codename) for p in self.user_permissions.select_related()])
-            self._perm_cache.update(self.get_group_permissions())
-        return self._perm_cache
+        permissions = set()
+        for backend in auth.get_backends():
+            if hasattr(backend, "get_all_permissions"):
+                permissions.update(backend.get_all_permissions(self))
+        return permissions 
 
     def has_perm(self, perm):
-        "Returns True if the user has the specified permission."
+        """
+        Returns True if the user has the specified permission. This method
+        queries all available auth backends, but returns immediately if any
+        backend returns True. Thus, a user who has permission from a single
+        auth backend is assumed to have permission in general.
+        """
+        # Inactive users have no permissions.
         if not self.is_active:
             return False
+        
+        # Superusers have all permissions.
         if self.is_superuser:
             return True
-        return perm in self.get_all_permissions()
+            
+        # Otherwise we need to check the backends.
+        for backend in auth.get_backends():
+            if hasattr(backend, "has_perm"):
+                if backend.has_perm(self, perm):
+                    return True
+        return False
 
     def has_perms(self, perm_list):
-        "Returns True if the user has each of the specified permissions."
+        """Returns True if the user has each of the specified permissions."""
         for perm in perm_list:
             if not self.has_perm(perm):
                 return False
         return True
 
     def has_module_perms(self, app_label):
-        "Returns True if the user has any permissions in the given app label."
+        """
+        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:
             return True
-        return bool(len([p for p in self.get_all_permissions() if 
p[:p.index('.')] == app_label]))
 
+        for backend in auth.get_backends():
+            if hasattr(backend, "has_module_perms"):
+                if backend.has_module_perms(self, app_label):
+                    return True
+        return False
+
     def get_and_delete_messages(self):
         messages = []
         for m in self.message_set.all():
@@ -300,7 +305,12 @@
 
 class Message(models.Model):
     """
-    The message system is a lightweight way to queue messages for given users. 
A message is associated with a User instance (so it is only applicable for 
registered users). There's no concept of expiration or timestamps. Messages are 
created by the Django admin after successful actions. For example, "The poll 
Foo was created successfully." is a message.
+    The message system is a lightweight way to queue messages for given
+    users. A message is associated with a User instance (so it is only
+    applicable for registered users). There's no concept of expiration or
+    timestamps. Messages are created by the Django admin after successful
+    actions. For example, "The poll Foo was created successfully." is a
+    message.
     """
     user = models.ForeignKey(User)
     message = models.TextField(_('message'))

Modified: django/trunk/docs/authentication.txt
===================================================================
--- django/trunk/docs/authentication.txt        2007-09-19 13:26:56 UTC (rev 
6374)
+++ django/trunk/docs/authentication.txt        2007-09-19 16:50:30 UTC (rev 
6375)
@@ -1062,3 +1062,40 @@
                 return User.objects.get(pk=user_id)
             except User.DoesNotExist:
                 return None
+
+Handling authorization in custom backends
+-----------------------------------------
+
+Custom auth backends can provide their own permissions. 
+
+The user model will delegate permission lookup functions
+(``get_group_permissions()``, ``get_all_permissions()``, ``has_perm()``, and
+``has_module_perms()``) to any authentication backend that implements these
+functions.
+
+The permissions given to the user will be the superset of all permissions
+returned by all backends. That is, Django grants a permission to a user that 
any
+one backend grants.
+
+The simple backend above could implement permissions for the magic admin fairly
+simply::
+        
+    class SettingsBackend:
+    
+        # ...
+
+        def has_perm(self, user_obj, perm):
+            if user_obj.username == settings.ADMIN_LOGIN:
+                return True
+            else:
+                return False
+                
+This gives full permissions to user granted access in the above example. Notice
+that the backend auth functions all take the user object as an argument, and
+also accept the same arguments given to the associated ``User`` functions.
+
+A full authorization implementation can be found in
+``django/contrib/auth/backends.py`` _, which is the default backend and queries
+the ``auth_permission``-table most of the time.
+
+.. _django/contrib/auth/backends.py: 
http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py

Added: django/trunk/tests/regressiontests/auth_backends/__init__.py
===================================================================

Added: django/trunk/tests/regressiontests/auth_backends/models.py
===================================================================

Added: django/trunk/tests/regressiontests/auth_backends/tests.py
===================================================================
--- django/trunk/tests/regressiontests/auth_backends/tests.py                   
        (rev 0)
+++ django/trunk/tests/regressiontests/auth_backends/tests.py   2007-09-19 
16:50:30 UTC (rev 6375)
@@ -0,0 +1,66 @@
+"""
+>>> from django.contrib.auth.models import User, Group, Permission
+>>> from django.contrib.contenttypes.models import ContentType
+
+# No Permissions assigned yet, should return False except for superuser
+
+>>> user = User.objects.create_user('test', '[EMAIL PROTECTED]', 'test')
+>>> user.has_perm("auth.test")
+False
+>>> user.is_staff=True
+>>> user.save()
+>>> user.has_perm("auth.test")
+False
+>>> user.is_superuser=True
+>>> user.save()
+>>> user.has_perm("auth.test")
+True
+>>> user.is_staff = False
+>>> user.is_superuser = False
+>>> user.save()
+>>> user.has_perm("auth.test")
+False
+>>> content_type=ContentType.objects.get_for_model(Group)
+>>> perm = Permission.objects.create(name="test", content_type=content_type, 
codename="test")
+>>> user.user_permissions.add(perm)
+>>> user.save()
+
+# reloading user to purge the _perm_cache
+
+>>> user = User.objects.get(username="test")
+>>> user.get_all_permissions()
+set([u'auth.test'])
+>>> user.get_group_permissions()
+set([])
+>>> user.has_module_perms("Group")
+False
+>>> user.has_module_perms("auth")
+True
+>>> perm = Permission.objects.create(name="test2", content_type=content_type, 
codename="test2")
+>>> user.user_permissions.add(perm)
+>>> user.save()
+>>> perm = Permission.objects.create(name="test3", content_type=content_type, 
codename="test3")
+>>> user.user_permissions.add(perm)
+>>> user.save()
+>>> user = User.objects.get(username="test")
+>>> user.get_all_permissions()
+set([u'auth.test2', u'auth.test', u'auth.test3'])
+>>> user.has_perm('test')
+False
+>>> user.has_perm('auth.test')
+True
+>>> user.has_perms(['auth.test2', 'auth.test3'])
+True
+>>> perm = Permission.objects.create(name="test_group", 
content_type=content_type, codename="test_group")
+>>> group = Group.objects.create(name='test_group')
+>>> group.permissions.add(perm)
+>>> group.save()
+>>> user.groups.add(group)
+>>> user = User.objects.get(username="test")
+>>> user.get_all_permissions()
+set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
+>>> user.get_group_permissions()
+set([u'auth.test_group'])
+>>> user.has_perms(['auth.test3', 'auth.test_group'])
+True
+"""
\ No newline at end of file


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to