From 070c3b648cb328d881219abd295b070a67de7a87 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Tue, 30 Jun 2015 02:38:22 -0400 Subject: [PATCH] Refactor acls app --- mayan/apps/acls/admin.py | 7 +- mayan/apps/acls/apps.py | 20 +- mayan/apps/acls/classes.py | 11 - mayan/apps/acls/forms.py | 2 - mayan/apps/acls/links.py | 20 +- mayan/apps/acls/managers.py | 98 +---- .../migrations/0002_auto_20150629_1852.py | 47 ++ .../migrations/0003_auto_20150630_0442.py | 37 ++ .../migrations/0004_auto_20150630_0544.py | 18 + mayan/apps/acls/models.py | 41 +- mayan/apps/acls/permissions.py | 6 +- mayan/apps/acls/urls.py | 13 +- mayan/apps/acls/views.py | 415 +++++------------- mayan/apps/acls/widgets.py | 20 - 14 files changed, 302 insertions(+), 453 deletions(-) create mode 100644 mayan/apps/acls/migrations/0002_auto_20150629_1852.py create mode 100644 mayan/apps/acls/migrations/0003_auto_20150630_0442.py create mode 100644 mayan/apps/acls/migrations/0004_auto_20150630_0544.py delete mode 100644 mayan/apps/acls/widgets.py diff --git a/mayan/apps/acls/admin.py b/mayan/apps/acls/admin.py index e33addc57c..9ef4463996 100644 --- a/mayan/apps/acls/admin.py +++ b/mayan/apps/acls/admin.py @@ -2,15 +2,14 @@ from __future__ import unicode_literals from django.contrib import admin -from .models import AccessEntry +from .models import AccessControlList -class AccessEntryAdmin(admin.ModelAdmin): - model = AccessEntry +class AccessControlListAdmin(admin.ModelAdmin): list_display = ('pk', 'role', 'permission', 'content_object') list_display_links = ('pk',) related_lookup_fields = { 'generic': (('content_type', 'object_id'),), } -admin.site.register(AccessEntry, AccessEntryAdmin) +admin.site.register(AccessControlList, AccessControlListAdmin) diff --git a/mayan/apps/acls/apps.py b/mayan/apps/acls/apps.py index 1426cd6882..edcb5061be 100644 --- a/mayan/apps/acls/apps.py +++ b/mayan/apps/acls/apps.py @@ -1,19 +1,12 @@ from __future__ import unicode_literals -from django.db.models.signals import post_migrate from django.utils.translation import ugettext_lazy as _ -from common import ( - MayanAppConfig, menu_multi_item, menu_object, menu_secondary, menu_setup, - menu_sidebar -) +from common import MayanAppConfig, menu_object, menu_sidebar + +from .links import link_acl_new, link_acl_delete, link_acl_permissions +from .models import AccessControlList -from .classes import ( - AccessHolder, AccessObject, AccessObjectClass -) -from .links import ( - link_acl_detail, link_acl_grant, link_acl_holder_new, link_acl_revoke -) class ACLsApp(MayanAppConfig): name = 'acls' @@ -22,6 +15,5 @@ class ACLsApp(MayanAppConfig): def ready(self): super(ACLsApp, self).ready() - menu_multi_item.bind_links(links=[link_acl_grant, link_acl_revoke], sources=['acls:acl_detail']) - menu_object.bind_links(links=[link_acl_detail], sources=[AccessHolder]) - menu_sidebar.bind_links(links=[link_acl_holder_new], sources=[AccessObject]) + menu_object.bind_links(links=[link_acl_permissions, link_acl_delete], sources=[AccessControlList]) + menu_sidebar.bind_links(links=[link_acl_new], sources=['acls:acl_list']) diff --git a/mayan/apps/acls/classes.py b/mayan/apps/acls/classes.py index cc0b3c2026..38bec74a0d 100644 --- a/mayan/apps/acls/classes.py +++ b/mayan/apps/acls/classes.py @@ -1,8 +1,6 @@ from __future__ import unicode_literals import logging -import sys -import types from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist @@ -138,12 +136,3 @@ class AccessObject(EncapsulatedObject): class AccessObjectClass(EncapsulatedObject): source_object_name = 'cls' - - -if sys.version_info < (2, 5): - # Prior to Python 2.5, Exception was an old-style class - def subclass_exception(name, parents, unused): - return types.ClassType(name, parents, {}) -else: - def subclass_exception(name, parents, module): - return type(name, parents, {'__module__': module}) diff --git a/mayan/apps/acls/forms.py b/mayan/apps/acls/forms.py index 5ce65796ca..9a5eb4394a 100644 --- a/mayan/apps/acls/forms.py +++ b/mayan/apps/acls/forms.py @@ -3,9 +3,7 @@ from __future__ import absolute_import, unicode_literals from django import forms from django.contrib.auth.models import User, Group from django.utils.translation import ugettext_lazy as _ -from django.utils.functional import SimpleLazyObject -from common.models import AnonymousUserSingleton from common.utils import get_object_name from permissions.models import Role diff --git a/mayan/apps/acls/links.py b/mayan/apps/acls/links.py index 534c474650..82b819497c 100644 --- a/mayan/apps/acls/links.py +++ b/mayan/apps/acls/links.py @@ -1,14 +1,22 @@ from __future__ import unicode_literals +from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext_lazy as _ from navigation import Link -from .permissions import acls_edit_acl, acls_view_acl +from .permissions import permission_acl_view, permission_acl_edit -link_acl_list = Link(permissions=[acls_view_acl], text=_('ACLs'), view='acls:acl_list') -link_acl_detail = Link(permissions=[acls_view_acl], text=_('Details'), view='acls:acl_detail', args=['access_object.gid', 'object.gid']) -link_acl_grant = Link(permissions=[acls_edit_acl], text=_('Grant'), view='acls:acl_multiple_grant') -link_acl_revoke = Link(permissions=[acls_edit_acl], text=_('Revoke'), view='acls:acl_multiple_revoke') -link_acl_holder_new = Link(permissions=[acls_edit_acl], text=_('New holder'), view='acls:acl_holder_new', args='access_object.gid') +def get_kwargs_factory(variable_name): + def get_kwargs(context): + content_type = ContentType.objects.get_for_model(context[variable_name]) + return {'app_label': '"{}"'.format(content_type.app_label), 'model': '"{}"'.format(content_type.model), 'object_id': '{}.pk'.format(variable_name)} + + return get_kwargs + + +link_acl_delete = Link(permissions=[permission_acl_edit], tags='dangerous', text=_('Delete'), view='acls:acl_delete', args='resolved_object.pk') +link_acl_new = Link(permissions=[permission_acl_edit], text=_('New entry'), view='acls:acl_new', kwargs=get_kwargs_factory('resolved_object')) +link_acl_list = Link(permissions=[permission_acl_view], text=_('ACLs'), view='acls:acl_list', kwargs=get_kwargs_factory('resolved_object')) +link_acl_permissions = Link(permissions=[permission_acl_edit], text=_('Permissions'), view='acls:acl_permissions', args='resolved_object.pk') diff --git a/mayan/apps/acls/managers.py b/mayan/apps/acls/managers.py index 086e53af08..05e0de623f 100644 --- a/mayan/apps/acls/managers.py +++ b/mayan/apps/acls/managers.py @@ -18,6 +18,27 @@ from .classes import AccessHolder, get_source_object logger = logging.getLogger(__name__) +class AccessControlListManager(models.Manager): + """ + Implement a 3 tier permission system, involving a permissions, an actor + and an object + """ + + def check_access(self, permission, actor, obj): + if actor.is_superuser or actor.is_staff: + return True + + user_roles = [] + for group in user.groups.all(): + for role in group.roles.all(): + user_roles.append(role) + + if not self.filter(content_object=obj, permissions=permission, role__in=user_roles): + raise PermissionDenied(ugettext('Insufficient access.')) + + # TODO: add filter_objects_by_access + + class AccessEntryManager(models.Manager): """ Implement a 3 tier permission system, involving a permissions, an actor @@ -252,80 +273,3 @@ class AccessEntryManager(models.Manager): raise PermissionDenied return object_list - - -class DefaultAccessEntryManager(models.Manager): - """ - Implement a 3 tier permission system, involving a permission, an actor - and a class or content type. This model keeps track of the access - control lists that will be added when an instance of the recorded - content type is created. - """ - def get_holders_for(self, cls): - cls = get_source_object(cls) - content_type = ContentType.objects.get_for_model(cls) - holder_list = [] - #for access_entry in self.model.objects.filter(content_type=content_type): - #if access_entry.holder_object: - # Don't add references to non existant content type objects - #TODO: FIX - #entry = ClassAccessHolder.encapsulate(access_entry.holder_object) - - #if entry not in holder_list: - # holder_list.append(entry) - - return holder_list - - def has_access(self, permission, actor, cls): - if isinstance(actor, User): - if actor.is_superuser or actor.is_staff: - return True - - try: - self.model.objects.get( - permission=permission.stored_permission, - holder_type=ContentType.objects.get_for_model(actor), - holder_id=actor.pk, - content_type=ContentType.objects.get_for_model(cls), - ) - except self.model.DoesNotExist: - return False - else: - return True - - def grant(self, permission, actor, cls): - """ - Grant a permission (what), (to) an actor, (on) a specific class - """ - access_entry, created = self.model.objects.get_or_create( - permission=permission, - holder_type=ContentType.objects.get_for_model(actor), - holder_id=actor.pk, - content_type=ContentType.objects.get_for_model(cls), - ) - return created - - def revoke(self, permission, actor, cls): - """ - Revoke a permission (what), (from) an actor, (on) a specific class - """ - try: - access_entry = self.model.objects.get( - permission=permission, - holder_type=ContentType.objects.get_for_model(actor), - holder_id=actor.pk, - content_type=ContentType.objects.get_for_model(cls), - ) - access_entry.delete() - return True - except self.model.DoesNotExist: - return False - - def get_holder_permissions_for(self, cls, actor): - if isinstance(actor, User): - if actor.is_superuser or actor.is_staff: - return Permission.all() - - actor_type = ContentType.objects.get_for_model(actor) - content_type = ContentType.objects.get_for_model(cls) - return [access.permission for access in self.model.objects.filter(content_type=content_type, holder_type=actor_type, holder_id=actor.pk)] diff --git a/mayan/apps/acls/migrations/0002_auto_20150629_1852.py b/mayan/apps/acls/migrations/0002_auto_20150629_1852.py new file mode 100644 index 0000000000..8c9d953125 --- /dev/null +++ b/mayan/apps/acls/migrations/0002_auto_20150629_1852.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('permissions', '0002_auto_20150628_0533'), + ('acls', '0001_initial'), + ] + + operations = [ + migrations.DeleteModel( + name='CreatorSingleton', + ), + migrations.RemoveField( + model_name='defaultaccessentry', + name='content_type', + ), + migrations.RemoveField( + model_name='defaultaccessentry', + name='holder_type', + ), + migrations.RemoveField( + model_name='defaultaccessentry', + name='permission', + ), + migrations.DeleteModel( + name='DefaultAccessEntry', + ), + migrations.RemoveField( + model_name='accessentry', + name='holder_id', + ), + migrations.RemoveField( + model_name='accessentry', + name='holder_type', + ), + migrations.AddField( + model_name='accessentry', + name='role', + field=models.ForeignKey(default=1, verbose_name='Role', to='permissions.Role'), + preserve_default=False, + ), + ] diff --git a/mayan/apps/acls/migrations/0003_auto_20150630_0442.py b/mayan/apps/acls/migrations/0003_auto_20150630_0442.py new file mode 100644 index 0000000000..d7293e3f82 --- /dev/null +++ b/mayan/apps/acls/migrations/0003_auto_20150630_0442.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0001_initial'), + ('permissions', '0002_auto_20150628_0533'), + ('acls', '0002_auto_20150629_1852'), + ] + + operations = [ + migrations.CreateModel( + name='AccessControlList', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('object_id', models.PositiveIntegerField()), + ('content_type', models.ForeignKey(related_name='object_content_type', to='contenttypes.ContentType')), + ('permissions', models.ManyToManyField(related_name='acls', verbose_name='Permissions', to='permissions.StoredPermission')), + ('role', models.ForeignKey(related_name='acls', verbose_name='Role', to='permissions.Role')), + ], + options={ + 'verbose_name': 'Access entry', + 'verbose_name_plural': 'Access entries', + }, + bases=(models.Model,), + ), + migrations.AlterField( + model_name='accessentry', + name='content_type', + field=models.ForeignKey(related_name='object_content_type_1', to='contenttypes.ContentType'), + preserve_default=True, + ), + ] diff --git a/mayan/apps/acls/migrations/0004_auto_20150630_0544.py b/mayan/apps/acls/migrations/0004_auto_20150630_0544.py new file mode 100644 index 0000000000..f17a6841fc --- /dev/null +++ b/mayan/apps/acls/migrations/0004_auto_20150630_0544.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('acls', '0003_auto_20150630_0442'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='accesscontrollist', + unique_together=set([('content_type', 'object_id', 'role')]), + ), + ] diff --git a/mayan/apps/acls/models.py b/mayan/apps/acls/models.py index afb177d175..a6b314ec34 100644 --- a/mayan/apps/acls/models.py +++ b/mayan/apps/acls/models.py @@ -6,19 +6,46 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _, ugettext - -from solo.models import SingletonModel +from django.utils.translation import ugettext_lazy as _ from permissions.models import Role, StoredPermission -from .api import get_classes -from .classes import AccessObjectClass -from .managers import AccessEntryManager, DefaultAccessEntryManager +from .managers import AccessControlListManager, AccessEntryManager logger = logging.getLogger(__name__) +@python_2_unicode_compatible +class AccessControlList(models.Model): + """ + Model that hold the permission, object, actor relationship + """ + + content_type = models.ForeignKey( + ContentType, + related_name='object_content_type' + ) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey( + ct_field='content_type', + fk_field='object_id' + ) + # TODO: limit choices to the permissions valid for the content_object + permissions = models.ManyToManyField(StoredPermission, blank=True, related_name='acls', verbose_name=_('Permissions')) + role = models.ForeignKey(Role, related_name='acls', verbose_name=_('Role')) + + objects = AccessControlListManager() + + class Meta: + unique_together = ('content_type', 'object_id', 'role') + verbose_name = _('Access entry') + verbose_name_plural = _('Access entries') + + def __str__(self): + return '{} <=> {}'.format(self.content_object, self.role) + + +# TODO: remove @python_2_unicode_compatible class AccessEntry(models.Model): """ @@ -28,7 +55,7 @@ class AccessEntry(models.Model): role = models.ForeignKey(Role, verbose_name=_('Role')) content_type = models.ForeignKey( ContentType, - related_name='object_content_type' + related_name='object_content_type_1' ) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey( diff --git a/mayan/apps/acls/permissions.py b/mayan/apps/acls/permissions.py index b0623c4952..f9b23ae728 100644 --- a/mayan/apps/acls/permissions.py +++ b/mayan/apps/acls/permissions.py @@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _ from permissions import PermissionNamespace -acls_namespace = PermissionNamespace('acls', _('Access control lists')) +namespace = PermissionNamespace('acls', _('Access control lists')) -acls_edit_acl = acls_namespace.add_permission(name='acl_edit', label=_('Edit ACLs')) -acls_view_acl = acls_namespace.add_permission(name='acl_view', label=_('View ACLs')) +permission_acl_edit = namespace.add_permission(name='acl_edit', label=_('Edit ACLs')) +permission_acl_view = namespace.add_permission(name='acl_view', label=_('View ACLs')) diff --git a/mayan/apps/acls/urls.py b/mayan/apps/acls/urls.py index 3f584f6a56..19c8d5c08d 100644 --- a/mayan/apps/acls/urls.py +++ b/mayan/apps/acls/urls.py @@ -2,13 +2,12 @@ from __future__ import unicode_literals from django.conf.urls import patterns, url +from .views import ACLCreateView, ACLDeleteView, ACLListView, ACLPermissionsView + urlpatterns = patterns( 'acls.views', - url(r'^new_holder_for/(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/$', 'acl_new_holder_for', name='acl_new_holder_for'), - url(r'^list_for/(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/$', 'acl_list', name='acl_list'), - url(r'^details/(?P[.\w]+)/holder/(?P[.\w]+)/$', 'acl_detail', name='acl_detail'), - url(r'^holder/new/(?P[.\w]+)/$', 'acl_holder_new', name='acl_holder_new'), - - url(r'^multiple/grant/$', 'acl_grant', name='acl_multiple_grant'), - url(r'^multiple/revoke/$', 'acl_revoke', name='acl_multiple_revoke'), + url(r'^(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/new/$', ACLCreateView.as_view(), name='acl_new'), + url(r'^(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/list/$', ACLListView.as_view(), name='acl_list'), + url(r'^(?P\d+)/delete/$', ACLDeleteView.as_view(), name='acl_delete'), + url(r'^(?P\d+)/permissions/$', ACLPermissionsView.as_view(), name='acl_permissions'), ) diff --git a/mayan/apps/acls/views.py b/mayan/apps/acls/views.py index 41ddfd1ffa..5508944f6d 100644 --- a/mayan/apps/acls/views.py +++ b/mayan/apps/acls/views.py @@ -1,29 +1,24 @@ from __future__ import absolute_import, unicode_literals +import itertools import logging -from json import loads -from django.conf import settings -from django.contrib import messages from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ObjectDoesNotExist, PermissionDenied -from django.core.urlresolvers import reverse -from django.http import Http404, HttpResponseRedirect -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext -from django.utils.http import urlencode +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ from common.utils import encapsulate -from common.widgets import two_state_template -from permissions import Permission +from common.views import ( + AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView, + SingleObjectListView +) +from permissions import Permission, PermissionNamespace +from permissions.models import StoredPermission -from .api import get_class_permissions_for -from .classes import AccessHolder, AccessObject, AccessObjectClass -from .forms import ClassHolderSelectionForm, HolderSelectionForm -from .models import AccessEntry -from .permissions import acls_edit_acl, acls_view_acl -from .widgets import object_indentifier +from .models import AccessControlList +from .permissions import permission_acl_edit, permission_acl_view logger = logging.getLogger(__name__) @@ -32,327 +27,143 @@ def _permission_titles(permission_list): return ', '.join([unicode(permission) for permission in permission_list]) -def acl_list_for(request, obj, extra_context=None): - try: - Permission.check_permissions(request.user, [acls_view_acl]) - except PermissionDenied: - AccessEntry.objects.check_access(acls_view_acl, request.user, obj) +class ACLListView(SingleObjectListView): + def dispatch(self, request, *args, **kwargs): + self.content_type = get_object_or_404(ContentType, app_label=self.kwargs['app_label'], model=self.kwargs['model']) - logger.debug('obj: %s', obj) + try: + self.content_object = self.content_type.get_object_for_this_type(pk=self.kwargs['object_id']) + except self.content_type.model_class().DoesNotExist: + raise Http404 - context = { - 'object_list': AccessEntry.objects.get_holders_for(obj), - 'title': _('Access control lists for: %s' % obj), - 'extra_columns': [ - {'name': _('Holder'), 'attribute': encapsulate(lambda x: object_indentifier(x.source_object))}, - {'name': _('Permissions'), 'attribute': encapsulate(lambda x: _permission_titles(AccessEntry.objects.get_holder_permissions_for(obj, x.source_object, db_only=True)))}, - ], - 'hide_object': True, - 'access_object': AccessObject.encapsulate(obj), - 'object': obj, - 'navigation_object_list': ['object', 'access_object'], - } + try: + Permission.check_permissions(request.user, permissions=(permission_acl_view,)) + except PermissionDenied: + AccessControlList.objects.check_access(permission_acl_view, request.user, self.content_object) - if extra_context: - context.update(extra_context) + return super(ACLListView, self).dispatch(request, *args, **kwargs) - return render_to_response('appearance/generic_list.html', context, - context_instance=RequestContext(request)) + def get_queryset(self): + return AccessControlList.objects.filter(content_type=self.content_type, object_id=self.content_object.pk) - -def acl_list(request, app_label, model_name, object_id): - ct = get_object_or_404(ContentType, app_label=app_label, model=model_name) - obj = get_object_or_404(ct.get_object_for_this_type, pk=object_id) - return acl_list_for(request, obj) - - -def acl_detail(request, access_object_gid, holder_object_gid): - try: - holder = AccessHolder.get(gid=holder_object_gid) - access_object = AccessObject.get(gid=access_object_gid) - except ObjectDoesNotExist: - raise Http404 - - # return acl_detail_for(request, holder.source_object, access_object.source_object) - return acl_detail_for(request, holder, access_object) - - -def acl_detail_for(request, actor, obj): - try: - Permission.check_permissions(request.user, [acls_view_acl]) - except PermissionDenied: - AccessEntry.objects.check_accesses([acls_view_acl], actor, obj) - - permission_list = get_class_permissions_for(obj.source_object) - # TODO : get all globally assigned permission, new function get_permissions_for_holder (roles aware) - subtemplates_list = [ - { - 'name': 'appearance/generic_list_subtemplate.html', - 'context': { - 'title': _('Permissions available to: %(actor)s for %(obj)s' % { - 'actor': actor, - 'obj': obj - } - ), - 'object_list': permission_list, + def get_context_data(self, **kwargs): + context = super(ACLListView, self).get_context_data(**kwargs) + context.update( + { + 'hide_object': True, + 'object': self.content_object, + 'title': _('Access control lists for: %s' % self.content_object), 'extra_columns': [ - {'name': _('Namespace'), 'attribute': 'namespace'}, - {'name': _('Label'), 'attribute': 'label'}, { - 'name': _('Has permission'), - 'attribute': encapsulate(lambda permission: two_state_template(AccessEntry.objects.has_access(permission, actor, obj, db_only=True))) + 'name': _('Role'), + 'attribute': 'role' + }, + { + 'name': _('Permissions'), + 'attribute': encapsulate(lambda x: _permission_titles(x.permissions.all())) }, ], - 'hide_object': True, } - }, - ] + ) - context = { - 'object': obj.source_object, - 'subtemplates_list': subtemplates_list, - 'multi_select_item_properties': { - 'permission_pk': lambda x: x.pk, - 'holder_gid': lambda x: actor.gid, - 'object_gid': lambda x: obj.gid, - }, - 'access_object': obj, - 'navigation_object_list': ['object', 'access_object'], - 'read_only': True, - } - - return render_to_response('appearance/generic_form.html', context, - context_instance=RequestContext(request)) + return context -def acl_grant(request): - items_property_list = loads(request.GET.get('items_property_list', [])) +class ACLCreateView(SingleObjectCreateView): + model = AccessControlList + fields = ('role',) - next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) - previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) + def dispatch(self, request, *args, **kwargs): + content_type = get_object_or_404(ContentType, app_label=self.kwargs['app_label'], model=self.kwargs['model']) - items = {} - title_suffix = [] - navigation_object = None - navigation_object_count = 0 - - for item_properties in items_property_list: try: - permission = Permission.get({'pk': item_properties['permission_pk']}) - except Permission.DoesNotExist: + self.content_object = content_type.get_object_for_this_type(pk=self.kwargs['object_id']) + except content_type.model_class().DoesNotExist: raise Http404 try: - requester = AccessHolder.get(gid=item_properties['holder_gid']) - access_object = AccessObject.get(gid=item_properties['object_gid']) - except ObjectDoesNotExist: - raise Http404 - - try: - Permission.check_permissions(request.user, [acls_edit_acl]) + Permission.check_permissions(request.user, permissions=(permission_acl_edit,)) except PermissionDenied: - try: - AccessEntry.objects.check_access(acls_edit_acl, request.user, access_object) - except PermissionDenied: - raise - else: - items.setdefault(requester, {}) - items[requester].setdefault(access_object, []) - items[requester][access_object].append(permission) - navigation_object = access_object - navigation_object_count += 1 - else: - items.setdefault(requester, {}) - items[requester].setdefault(access_object, []) - items[requester][access_object].append(permission) - navigation_object = access_object - navigation_object_count += 1 + AccessControlList.objects.check_access(permission_acl_edit, request.user, self.content_object) - for requester, obj_ps in items.items(): - for obj, ps in obj_ps.items(): - title_suffix.append(_(', ').join(['"%s"' % unicode(p) for p in ps])) - title_suffix.append(_(' for %s') % obj) - title_suffix.append(_(' to %s') % requester) + return super(ACLCreateView, self).dispatch(request, *args, **kwargs) - if len(items_property_list) == 1: - title_prefix = _('Are you sure you wish to grant the permission %(title_suffix)s?') - else: - title_prefix = _('Are you sure you wish to grant the permissions %(title_suffix)s?') + def form_valid(self, form): + instance = form.save(commit=False) + instance.content_object = self.content_object + instance.save() - if request.method == 'POST': - for requester, object_permissions in items.items(): - for obj, permissions in object_permissions.items(): - for permission in permissions: - if AccessEntry.objects.grant(permission, requester.source_object, obj.source_object): - messages.success(request, _('Permission "%(permission)s" granted to %(actor)s for %(object)s.') % { - 'permission': permission, - 'actor': requester, - 'object': obj - }) - else: - messages.warning(request, _('%(actor)s, already had the permission "%(permission)s" granted for %(object)s.') % { - 'actor': requester, - 'permission': permission, - 'object': obj, - }) + return super(ACLCreateView, self).form_valid(form) - return HttpResponseRedirect(next) + def get_context_data(self, **kwargs): + context = super(ACLCreateView, self).get_context_data(**kwargs) + context.update( + { + 'object': self.content_object, + 'title': _('New access control lists for: %s' % self.content_object), + } + ) - context = { - 'previous': previous, - 'next': next, - } - - context['title'] = title_prefix % { - 'title_suffix': ''.join(title_suffix), - } - - logger.debug('navigation_object_count: %d', navigation_object_count) - logger.debug('navigation_object: %s', navigation_object) - if navigation_object_count == 1: - context['object'] = navigation_object.source_object - - return render_to_response('appearance/generic_confirm.html', context, - context_instance=RequestContext(request)) + return context -def acl_revoke(request): - items_property_list = loads(request.GET.get('items_property_list', [])) +class ACLDeleteView(SingleObjectDeleteView): + model = AccessControlList + object_permission = permission_acl_edit - next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) - previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) + def get_context_data(self, **kwargs): + context = super(ACLDeleteView, self).get_context_data(**kwargs) + context.update( + { + 'object': self.get_object().content_object, + } + ) - items = {} - title_suffix = [] - navigation_object = None - navigation_object_count = 0 - - for item_properties in items_property_list: - try: - permission = Permission.get({'pk': item_properties['permission_pk']}) - except Permission.DoesNotExist: - raise Http404 - - try: - requester = AccessHolder.get(gid=item_properties['holder_gid']) - access_object = AccessObject.get(gid=item_properties['object_gid']) - except ObjectDoesNotExist: - raise Http404 - - try: - Permission.check_permissions(request.user, [acls_edit_acl]) - except PermissionDenied: - try: - AccessEntry.objects.check_access(acls_edit_acl, request.user, access_object) - except PermissionDenied: - raise - else: - items.setdefault(requester, {}) - items[requester].setdefault(access_object, []) - items[requester][access_object].append(permission) - navigation_object = access_object - navigation_object_count += 1 - else: - items.setdefault(requester, {}) - items[requester].setdefault(access_object, []) - items[requester][access_object].append(permission) - navigation_object = access_object - navigation_object_count += 1 - - for requester, obj_ps in items.items(): - for obj, ps in obj_ps.items(): - title_suffix.append(_(', ').join(['"%s"' % unicode(p) for p in ps])) - title_suffix.append(_(' for %s') % obj) - title_suffix.append(_(' from %s') % requester) - - if len(items_property_list) == 1: - title_prefix = _('Are you sure you wish to revoke the permission %(title_suffix)s?') - else: - title_prefix = _('Are you sure you wish to revoke the permissions %(title_suffix)s?') - - if request.method == 'POST': - for requester, object_permissions in items.items(): - for obj, permissions in object_permissions.items(): - for permission in permissions: - if AccessEntry.objects.revoke(permission, requester.source_object, obj.source_object): - messages.success(request, _('Permission "%(permission)s" revoked of %(actor)s for %(object)s.') % { - 'permission': permission, - 'actor': requester, - 'object': obj - }) - else: - messages.warning(request, _('%(actor)s, didn\'t had the permission "%(permission)s" for %(object)s.') % { - 'actor': requester, - 'permission': permission, - 'object': obj, - }) - - return HttpResponseRedirect(next) - - context = { - 'previous': previous, - 'next': next, - } - - context['title'] = title_prefix % { - 'title_suffix': ''.join(title_suffix), - } - - logger.debug('navigation_object_count: %d', navigation_object_count) - logger.debug('navigation_object: %s', navigation_object) - if navigation_object_count == 1: - context['object'] = navigation_object.source_object - - return render_to_response('appearance/generic_confirm.html', context, - context_instance=RequestContext(request)) + return context -def acl_new_holder_for(request, obj, extra_context=None, navigation_object=None): - try: - Permission.check_permissions(request.user, [acls_edit_acl]) - except PermissionDenied: - AccessEntry.objects.check_access(acls_edit_acl, request.user, obj) +class ACLPermissionsView(AssignRemoveView): + grouped = True + object_permission = permission_acl_edit + left_list_title = _('Available permissions') + right_list_title = _('Granted permissions') - if request.method == 'POST': - form = HolderSelectionForm(request.POST) - if form.is_valid(): - try: - access_object = AccessObject.encapsulate(obj) - access_holder = AccessHolder.get(form.cleaned_data['holder_gid']) + def add(self, item): + permission = get_object_or_404(StoredPermission, pk=item) + self.get_object().permissions.add(permission) - query_string = {'navigation_object': navigation_object} + def get_object(self): + return get_object_or_404(AccessControlList, pk=self.kwargs['pk']) - return HttpResponseRedirect( - '%s?%s' % ( - reverse('acls:acl_detail', args=[access_object.gid, access_holder.gid]), - urlencode(query_string) - ) - ) - except ObjectDoesNotExist: - raise Http404 - else: - form = HolderSelectionForm() + def left_list(self): + results = [] + for namespace, permissions in itertools.groupby(StoredPermission.objects.exclude(id__in=self.get_object().permissions.values_list('pk', flat=True)), lambda entry: entry.namespace): + permission_options = [(unicode(permission.pk), permission) for permission in permissions] + results.append((PermissionNamespace.get(namespace), permission_options)) - context = { - 'form': form, - 'title': _('Add new holder for: %s') % obj, - 'submit_label': _('Select'), - 'object': obj, - 'access_object': AccessObject.encapsulate(obj), - 'navigation_object_list': ['object', 'access_object'], - } + return results - if extra_context: - context.update(extra_context) + def right_list(self): + results = [] + for namespace, permissions in itertools.groupby(self.get_object().permissions.all(), lambda entry: entry.namespace): + permission_options = [(unicode(permission.pk), permission) for permission in permissions] + results.append((PermissionNamespace.get(namespace), permission_options)) - return render_to_response('appearance/generic_form.html', context, - context_instance=RequestContext(request)) + return results + def get_context_data(self, **kwargs): + context = super(ACLPermissionsView, self).get_context_data(**kwargs) + context.update( + { + 'object': self.get_object().content_object, + 'title': _('Role "%(role)s" permission\'s for "%(object)s"') % { + 'role': self.get_object().role, + 'object': self.get_object().content_object, + } + } + ) + return context -def acl_holder_new(request, access_object_gid): - try: - access_object = AccessObject.get(gid=access_object_gid) - except ObjectDoesNotExist: - raise Http404 - - return acl_new_holder_for(request, access_object.source_object) + def remove(self, item): + permission = get_object_or_404(StoredPermission, pk=item) + self.get_object().permissions.remove(permission) diff --git a/mayan/apps/acls/widgets.py b/mayan/apps/acls/widgets.py deleted file mode 100644 index 913fe2325d..0000000000 --- a/mayan/apps/acls/widgets.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import unicode_literals - -from django.utils.safestring import mark_safe -from django.contrib.contenttypes.models import ContentType -from django.db.models.base import ModelBase - - -def object_indentifier(obj): - content_type = ContentType.objects.get_for_model(obj) - - ct_fullname = '%s.%s' % (content_type.app_label, content_type.name) - if isinstance(obj, ModelBase): - label = getattr(obj._meta, 'verbose_name_plural', unicode(content_type)) - else: - if ct_fullname == 'auth.user': - label = obj.get_full_name() - else: - label = unicode(obj) - - return mark_safe('{}'.format(label))