1. Luke Plant
  2. django

Commits

jkoc...@bcc190cf-cafb-0310-a4f2-bffc1f526a37  committed c102218

Applied patch from http://code.djangoproject.org/wiki/GenericAuthorization things are working now, but ugly.

  • Participants
  • Parent commits 38b9446
  • Branches generic-auth

Comments (0)

Files changed (6)

File django/contrib/admin/templatetags/adminapplist.py

View file
  • Ignore whitespace
 from django import template
 from django.db.models import get_models
+from django.contrib.auth import has_permission
+from django.contrib.auth.models import Permission
 
 register = template.Library()
 
                 model_list = []
                 for m in app_models:
                     if m._meta.admin:
+                        opts = m._meta
                         perms = {
-                            'add': user.has_perm("%s.%s" % (app_label, m._meta.get_add_permission())),
-                            'change': user.has_perm("%s.%s" % (app_label, m._meta.get_change_permission())),
-                            'delete': user.has_perm("%s.%s" % (app_label, m._meta.get_delete_permission())),
+                            'add': has_permission(user, opts.get_add_permission()),
+                            'change': has_permission(user, opts.get_change_permission()),
+                            'delete': has_permission(user, opts.get_delete_permission()),
                         }
 
                         # Check whether user has any perm for this module.

File django/contrib/admin/views/main.py

View file
  • Ignore whitespace
 from django import forms, template
 from django.conf import settings
+from django.contrib.auth import has_permission
 from django.contrib.admin.filterspecs import FilterSpec
 from django.contrib.admin.views.decorators import staff_member_required
 from django.views.decorators.cache import never_cache
     extra_context = {
         'add': add,
         'change': change,
-        'has_delete_permission': context['perms'][app_label][opts.get_delete_permission()],
-        'has_change_permission': context['perms'][app_label][opts.get_change_permission()],
+        'has_delete_permission': context['perms'][app_label][opts.get_delete_permission().codename],
+        'has_change_permission': context['perms'][app_label][opts.get_change_permission().codename],
         'has_file_field': opts.has_field_type(models.FileField),
         'has_absolute_url': hasattr(model, 'get_absolute_url'),
         'auto_populated_fields': auto_populated_fields,
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
     opts = model._meta
 
-    if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
+    if not has_permission(request.user, opts.get_add_permission()):
         raise PermissionDenied
 
-    if post_url is None:
-        if request.user.has_perm(app_label + '.' + opts.get_change_permission()):
-            # redirect to list view
-            post_url = '../'
-        else:
-            # Object list will give 'Permission Denied', so go back to admin home
-            post_url = '../../../'
-
     manipulator = model.AddManipulator()
     if request.POST:
         new_data = request.POST.copy()
             pk_value = new_object._get_pk_val()
             LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, str(new_object), ADDITION)
             msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
+
+            if post_url is None:
+                # We want to call has permission WITHOUT passing it the new
+                # object here. We're concerned with whether the user can edit
+                # ANY instances of this model, not just the one we created.
+                if has_permission(request.user, opts.get_change_permission()):
+                    # redirect to list view
+                    post_url = '../'
+                else:
+                    # Object list will give 'Permission Denied', so go back to admin home
+                    post_url = '../../../'
+
             # Here, we distinguish between different save types by checking for
             # the presence of keys in request.POST.
             if request.POST.has_key("_continue"):
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
     opts = model._meta
 
-    if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
-        raise PermissionDenied
-
     if request.POST and request.POST.has_key("_saveasnew"):
         return add_stage(request, app_label, model_name, form_url='../../add/')
 
     except ObjectDoesNotExist:
         raise Http404
 
+    if not has_permission(request.user, opts.get_change_permission(), manipulator.original_object):
+        raise PermissionDenied
+
     if request.POST:
         new_data = request.POST.copy()
 
                 pass
             else:
                 if related.opts.admin:
-                    p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
-                    if not user.has_perm(p):
+                    if not has_permission(user, related.opts.get_delete_permission(), related):
                         perms_needed.add(related.opts.verbose_name)
                         # We don't care about populating deleted_objects now.
                         continue
             # If there were related objects, and the user doesn't have
             # permission to delete them, add the missing perm to perms_needed.
             if related.opts.admin and has_related_objs:
-                p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
-                if not user.has_perm(p):
+                if not has_permission(user, related.opts.get_delete_permission(), related):
                     perms_needed.add(rel_opts_name)
     for related in opts.get_all_related_many_to_many_objects():
         if related.opts in opts_seen:
         # If there were related objects, and the user doesn't have
         # permission to change them, add the missing perm to perms_needed.
         if related.opts.admin and has_related_objs:
-            p = '%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
-            if not user.has_perm(p):
+            if not has_permission(user, related.opts.get_delete_permission(), related):
                 perms_needed.add(related.opts.verbose_name)
 
 def delete_stage(request, app_label, model_name, object_id):
     if model is None:
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
     opts = model._meta
