summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Kocherhans <joseph@jkocherhans.com>2006-11-06 21:25:29 +0000
committerJoseph Kocherhans <joseph@jkocherhans.com>2006-11-06 21:25:29 +0000
commit2f163ffe9aca6a9527163435f4514d79908aa456 (patch)
treeadb3e470a246183afebefe3d57f114fdad0949ab
parentdc59c670b8cbe055ff3565f8d5a2f600c5ab1ba8 (diff)
Applied patch from http://code.djangoproject.org/wiki/GenericAuthorization things are working now, but ugly.
git-svn-id: http://code.djangoproject.com/svn/django/branches/generic-auth@4025 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/contrib/admin/templatetags/adminapplist.py9
-rw-r--r--django/contrib/admin/views/main.py51
-rw-r--r--django/contrib/auth/__init__.py29
-rw-r--r--django/db/models/options.py18
-rw-r--r--tests/regressiontests/authorization/__init__.py0
-rw-r--r--tests/regressiontests/authorization/models.py50
6 files changed, 78 insertions, 79 deletions
diff --git a/django/contrib/admin/templatetags/adminapplist.py b/django/contrib/admin/templatetags/adminapplist.py
index 10e09ca0b6..ed9038f83a 100644
--- a/django/contrib/admin/templatetags/adminapplist.py
+++ b/django/contrib/admin/templatetags/adminapplist.py
@@ -1,5 +1,7 @@
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()
@@ -26,10 +28,11 @@ class AdminApplistNode(template.Node):
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.
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index 705dfad6c8..ec2a20c82b 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -1,5 +1,6 @@
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
@@ -199,8 +200,8 @@ def render_change_form(model, manipulator, context, add=False, change=False, for
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,
@@ -229,17 +230,9 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
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()
@@ -255,6 +248,18 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po
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"):
@@ -303,9 +308,6 @@ def change_stage(request, app_label, model_name, object_id):
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/')
@@ -314,6 +316,9 @@ def change_stage(request, app_label, model_name, object_id):
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()
@@ -419,8 +424,7 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
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
@@ -450,8 +454,7 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
# 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:
@@ -479,8 +482,7 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
# 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):
@@ -490,9 +492,9 @@ 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()):
- raise PermissionDenied
obj = get_object_or_404(model, pk=object_id)
+ if not has_permission(request.user, opts.get_delete_permission(), obj):
+ raise PermissionDenied
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
@@ -730,7 +732,10 @@ def change_list(request, app_label, model_name):
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)
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index a0097a01ed..ca5f1f12bc 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -5,6 +5,35 @@ BACKEND_SESSION_KEY = '_auth_user_backend'
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:]
diff --git a/django/db/models/options.py b/django/db/models/options.py
index ff0d112d16..4bf5b42e55 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -106,13 +106,25 @@ class Options(object):
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.
diff --git a/tests/regressiontests/authorization/__init__.py b/tests/regressiontests/authorization/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/tests/regressiontests/authorization/__init__.py
+++ /dev/null
diff --git a/tests/regressiontests/authorization/models.py b/tests/regressiontests/authorization/models.py
deleted file mode 100644
index 8a54b45f99..0000000000
--- a/tests/regressiontests/authorization/models.py
+++ /dev/null
@@ -1,50 +0,0 @@
-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
-
-"""