Adding |for_view filter to bind permissions for a view name.

This commit is contained in:
Gregor Müllegger 2013-05-26 13:32:43 +02:00
parent 9ce77ae572
commit 635bbca553
4 changed files with 136 additions and 30 deletions

View file

@ -236,9 +236,33 @@ class TemplatePermissionChecker(object):
except ValueError:
return ''
new_permissions = self.clone()
new_permissions._view = None
new_permissions._model_admin = admin
return new_permissions
def bind_view(self, view):
'''
Return a clone of the permission wrapper with a new view bind to it.
'''
if isinstance(view, six.string_types):
if view not in self.view_name_mapping:
return ''
view_name = self.view_name_mapping[view]
view = getattr(self._model_admin, view_name)
# we don't support binding view classes yet, only the name of views
# are processed. We have the problem with view classes that we cannot
# tell which model admin it was attached to.
else:
return ''
# if view is a class and not instantiated yet, do it!
if isinstance(view, type):
view = view(
request=self._request,
**self._model_admin.get_default_view_kwargs())
new_permissions = self.clone()
new_permissions._view = view
return new_permissions
def bind_object(self, obj):
'''
Return a clone of the permission wrapper with a new object bind
@ -248,30 +272,17 @@ class TemplatePermissionChecker(object):
new_permissions._obj = obj
return new_permissions
def get_view_by_name(self, view_name):
view_name = self.view_name_mapping[view_name]
model_admin = self._model_admin
view_class = getattr(model_admin, view_name)
view = view_class(
request=self._request,
**model_admin.get_default_view_kwargs())
return view
#########################################
# interface exposed to the template users
def __getitem__(self, key):
match = self._has_named_permission_regex.match(key)
if match:
# the key was a has_*_permission, so get the *has permission
# wrapper*
# the key was a has_*_permission, so bind the correspodning view
view_name = match.groupdict()['name']
if view_name not in self.view_name_mapping:
raise KeyError
view = self.get_view_by_name(view_name)
return self.bind_view(view)
# the name might be a named object admin. So get that one and try to
# check the permission there for further traversal
return self.bind_view(view_name)
# the name might be a named object admin. So get that one and bind it
# to the permission checking
try:
admin_site = self._model_admin.admin
model_admin = admin_site.get_admin_by_name(key)

View file

@ -45,19 +45,6 @@ def formset_visible_fieldlist(formset):
return [f.label for f in formset.forms[0].visible_fields()]
@register.filter
def for_object(permissions, obj):
"""
Only useful in the permission handling. This filter binds a new object to
the permission handler to check for object-level permissions.
"""
# some permission check has failed earlier, so we don't bother trying to
# bind a new object to it.
if permissions == '':
return permissions
return permissions.bind_object(obj)
@register.filter
def for_admin(permissions, admin):
"""
@ -69,3 +56,30 @@ def for_admin(permissions, admin):
if permissions == '':
return permissions
return permissions.bind_admin(admin)
@register.filter
def for_view(permissions, view):
"""
Only useful in the permission handling. This filter binds a new view to
the permission handler to check for view names that are not known during
template compile time.
"""
# some permission check has failed earlier, so we don't bother trying to
# bind a new admin to it.
if permissions == '':
return permissions
return permissions.bind_view(view)
@register.filter
def for_object(permissions, obj):
"""
Only useful in the permission handling. This filter binds a new object to
the permission handler to check for object-level permissions.
"""
# some permission check has failed earlier, so we don't bother trying to
# bind a new object to it.
if permissions == '':
return permissions
return permissions.bind_object(obj)

View file

@ -12,6 +12,7 @@ from .utils import admin2_urlname, model_options
class PermissionMixin(object):
do_not_call_in_templates = True
permission_classes = (permissions.IsStaffPermission,)
def __init__(self, **kwargs):

View file

@ -154,6 +154,86 @@ class TemplatePermissionTest(TestCase):
context)
self.assertEqual(result, '')
def test_view_binding(self):
user_admin = djadmin2.default.get_admin_by_name('auth_user')
post_admin = djadmin2.default.get_admin_by_name('blog_post')
request = self.factory.get(reverse('admin2:auth_user_index'))
request.user = self.user
permissions = TemplatePermissionChecker(request, user_admin)
context = {
'post_admin': post_admin,
'post_add_view': post_admin.create_view,
'permissions': permissions,
}
result = self.render(
'{% load admin2_tags %}'
'{{ permissions|for_view:"add" }}',
context)
self.assertEqual(result, 'False')
# view classes are not supported yet
result = self.render(
'{% load admin2_tags %}'
'{{ permissions|for_view:post_add_view }}',
context)
self.assertEqual(result, '')
result = self.render(
'{% load admin2_tags %}'
# user add permission
'{{ permissions.has_add_permission }}'
'{% with permissions|for_admin:"blog_post"|for_view:"add" as post_add_perm %}'
# post add permission
'{{ post_add_perm }}'
'{% endwith %}',
context)
self.assertEqual(result, 'FalseFalse')
post_add_permission = Permission.objects.get(
content_type__app_label='blog',
content_type__model='post',
codename='add_post')
self.user.user_permissions.add(post_add_permission)
user_change_permission = Permission.objects.get(
content_type__app_label='auth',
content_type__model='user',
codename='change_user')
self.user.user_permissions.add(user_change_permission)
# invalidate the users permission cache
if hasattr(self.user, '_perm_cache'):
del self.user._perm_cache
result = self.render(
'{% load admin2_tags %}'
# user add permission
'{{ permissions.has_add_permission }}'
'{% with permissions|for_admin:"blog_post"|for_view:"add" as post_add_perm %}'
# post add permission
'{{ post_add_perm }}'
'{% endwith %}'
# user change permission
'{{ permissions|for_view:"change" }}',
context)
self.assertEqual(result, 'FalseTrueTrue')
# giving a string (the name of the view) also works
result = self.render(
'{% load admin2_tags %}'
'{% with permissions|for_view:"change" as user_change_perm %}'
'1{{ user_change_perm }}'
'2{{ user_change_perm|for_view:"add" }}'
# this shouldn't return True or False but '' since the
# previously bound change view doesn't belong to the newly
# bound blog_post admin
'3{{ user_change_perm|for_admin:"blog_post" }}'
'4{{ user_change_perm|for_admin:"blog_post"|for_view:"add" }}'
'{% endwith %}',
context)
self.assertEqual(result, '1True2False34True')
def test_object_level_permission(self):
model_admin = ModelAdmin2(Post, djadmin2.default)
request = self.factory.get(reverse('admin2:blog_post_index'))