Index: django/contrib/auth/backends.py =================================================================== --- django/contrib/auth/backends.py (revision 6184) +++ django/contrib/auth/backends.py (working copy) @@ -1,3 +1,4 @@ +from django.db import connection, models from django.contrib.auth.models import User class ModelBackend: @@ -14,6 +15,51 @@ 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) Index: django/contrib/auth/models.py =================================================================== --- django/contrib/auth/models.py (revision 6184) +++ django/contrib/auth/models.py (working copy) @@ -1,3 +1,4 @@ +from django.contrib.auth import get_backends from django.core import validators from django.core.exceptions import ImproperlyConfigured from django.db import connection, models @@ -177,49 +178,41 @@ 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 the available backends. + """ + group_permissions = set([]) + for backend in get_backends(): + # Backend does not support this method, so skip it + if not hasattr(backend, "get_group_permissions"): continue + group_permissions.update(backend.get_group_permissions(self)) + return group_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 get_backends(): + # Backend does not support this method, so skip it + if not hasattr(backend, "get_all_permissions"): continue + 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 the available backends and stops on + the first "True". + """ if not self.is_active: return False if self.is_superuser: return True - return perm in self.get_all_permissions() + for backend in get_backends(): + # Backend does not support this method, so skip it + if not hasattr(backend, "has_perm"): continue + 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." @@ -229,12 +222,21 @@ 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. + This method queries the available backends and stops on + the first "True". + """ 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 get_backends(): + # Backend does not support this method, so skip it + if not hasattr(backend, "has_module_perms"): continue + if backend.has_module_perms(self, app_label): + return True + return False def get_and_delete_messages(self): messages = [] Index: docs/authentication.txt =================================================================== --- docs/authentication.txt (revision 6184) +++ docs/authentication.txt (working copy) @@ -1041,3 +1041,26 @@ return User.objects.get(pk=user_id) except User.DoesNotExist: return None + +Optionally the Backend allows you to specify any of the permission methods of +the ``User`` object (except from ``has_perms`` which calls ``has_perm`` for each +perm in the list, and ``has_perm`` can be specified in the backend). +This will allow your backend to handle the permissions, if one backend return +``True`` in a permission check, the user will have the permission. + +Here is an sample implementation for ``has_perm`` extending the above exmaple:: + + # The permission functions all take the user_obj as first (besides the self of + # the backend, of course) argument and the rest + # is as you would call it with ``User.*`` + def has_perm(self, user_obj, perm): + # we give the user admin the right to do everything. + if user_obj.username == "admin": + return True + else: + return False + +A full 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