-    if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
+    obj = get_object_or_404(model, pk=object_id)
+    if not has_permission(request.user, opts.get_delete_permission(), obj):
         raise PermissionDenied
-    obj = get_object_or_404(model, pk=object_id)
 
     # Populate deleted_objects, a data structure of all related objects that
     # will also be deleted.
     model = models.get_model(app_label, model_name)
     if model is None:
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
-    if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
+    # There isn't a specific object to check here, so don't pass one to 
+    # has_permission. There should be a has_permission implementation 
+    # registered that knows when the obj arg is missing.
+    if not has_permission(request.user, model._meta.get_change_permission()):
         raise PermissionDenied
     try:
         cl = ChangeList(request, model)

File django/contrib/auth/__init__.py

View file
  • Ignore whitespace
 LOGIN_URL = '/accounts/login/'
 REDIRECT_FIELD_NAME = 'next'
 
+def default_has_permission(user, permission, obj):
+    p_name = "%s.%s" % (permission.content_type.app_label, permission.codename)
+    return user.has_perm(p_name)
+
+class HasPermission(object):
+    """
+    Function that supports multiple implementations via a type registry. The 
+    implemetation called depends on the argument types.
+    """
+    def __init__(self):
+        self.registry = {}
+
+    def __call__(self, user, permission, obj=None):
+        # TODO: this isn't very robust. Only matches on exact types. Support 
+        # for matching subclasses and caching registry hits would be helpful,
+        # but we'll add that later
+        types = (type(user), type(permission), type(obj))
+        func = self.registry.get(types)
+        if func is not None:
+            return func(user, permission, obj)
+        else:
+            return default_has_permission(user, permission, obj)
+
+    def register(self, func, user_type, permission_type, obj_type=type(None)):
+        types = (user_type, permission_type, obj_type)
+        self.registry[types] = func
+
+has_permission = HasPermission()
+
 def load_backend(path):
     i = path.rfind('.')
     module, attr = path[:i], path[i+1:]

File django/db/models/options.py

View file
  • Ignore whitespace
         return 'ORDER BY ' + orderlist2sql(self.ordering, self, pre)
 
     def get_add_permission(self):
-        return 'add_%s' % self.object_name.lower()
+        from django.contrib.auth.models import Permission
+        codename = 'add_%s' % self.object_name.lower()
+        return Permission.objects.get(
+            content_type__app_label__exact=self.app_label,
+            codename=codename)
 
     def get_change_permission(self):
-        return 'change_%s' % self.object_name.lower()
+        from django.contrib.auth.models import Permission
+        codename = 'change_%s' % self.object_name.lower()
+        return Permission.objects.get(
+            content_type__app_label__exact=self.app_label,
+            codename=codename)
 
     def get_delete_permission(self):
-        return 'delete_%s' % self.object_name.lower()
+        from django.contrib.auth.models import Permission
+        codename = 'delete_%s' % self.object_name.lower()
+        return Permission.objects.get(
+            content_type__app_label__exact=self.app_label,
+            codename=codename)
 
     def get_all_related_objects(self):
         try: # Try the cache first.

File tests/regressiontests/authorization/__init__.py

  • Ignore whitespace
Empty file removed.

File tests/regressiontests/authorization/models.py

  • Ignore whitespace
-from django.db import models
-
-class TestModel(models.Model):
-    name = models.CharField(maxlength=255)
-    
-    class Admin:
-        pass
-
-API_TESTS = """
-# Let's create a default implementation of has_permission. For now, It should 
-# just call user.has_permission(permission) for the given django.contrib.auth.models.User. 
-# Eventually the user.has_permission implementation should be extracted here.
->>> from django.contrib.auth import has_permission
->>> def user_has_permission(user, permission, object=None):
-...     return user.has_perm(permission)
-
-# Then let's register that function to be called when we get an instance of
-# django.contrib.auth.models.User and a string as the permission. We use str
-# as the permission type for convenience. It would be annoying to grab the
-# actual Permission object instead of just using the codename. This feels kind
-# of limiting, but can be revisited later.
->>> from django.contrib.auth.models import User
->>> has_permission.register(User, str, TestModel, user_has_permission)
-
-# Now make sure it works.
->>> admin = User(username='admin', password='test', email='test@example.com', is_superuser=True)
->>> admin.save()
->>> has_permission(admin, 'testmodel.add', TestModel())
-True
-
-# Now let's create an implemetation for AnonymousUsers... it should always
-# return False.
->>> def anon_has_permission(user, permission, object=None):
-...     return False
-
-# Register it like before, but for AnonymousUser rather than User.
->>> from django.contrib.auth.models import AnonymousUser
->>> has_permission.register(AnonymousUser, str, TestModel, anon_has_permission)
-
-# And make sure it works.
->>> anonymous = AnonymousUser()
->>> has_permission(anonymous, 'testmodel.add', TestModel())
-False
-
-# Let's double check that the function we registered for User still works (we're
-# not just replacing the implementation of has_permission)
->>> has_permission(admin, 'testmodel.add', TestModel())
-True
-
-"""