diff --git a/apps/common/templates/404.html b/apps/common/templates/404.html index a3e4254cab..3b629ecd0d 100755 --- a/apps/common/templates/404.html +++ b/apps/common/templates/404.html @@ -1,9 +1,12 @@ {% extends "base.html" %} - +{% load i18n %} {% block title %}Page not found{% endblock %} {% block content %} -

Page not found

- -

Sorry, but the requested page could not be found.

+
+

{% blocktrans %}Page not found or insufficient permissions.{% endblocktrans %}

+
+

{% blocktrans %}Sorry, but the requested page could not be found.{% endblocktrans %}

+
+
{% endblock %} diff --git a/apps/common/templatetags/navigation.py b/apps/common/templatetags/navigation.py index 982dbbb60d..b8f2689615 100755 --- a/apps/common/templatetags/navigation.py +++ b/apps/common/templatetags/navigation.py @@ -227,6 +227,7 @@ def get_object_navigation_links(parser, token): def object_navigation_template(context): return { + 'request':context['request'], 'horizontal':True, 'object_navigation_links':_get_object_navigation_links(context) } diff --git a/apps/permissions/__init__.py b/apps/permissions/__init__.py index 567cb34554..1a899077c0 100644 --- a/apps/permissions/__init__.py +++ b/apps/permissions/__init__.py @@ -9,15 +9,33 @@ from common.api import register_links, register_menu, \ from permissions.conf.settings import DEFAULT_ROLES from models import Role +from api import register_permissions -role_list = {'text':_(u'roles'), 'view':'role_list', 'famfam':'medal_gold_1'}#, 'permissions':{'namespace':'documents', 'permissions':[PERMISSION_DOCUMENT_VIEW]}} -role_view = {'text':_(u'role details'), 'view':'role_view', 'args':'object.id', 'famfam':'medal_gold_1'}#, 'permissions':{'namespace':'documents', 'permissions':[PERMISSION_DOCUMENT_VIEW]}} -role_create = {'text':_(u'create new role'), 'view':'role_create', 'famfam':'medal_gold_add'}#, 'permissions':{'namespace':'documents', 'permissions':[PERMISSION_DOCUMENT_VIEW]}} -role_delete = {'text':_(u'delete'), 'view':'role_delete', 'args':'object.id', 'famfam':'medal_gold_delete'}#, 'permissions':{'namespace':'documents', 'permissions':[PERMISSION_DOCUMENT_VIEW]}} +PERMISSION_ROLE_VIEW = 'role_view' +PERMISSION_ROLE_EDIT = 'role_edit' +PERMISSION_ROLE_CREATE = 'role_create' +PERMISSION_ROLE_DELETE = 'role_delete' +PERMISSION_PERMISSION_GRANT = 'permission_grant' +PERMISSION_PERMISSION_REVOKE = 'permission_revoke' -register_links(Role, [role_view], menu_name='sidebar') -register_links(Role, [role_delete]) -register_links(['role_list', 'role_view', 'role_create', 'role_delete'], [role_create], menu_name='sidebar') +register_permissions('permissions', [ + {'name':PERMISSION_ROLE_VIEW, 'label':_(u'View roles')}, + {'name':PERMISSION_ROLE_EDIT, 'label':_(u'Edit roles')}, + {'name':PERMISSION_ROLE_CREATE, 'label':_(u'Create roles')}, + {'name':PERMISSION_ROLE_DELETE, 'label':_(u'Delete roles')}, + {'name':PERMISSION_PERMISSION_GRANT, 'label':_(u'Grant permissions')}, + {'name':PERMISSION_PERMISSION_REVOKE, 'label':_(u'Revoke permissions')}, +]) + + +role_list = {'text':_(u'roles'), 'view':'role_list', 'famfam':'medal_gold_1', 'permissions':{'namespace':'permissions', 'permissions':[PERMISSION_ROLE_VIEW]}} +role_create = {'text':_(u'create new role'), 'view':'role_create', 'famfam':'medal_gold_add', 'permissions':{'namespace':'documents', 'permissions':[PERMISSION_ROLE_CREATE]}} +role_edit = {'text':_(u'edit'), 'view':'role_edit', 'args':'object.id', 'famfam':'medal_gold_1', 'permissions':{'namespace':'permissions', 'permissions':[PERMISSION_ROLE_EDIT]}} +role_permissions = {'text':_(u'role permissions'), 'view':'role_permissions', 'args':'object.id', 'famfam':'key_go', 'permissions':{'namespace':'permissions', 'permissions':[PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE]}} +role_delete = {'text':_(u'delete'), 'view':'role_delete', 'args':'object.id', 'famfam':'medal_gold_delete', 'permissions':{'namespace':'permissions', 'permissions':[PERMISSION_ROLE_DELETE]}} + +register_links(Role, [role_permissions, role_edit, role_delete]) +register_links(['role_list', 'role_view', 'role_create', 'role_edit', 'role_permissions', 'role_delete'], [role_create], menu_name='sidebar') def user_post_save(sender, instance, **kwargs): diff --git a/apps/permissions/forms.py b/apps/permissions/forms.py index 08c062e837..40bec56fe8 100644 --- a/apps/permissions/forms.py +++ b/apps/permissions/forms.py @@ -1,13 +1,6 @@ from django import forms from django.utils.translation import ugettext_lazy as _ -#from django.http import HttpResponseRedirect -#from django.utils.http import urlencode -#from django.core.urlresolvers import reverse -#from django.utils.safestring import mark_safe -#from django.forms.formsets import formset_factory -#from common.wizard import BoundFormWizard -#from common.utils import urlquote from common.forms import DetailForm from models import Role diff --git a/apps/permissions/models.py b/apps/permissions/models.py index bddcdb3bc5..06c5549c04 100644 --- a/apps/permissions/models.py +++ b/apps/permissions/models.py @@ -4,17 +4,24 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic +class PermissionManager(models.Manager): + def get_for_holder(self, holder): + ct = ContentType.objects.get_for_model(holder) + return [Permission.objects.get(id=id) for id in PermissionHolder.objects.filter(holder_type=ct,holder_id=holder.id).values_list('permission_id', flat=True)] + class Permission(models.Model): namespace = models.CharField(max_length=64, verbose_name=_(u'namespace')) name = models.CharField(max_length=64, verbose_name=_(u'name')) label = models.CharField(max_length=64, verbose_name=_(u'label')) + objects = PermissionManager() + class Meta: + ordering = ('namespace', 'label') unique_together = ('namespace', 'name') verbose_name = _(u'permission') verbose_name_plural = _(u'permissions') - def __unicode__(self): return self.label @@ -38,7 +45,7 @@ class PermissionHolder(models.Model): class Role(models.Model): name = models.CharField(max_length=64, unique=True) label = models.CharField(max_length=64, unique=True, verbose_name=_(u'label')) - + class Meta: ordering = ('label',) verbose_name = _(u'role') @@ -52,17 +59,17 @@ class Role(models.Model): def __unicode__(self): return self.label - + @models.permalink def get_absolute_url(self): - return ('role_view', [self.id]) + return ('role_list',) class RoleMember(models.Model): role = models.ForeignKey(Role, verbose_name=_(u'role')) member_type = models.ForeignKey(ContentType, related_name='role_member', - limit_choices_to = {'model__in': ('user', 'group', 'role')}) + limit_choices_to = {'model__in': ('user', 'group')}) member_id = models.PositiveIntegerField() member_object = generic.GenericForeignKey(ct_field='member_type', fk_field='member_id') diff --git a/apps/permissions/urls.py b/apps/permissions/urls.py index 464da525e7..76bb2af84e 100644 --- a/apps/permissions/urls.py +++ b/apps/permissions/urls.py @@ -1,23 +1,12 @@ from django.conf.urls.defaults import * -#from django.utils.translation import ugettext_lazy as _ -#from django.views.generic.create_update import create_object, update_object urlpatterns = patterns('permissions.views', url(r'^role/list/$', 'role_list', (), 'role_list'), - url(r'^role/(?P\d+)/$', 'role_view', (), 'role_view'), url(r'^role/create/$', 'role_create', (), 'role_create'), + url(r'^role/(?P\d+)/permissions/$', 'role_permissions', (), 'role_permissions'), + url(r'^role/(?P\d+)/edit/$', 'role_edit', (), 'role_edit'), url(r'^role/(?P\d+)/delete/$', 'role_delete', (), 'role_delete'), -# url(r'^document/create/from/local/single/$', 'document_create', {'multiple':False}, 'document_create'), -# url(r'^document/create/from/local/multiple/$', 'document_create', {'multiple':True}, 'document_create_multiple'), -# url(r'^document/type/(?P\d+)/upload/single/$', 'upload_document_with_type', {'multiple':False}, 'upload_document_with_type'), -# url(r'^document/type/(?P\d+)/upload/multiple/$', 'upload_document_with_type', {'multiple':True}, 'upload_multiple_documents_with_type'), -# url(r'^document/(?P\d+)/edit/$', 'document_edit', (), 'document_edit'), -# url(r'^document/(?P\d+)/edit/metadata/$', 'document_edit_metadata', (), 'document_edit_metadata'), -# url(r'^document/(?P\d+)/display/preview/$', 'get_document_image', {'size':PREVIEW_SIZE}, 'document_preview'), -# url(r'^document/(?P\d+)/display/preview/multipage/$', 'get_document_image', {'size':MULTIPAGE_PREVIEW_SIZE}, 'document_preview_multipage'), -# url(r'^document/(?P\d+)/display/thumbnail/$', 'get_document_image', {'size':THUMBNAIL_SIZE}, 'document_thumbnail'), -# url(r'^document/(?P\d+)/display/$', 'get_document_image', {'size':DISPLAY_SIZE,'quality':QUALITY_HIGH}, 'document_display'), -# url(r'^document/(?P\d+)/download/$', 'document_download', (), 'document_download'), -# url(r'^document/(?P\d+)/create/siblings/$', 'document_create_sibling', {'multiple':False}, 'document_create_sibling'), + url(r'^permission/(?P\d+)/for/(?P[\w\-]+)/(?P[\w\-]+)/(?P\d+)/grant/$', 'permission_grant_revoke', {'action':'grant'}, 'permission_grant'), + url(r'^permission/(?P\d+)/for/(?P[\w\-]+)/(?P[\w\-]+)/(?P\d+)/revoke/$', 'permission_grant_revoke', {'action':'revoke'}, 'permission_revoke'), ) diff --git a/apps/permissions/views.py b/apps/permissions/views.py index e028b88b6e..171547bea9 100644 --- a/apps/permissions/views.py +++ b/apps/permissions/views.py @@ -7,16 +7,23 @@ from django.views.generic.list_detail import object_detail, object_list from django.core.urlresolvers import reverse from django.views.generic.create_update import create_object, delete_object, update_object from django.conf import settings +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist + +from models import Role, Permission, PermissionHolder +from forms import RoleForm, RoleForm_view +from permissions import PERMISSION_ROLE_VIEW, PERMISSION_ROLE_EDIT, \ + PERMISSION_ROLE_CREATE, PERMISSION_ROLE_DELETE, PERMISSION_PERMISSION_GRANT, \ + PERMISSION_PERMISSION_REVOKE +from api import check_permissions, Unauthorized -from models import Role -from forms import RoleForm_view def role_list(request): - #permissions = [PERMISSION_ROLE_VIEW] - #try: - # check_permissions(request.user, 'permissions', permissions) - #except Unauthorized, e: - # raise Http404(e) + permissions = [PERMISSION_ROLE_VIEW] + try: + check_permissions(request.user, 'permissions', permissions) + except Unauthorized, e: + raise Http404(e) return object_list( request, @@ -24,85 +31,155 @@ def role_list(request): template_name='generic_list.html', extra_context={ 'title':_(u'roles'), + 'hide_link':True, }, ) -def role_view(request, role_id): - #permissions = [PERMISSION_ROLE_VIEW] - #try: - # check_permissions(request.user, 'permissions', permissions) - #except Unauthorized, e: - # raise Http404(e) +def _role_permission_link(requester, permission, permission_list): + ct = ContentType.objects.get_for_model(requester) + + template = '%(text)s' + + if permission in permission_list: + return template % { + 'url':reverse('permission_revoke', + args=[permission.id, ct.app_label, ct.model, requester.id]), + 'icon':'delete', 'text':_(u'Revoke')} + else: + return template % { + 'url':reverse('permission_grant', + args=[permission.id, ct.app_label, ct.model, requester.id]), + 'icon':'add', 'text':_(u'Grant')} + + +def role_permissions(request, role_id): + permissions = [PERMISSION_PERMISSION_GRANT, PERMISSION_PERMISSION_REVOKE] + try: + check_permissions(request.user, 'permissions', permissions) + except Unauthorized, e: + raise Http404(e) role = get_object_or_404(Role, pk=role_id) - form = RoleForm_view(instance=role)#, extra_fields=[ - # {'label':_(u'Filename'), 'field':'file_filename'}, - # {'label':_(u'File extension'), 'field':'file_extension'}, - # {'label':_(u'File mimetype'), 'field':'file_mimetype'}, - # {'label':_(u'File mime encoding'), 'field':'file_mime_encoding'}, - # {'label':_(u'File size'), 'field':lambda x: pretty_size(x.file.storage.size(x.file.path)) if x.exists() else '-'}, - # {'label':_(u'Exists in storage'), 'field':'exists'}, - # {'label':_(u'Date added'), 'field':lambda x: x.date_added.date()}, - # {'label':_(u'Time added'), 'field':lambda x: unicode(x.date_added.time()).split('.')[0]}, - # {'label':_(u'Checksum'), 'field':'checksum'}, - # {'label':_(u'UUID'), 'field':'uuid'}, - # {'label':_(u'Pages'), 'field':lambda x: x.documentpage_set.count()}, - #]) - - - #subtemplates_dict = [ - # { - # 'name':'generic_list_subtemplate.html', - # 'title':_(u'metadata'), - # 'object_list':document.documentmetadata_set.all(), - # 'extra_columns':[{'name':_(u'value'), 'attribute':'value'}], - # 'hide_link':True, - # }, - # ] - - #if FILESYSTEM_FILESERVING_ENABLE: - # subtemplates_dict.append({ - # 'name':'generic_list_subtemplate.html', - # 'title':_(u'index links'), - # 'object_list':document.documentmetadataindex_set.all(), - # 'hide_link':True}) + form = RoleForm_view(instance=role) + role_permissions = Permission.objects.get_for_holder(role) + subtemplates_dict = [ + { + 'name':'generic_list_subtemplate.html', + 'title':_(u'permissions'), + 'object_list':Permission.objects.all(), + 'extra_columns':[ + {'name':_(u'namespace'), 'attribute':'namespace'}, + {'name':_(u'name'), 'attribute':'label'}, + {'name':_(u'state'), 'attribute':lambda x: _role_permission_link(role, x, role_permissions)} + ], + 'hide_link':True, + 'hide_object':True, + }, + ] return render_to_response('generic_detail.html', { - #'form_list':[{'form':form, 'object':role}], 'form':form, 'object':role, - #'object':role, - #'subtemplates_dict':subtemplates_dict, - #'sidebar_subtemplates_dict':sidebar_groups, + 'object_name':_(u'role'), + 'subtemplates_dict':subtemplates_dict, }, context_instance=RequestContext(request)) -def role_create(request): - #permissions = [PERMISSION_ROLE_CREATE] - #try: - # check_permissions(request.user, 'permissions', permissions) - #except Unauthorized, e: - # raise Http404(e) +def role_edit(request, role_id): + permissions = [PERMISSION_ROLE_EDIT] + try: + check_permissions(request.user, 'permissions', permissions) + except Unauthorized, e: + raise Http404(e) + + return update_object(request, template_name='generic_form.html', + form_class=RoleForm, object_id=role_id, extra_context={ + 'object_name':_(u'role')}) + + +def role_create(request): + permissions = [PERMISSION_ROLE_CREATE] + try: + check_permissions(request.user, 'permissions', permissions) + except Unauthorized, e: + raise Http404(e) + + return create_object(request, model=Role, + template_name='generic_form.html', + post_save_redirect=reverse('role_list')) - #TODO: post_create redirect - return create_object(request, model=Role, template_name='generic_form.html') def role_delete(request, role_id): -# permissions = [PERMISSION_DOCUMENT_DELETE] -# try: -# check_permissions(request.user, 'documents', permissions) -# except Unauthorized, e: -# raise Http404(e) -# - #role = get_object_or_404(Role, pk=role_id) + permissions = [PERMISSION_ROLE_DELETE] + try: + check_permissions(request.user, 'documents', permissions) + except Unauthorized, e: + raise Http404(e) + + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None))) + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', None))) return delete_object(request, model=Role, object_id=role_id, template_name='generic_confirm.html', post_delete_redirect=reverse('role_list'), extra_context={ 'delete_view':True, - #'object':document, + 'next':next, + 'previous':previous, 'object_name':_(u'role'), }) + +def permission_grant_revoke(request, permission_id, app_label, module_name, pk, action): + ct = get_object_or_404(ContentType, app_label=app_label, model=module_name) + requester_model = ct.model_class() + requester = get_object_or_404(requester_model, pk=pk) + permission = get_object_or_404(Permission, pk=permission_id) + + if action == 'grant': + permissions = [PERMISSION_PERMISSION_GRANT] + try: + check_permissions(request.user, 'permissions', permissions) + except Unauthorized, e: + raise Http404(e) + title = _('Are you sure you wish to grant the permission "%s" to %s: %s') % (permission, ct.name, requester) + + elif action == 'revoke': + permissions = [PERMISSION_PERMISSION_REVOKE] + try: + check_permissions(request.user, 'permissions', permissions) + except Unauthorized, e: + raise Http404(e) + title = _('Are you sure you wish to revoke the permission "%s" from %s: %s') % (permission, ct.name, requester) + else: + return HttpResponseRedirect('/') + + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None))) + previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', None))) + + + if request.method == 'POST': + if action == 'grant': + permission_holder, created = PermissionHolder.objects.get_or_create(permission=permission, holder_type=ct, holder_id=requester.id) + if created: + messages.success(request, _(u'Permission "%s" granted to %s: %s.') % (permission, ct.name, requester)) + else: + messages.warning(request, _(u'%s: %s, already had the permission "%s" granted.') % (ct.name, requester, permission)) + elif action == 'revoke': + try: + permission_holder = PermissionHolder.objects.get(permission=permission, holder_type=ct, holder_id=requester.id) + permission_holder.delete() + messages.success(request, _(u'Permission "%s" revoked from %s: %s.') % (permission, ct.name, requester)) + except ObjectDoesNotExist: + messages.warning(request, _(u'%s: %s doesn\'t have the permission "%s".') % (ct.name, requester, permission)) + return HttpResponseRedirect(next) + + return render_to_response('generic_confirm.html', { + 'object':requester, + 'next':next, + 'previous':previous, + 'title':title, + }, context_instance=RequestContext(request)) + + diff --git a/docs/TODO b/docs/TODO index c113d47250..a72c96f986 100755 --- a/docs/TODO +++ b/docs/TODO @@ -70,3 +70,4 @@ * Include annotations in transformed documents downloads * Document view temp transformations * Implement permissions decorators +* block Setup menu item to non staff and non superuser users