Django

Code

Changeset 6375

Show
Ignore:
Timestamp:
09/19/07 11:50:30 (1 year ago)
Author:
jacob
Message:

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.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/AUTHORS

    r6370 r6375  
    5050    Fabrice Aneche <akh@nobugware.com> 
    5151    ant9000@netwise.it 
     52    Florian Apolloner  
    5253    David Ascher <http://ascher.ca/> 
    5354    david@kazserve.org 
  • django/trunk/django/contrib/auth/backends.py

    r4265 r6375  
     1from django.db import connection 
    12from django.contrib.auth.models import User 
    23 
     
    1516            return None 
    1617 
     18    def get_group_permissions(self, user_obj): 
     19        "Returns a list of permission strings that this user has through his/her groups." 
     20        if not hasattr(user_obj, '_group_perm_cache'): 
     21            cursor = connection.cursor() 
     22            # The SQL below works out to the following, after DB quoting: 
     23            # cursor.execute(""" 
     24            #     SELECT ct."app_label", p."codename" 
     25            #     FROM "auth_permission" p, "auth_group_permissions" gp, "auth_user_groups" ug, "django_content_type" ct 
     26            #     WHERE p."id" = gp."permission_id" 
     27            #         AND gp."group_id" = ug."group_id" 
     28            #         AND ct."id" = p."content_type_id" 
     29            #         AND ug."user_id" = %s, [self.id]) 
     30            qn = connection.ops.quote_name 
     31            sql = """ 
     32                SELECT ct.%s, p.%s 
     33                FROM %s p, %s gp, %s ug, %s ct 
     34                WHERE p.%s = gp.%s 
     35                    AND gp.%s = ug.%s 
     36                    AND ct.%s = p.%s 
     37                    AND ug.%s = %%s""" % ( 
     38                qn('app_label'), qn('codename'), 
     39                qn('auth_permission'), qn('auth_group_permissions'), 
     40                qn('auth_user_groups'), qn('django_content_type'), 
     41                qn('id'), qn('permission_id'), 
     42                qn('group_id'), qn('group_id'), 
     43                qn('id'), qn('content_type_id'), 
     44                qn('user_id'),) 
     45            cursor.execute(sql, [user_obj.id]) 
     46            user_obj._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()]) 
     47        return user_obj._group_perm_cache 
     48     
     49    def get_all_permissions(self, user_obj): 
     50        if not hasattr(user_obj, '_perm_cache'): 
     51            user_obj._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()]) 
     52            user_obj._perm_cache.update(self.get_group_permissions(user_obj)) 
     53        return user_obj._perm_cache 
     54 
     55    def has_perm(self, user_obj, perm): 
     56        return perm in self.get_all_permissions(user_obj) 
     57 
     58    def has_module_perms(self, user_obj, app_label): 
     59        return bool(len([p for p in self.get_all_permissions(user_obj) if p[:p.index('.')] == app_label])) 
     60 
    1761    def get_user(self, user_id): 
    1862        try: 
  • django/trunk/django/contrib/auth/models.py

    r6318 r6375  
     1from django.contrib import auth 
    12from django.core import validators 
    23from django.core.exceptions import ImproperlyConfigured 
    3 from django.db import connection, models 
     4from django.db import models 
    45from django.db.models.manager import EmptyManager 
    56from django.contrib.contenttypes.models import ContentType 
     
    211212 
    212213    def get_group_permissions(self): 
    213         "Returns a list of permission strings that this user has through his/her groups." 
    214         if not hasattr(self, '_group_perm_cache'): 
    215             cursor = connection.cursor() 
    216             # The SQL below works out to the following, after DB quoting: 
    217             # cursor.execute(""" 
    218             #     SELECT ct."app_label", p."codename" 
    219             #     FROM "auth_permission" p, "auth_group_permissions" gp, "auth_user_groups" ug, "django_content_type" ct 
    220             #     WHERE p."id" = gp."permission_id" 
    221             #         AND gp."group_id" = ug."group_id" 
    222             #         AND ct."id" = p."content_type_id" 
    223             #         AND ug."user_id" = %s, [self.id]) 
    224             qn = connection.ops.quote_name 
    225             sql = """ 
    226                 SELECT ct.%s, p.%s 
    227                 FROM %s p, %s gp, %s ug, %s ct 
    228                 WHERE p.%s = gp.%s 
    229                     AND gp.%s = ug.%s 
    230                     AND ct.%s = p.%s 
    231                     AND ug.%s = %%s""" % ( 
    232                 qn('app_label'), qn('codename'), 
    233                 qn('auth_permission'), qn('auth_group_permissions'), 
    234                 qn('auth_user_groups'), qn('django_content_type'), 
    235                 qn('id'), qn('permission_id'), 
    236                 qn('group_id'), qn('group_id'), 
    237                 qn('id'), qn('content_type_id'), 
    238                 qn('user_id'),) 
    239             cursor.execute(sql, [self.id]) 
    240             self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()]) 
    241         return self._group_perm_cache 
     214        """ 
     215        Returns a list of permission strings that this user has through 
     216        his/her groups. This method queries all available auth backends. 
     217        """ 
     218        permissions = set() 
     219        for backend in auth.get_backends(): 
     220            if hasattr(backend, "get_group_permissions"): 
     221                permissions.update(backend.get_group_permissions(self)) 
     222        return permissions 
    242223 
    243224    def get_all_permissions(self): 
    244         if not hasattr(self, '_perm_cache'): 
    245             self._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in self.user_permissions.select_related()]) 
    246             self._perm_cache.update(self.get_group_permissions()) 
    247         return self._perm_cache 
     225        permissions = set() 
     226        for backend in auth.get_backends(): 
     227            if hasattr(backend, "get_all_permissions"): 
     228                permissions.update(backend.get_all_permissions(self)) 
     229        return permissions  
    248230 
    249231    def has_perm(self, perm): 
    250         "Returns True if the user has the specified permission." 
     232        """ 
     233        Returns True if the user has the specified permission. This method 
     234        queries all available auth backends, but returns immediately if any 
     235        backend returns True. Thus, a user who has permission from a single 
     236        auth backend is assumed to have permission in general. 
     237        """ 
     238        # Inactive users have no permissions. 
    251239        if not self.is_active: 
    252240            return False 
     241         
     242        # Superusers have all permissions. 
    253243        if self.is_superuser: 
    254244            return True 
    255         return perm in self.get_all_permissions() 
     245             
     246        # Otherwise we need to check the backends. 
     247        for backend in auth.get_backends(): 
     248            if hasattr(backend, "has_perm"): 
     249                if backend.has_perm(self, perm): 
     250                    return True 
     251        return False 
    256252 
    257253    def has_perms(self, perm_list): 
    258         "Returns True if the user has each of the specified permissions.
     254        """Returns True if the user has each of the specified permissions.""
    259255        for perm in perm_list: 
    260256            if not self.has_perm(perm): 
     
    263259 
    264260    def has_module_perms(self, app_label): 
    265         "Returns True if the user has any permissions in the given app label." 
     261        """ 
     262        Returns True if the user has any permissions in the given app 
     263        label. Uses pretty much the same logic as has_perm, above. 
     264        """ 
    266265        if not self.is_active: 
    267266            return False 
     267 
    268268        if self.is_superuser: 
    269269            return True 
    270         return bool(len([p for p in self.get_all_permissions() if p[:p.index('.')] == app_label])) 
     270 
     271        for backend in auth.get_backends(): 
     272            if hasattr(backend, "has_module_perms"): 
     273                if backend.has_module_perms(self, app_label): 
     274                    return True 
     275        return False 
    271276 
    272277    def get_and_delete_messages(self): 
     
    301306class Message(models.Model): 
    302307    """ 
    303     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. 
     308    The message system is a lightweight way to queue messages for given 
     309    users. A message is associated with a User instance (so it is only 
     310    applicable for registered users). There's no concept of expiration or 
     311    timestamps. Messages are created by the Django admin after successful 
     312    actions. For example, "The poll Foo was created successfully." is a 
     313    message. 
    304314    """ 
    305315    user = models.ForeignKey(User) 
  • django/trunk/docs/authentication.txt

    r6299 r6375  
    10631063            except User.DoesNotExist: 
    10641064                return None 
     1065 
     1066Handling authorization in custom backends 
     1067----------------------------------------- 
     1068 
     1069Custom auth backends can provide their own permissions.  
     1070 
     1071The user model will delegate permission lookup functions 
     1072(``get_group_permissions()``, ``get_all_permissions()``, ``has_perm()``, and 
     1073``has_module_perms()``) to any authentication backend that implements these 
     1074functions. 
     1075 
     1076The permissions given to the user will be the superset of all permissions 
     1077returned by all backends. That is, Django grants a permission to a user that any 
     1078one backend grants. 
     1079 
     1080The simple backend above could implement permissions for the magic admin fairly 
     1081simply:: 
     1082         
     1083    class SettingsBackend: 
     1084     
     1085        # ... 
     1086 
     1087        def has_perm(self, user_obj, perm): 
     1088            if user_obj.username == settings.ADMIN_LOGIN: 
     1089                return True 
     1090            else: 
     1091                return False 
     1092                 
     1093This gives full permissions to user granted access in the above example. Notice 
     1094that the backend auth functions all take the user object as an argument, and 
     1095also accept the same arguments given to the associated ``User`` functions. 
     1096 
     1097A full authorization implementation can be found in 
     1098``django/contrib/auth/backends.py`` _, which is the default backend and queries 
     1099the ``auth_permission``-table most of the time. 
     1100 
     1101.. _django/contrib/auth/backends.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py