mirror of
https://github.com/django/django.git
synced 2025-07-06 10:49:17 +00:00
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
This commit is contained in:
parent
dc59c670b8
commit
2f163ffe9a
@ -1,5 +1,7 @@
|
|||||||
from django import template
|
from django import template
|
||||||
from django.db.models import get_models
|
from django.db.models import get_models
|
||||||
|
from django.contrib.auth import has_permission
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
@ -26,10 +28,11 @@ class AdminApplistNode(template.Node):
|
|||||||
model_list = []
|
model_list = []
|
||||||
for m in app_models:
|
for m in app_models:
|
||||||
if m._meta.admin:
|
if m._meta.admin:
|
||||||
|
opts = m._meta
|
||||||
perms = {
|
perms = {
|
||||||
'add': user.has_perm("%s.%s" % (app_label, m._meta.get_add_permission())),
|
'add': has_permission(user, opts.get_add_permission()),
|
||||||
'change': user.has_perm("%s.%s" % (app_label, m._meta.get_change_permission())),
|
'change': has_permission(user, opts.get_change_permission()),
|
||||||
'delete': user.has_perm("%s.%s" % (app_label, m._meta.get_delete_permission())),
|
'delete': has_permission(user, opts.get_delete_permission()),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check whether user has any perm for this module.
|
# Check whether user has any perm for this module.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django import forms, template
|
from django import forms, template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import has_permission
|
||||||
from django.contrib.admin.filterspecs import FilterSpec
|
from django.contrib.admin.filterspecs import FilterSpec
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
from django.views.decorators.cache import never_cache
|
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 = {
|
extra_context = {
|
||||||
'add': add,
|
'add': add,
|
||||||
'change': change,
|
'change': change,
|
||||||
'has_delete_permission': context['perms'][app_label][opts.get_delete_permission()],
|
'has_delete_permission': context['perms'][app_label][opts.get_delete_permission().codename],
|
||||||
'has_change_permission': context['perms'][app_label][opts.get_change_permission()],
|
'has_change_permission': context['perms'][app_label][opts.get_change_permission().codename],
|
||||||
'has_file_field': opts.has_field_type(models.FileField),
|
'has_file_field': opts.has_field_type(models.FileField),
|
||||||
'has_absolute_url': hasattr(model, 'get_absolute_url'),
|
'has_absolute_url': hasattr(model, 'get_absolute_url'),
|
||||||
'auto_populated_fields': auto_populated_fields,
|
'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)
|
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
|
||||||
opts = model._meta
|
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
|
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()
|
manipulator = model.AddManipulator()
|
||||||
if request.POST:
|
if request.POST:
|
||||||
new_data = request.POST.copy()
|
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()
|
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)
|
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}
|
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
|
# Here, we distinguish between different save types by checking for
|
||||||
# the presence of keys in request.POST.
|
# the presence of keys in request.POST.
|
||||||
if request.POST.has_key("_continue"):
|
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)
|
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
|
||||||
opts = model._meta
|
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"):
|
if request.POST and request.POST.has_key("_saveasnew"):
|
||||||
return add_stage(request, app_label, model_name, form_url='../../add/')
|
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:
|
except ObjectDoesNotExist:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
if not has_permission(request.user, opts.get_change_permission(), manipulator.original_object):
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
if request.POST:
|
if request.POST:
|
||||||
new_data = request.POST.copy()
|
new_data = request.POST.copy()
|
||||||
|
|
||||||
@ -419,8 +424,7 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if related.opts.admin:
|
if related.opts.admin:
|
||||||
p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
|
if not has_permission(user, related.opts.get_delete_permission(), related):
|
||||||
if not user.has_perm(p):
|
|
||||||
perms_needed.add(related.opts.verbose_name)
|
perms_needed.add(related.opts.verbose_name)
|
||||||
# We don't care about populating deleted_objects now.
|
# We don't care about populating deleted_objects now.
|
||||||
continue
|
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
|
# If there were related objects, and the user doesn't have
|
||||||
# permission to delete them, add the missing perm to perms_needed.
|
# permission to delete them, add the missing perm to perms_needed.
|
||||||
if related.opts.admin and has_related_objs:
|
if related.opts.admin and has_related_objs:
|
||||||
p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
|
if not has_permission(user, related.opts.get_delete_permission(), related):
|
||||||
if not user.has_perm(p):
|
|
||||||
perms_needed.add(rel_opts_name)
|
perms_needed.add(rel_opts_name)
|
||||||
for related in opts.get_all_related_many_to_many_objects():
|
for related in opts.get_all_related_many_to_many_objects():
|
||||||
if related.opts in opts_seen:
|
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
|
# If there were related objects, and the user doesn't have
|
||||||
# permission to change them, add the missing perm to perms_needed.
|
# permission to change them, add the missing perm to perms_needed.
|
||||||
if related.opts.admin and has_related_objs:
|
if related.opts.admin and has_related_objs:
|
||||||
p = '%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
|
if not has_permission(user, related.opts.get_delete_permission(), related):
|
||||||
if not user.has_perm(p):
|
|
||||||
perms_needed.add(related.opts.verbose_name)
|
perms_needed.add(related.opts.verbose_name)
|
||||||
|
|
||||||
def delete_stage(request, app_label, model_name, object_id):
|
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:
|
if model is None:
|
||||||
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
|
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
|
||||||
opts = model._meta
|
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)
|
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
|
# Populate deleted_objects, a data structure of all related objects that
|
||||||
# will also be deleted.
|
# will also be deleted.
|
||||||
@ -730,7 +732,10 @@ def change_list(request, app_label, model_name):
|
|||||||
model = models.get_model(app_label, model_name)
|
model = models.get_model(app_label, model_name)
|
||||||
if model is None:
|
if model is None:
|
||||||
raise Http404, "App %r, model %r, not found" % (app_label, model_name)
|
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
|
raise PermissionDenied
|
||||||
try:
|
try:
|
||||||
cl = ChangeList(request, model)
|
cl = ChangeList(request, model)
|
||||||
|
@ -5,6 +5,35 @@ BACKEND_SESSION_KEY = '_auth_user_backend'
|
|||||||
LOGIN_URL = '/accounts/login/'
|
LOGIN_URL = '/accounts/login/'
|
||||||
REDIRECT_FIELD_NAME = 'next'
|
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):
|
def load_backend(path):
|
||||||
i = path.rfind('.')
|
i = path.rfind('.')
|
||||||
module, attr = path[:i], path[i+1:]
|
module, attr = path[:i], path[i+1:]
|
||||||
|
@ -106,13 +106,25 @@ class Options(object):
|
|||||||
return 'ORDER BY ' + orderlist2sql(self.ordering, self, pre)
|
return 'ORDER BY ' + orderlist2sql(self.ordering, self, pre)
|
||||||
|
|
||||||
def get_add_permission(self):
|
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):
|
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):
|
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):
|
def get_all_related_objects(self):
|
||||||
try: # Try the cache first.
|
try: # Try the cache first.
|
||||||
|
@ -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
|
|
||||||
|
|
||||||
"""
|
|
Loading…
x
Reference in New Issue
Block a user