From ffb98cdba6140b2e1a23fcde3182b7ea645a5561 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 10 Mar 2017 01:27:10 -0400 Subject: [PATCH] Add ACL creation API endpoint. --- mayan/apps/acls/api_views.py | 30 ++++++-- mayan/apps/acls/serializers.py | 120 ++++++++++++++++++++++++++++++ mayan/apps/acls/tests/test_api.py | 17 +++++ 3 files changed, 161 insertions(+), 6 deletions(-) diff --git a/mayan/apps/acls/api_views.py b/mayan/apps/acls/api_views.py index 36aece2dd7..9933b99e29 100644 --- a/mayan/apps/acls/api_views.py +++ b/mayan/apps/acls/api_views.py @@ -11,13 +11,12 @@ from permissions import Permission from .models import AccessControlList from .permissions import permission_acl_edit, permission_acl_view from .serializers import ( - AccessControlListPermissionSerializer, AccessControlListSerializer + AccessControlListPermissionSerializer, AccessControlListSerializer, + WritableAccessControlListSerializer ) -class APIObjectACLListView(generics.ListAPIView): - serializer_class = AccessControlListSerializer - +class APIObjectACLListView(generics.ListCreateAPIView): def get(self, *args, **kwargs): """ Returns a list of all the object's access control lists @@ -35,13 +34,18 @@ class APIObjectACLListView(generics.ListAPIView): content_type.model_class(), pk=self.kwargs['object_pk'] ) + if self.request.method == 'GET': + permission_required = permission_acl_view + else: + permission_required = permission_acl_edit + try: Permission.check_permissions( - self.request.user, permissions=(permission_acl_view,) + self.request.user, permissions=(permission_required,) ) except PermissionDenied: AccessControlList.objects.check_access( - permission_acl_view, self.request.user, content_object + permission_required, self.request.user, content_object ) return content_object @@ -55,11 +59,25 @@ class APIObjectACLListView(generics.ListAPIView): """ return { + 'content_object': self.get_content_object(), 'format': self.format_kwarg, 'request': self.request, 'view': self } + def get_serializer_class(self): + if self.request.method == 'GET': + return AccessControlListSerializer + else: + return WritableAccessControlListSerializer + + def post(self, *args, **kwargs): + """ + Create a new access control list for the selected object. + """ + + return super(APIObjectACLListView, self).post(*args, **kwargs) + class APIObjectACLView(generics.RetrieveAPIView): serializer_class = AccessControlListSerializer diff --git a/mayan/apps/acls/serializers.py b/mayan/apps/acls/serializers.py index 72d9163589..857d99bea6 100644 --- a/mayan/apps/acls/serializers.py +++ b/mayan/apps/acls/serializers.py @@ -1,11 +1,17 @@ from __future__ import absolute_import, unicode_literals +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError as DjangoValidationError +from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers +from rest_framework.exceptions import ValidationError from rest_framework.reverse import reverse from common.serializers import ContentTypeSerializer +from permissions import Permission +from permissions.models import Role from permissions.serializers import PermissionSerializer, RoleSerializer from .models import AccessControlList @@ -72,3 +78,117 @@ class AccessControlListPermissionSerializer(PermissionSerializer): instance.stored_permission.pk ), request=self.context['request'], format=self.context['format'] ) + + +class WritableAccessControlListSerializer(serializers.ModelSerializer): + permissions_pk_list = serializers.CharField( + help_text=_( + 'Comma separated list of permission primary keys to grant to this ' + 'access control list.' + ), required=False + ) + permissions_url = serializers.SerializerMethodField( + help_text=_( + 'API URL pointing to the list of permissions for this access ' + 'control list.' + ), read_only=True + ) + role_pk = serializers.IntegerField( + help_text=_( + 'Primary keys of the role to which this access control list ' + 'binds to.' + ), required=False + ) + url = serializers.SerializerMethodField() + + class Meta: + fields = ( + 'content_type', 'id', 'object_id', 'permissions_pk_list', + 'permissions_url', 'role_pk', 'url' + ) + model = AccessControlList + read_only_fields = ('content_type', 'object_id',) + + def _add_permissions(self, instance): + for pk in self.permissions_pk_list.split(','): + try: + stored_permission = Permission.get(get_dict={'pk': pk}) + instance.permissions.add(stored_permission) + instance.save() + except KeyError: + raise ValidationError(_('No such permission: %s') % pk) + + def create(self, validated_data): + validated_data['content_type'] = ContentType.objects.get_for_model(self.context['content_object']) + validated_data['object_id'] = self.context['content_object'].pk + + self.permissions_pk_list = validated_data.pop( + 'permissions_pk_list', '' + ) + + instance = super( + WritableAccessControlListSerializer, self + ).create(validated_data) + + if self.permissions_pk_list: + self._add_permissions(instance=instance) + + return instance + + def get_permissions_url(self, instance): + return reverse( + 'rest_api:accesscontrollist-permission-list', args=( + instance.content_type.app_label, instance.content_type.model, + instance.object_id, instance.pk + ), request=self.context['request'], format=self.context['format'] + ) + + def get_url(self, instance): + return reverse( + 'rest_api:accesscontrollist-detail', args=( + instance.content_type.app_label, instance.content_type.model, + instance.object_id, instance.pk + ), request=self.context['request'], format=self.context['format'] + ) + + def update(self, instance, validated_data): + self.permissions_pk_list = validated_data.pop( + 'permissions_pk_list', '' + ) + + instance = super(WritableAccessControlListSerializer, self).update( + instance, validated_data + ) + + if self.permissions_pk_list: + instance.permissions.clear() + self._add_permissions(instance=instance) + + return instance + + def validate(self, attrs): + attrs['content_type'] = ContentType.objects.get_for_model(self.context['content_object']) + attrs['object_id'] = self.context['content_object'].pk + + role_pk = attrs.pop('role_pk', None) + if not role_pk: + raise ValidationError( + { + 'role_pk': + _( + 'This field cannot be null.' + ) + } + ) + try: + attrs['role'] = Role.objects.get(pk=role_pk) + except Role.DoesNotExist as exception: + raise ValidationError(force_text(exception)) + + instance = AccessControlList(**attrs) + try: + instance.full_clean() + except DjangoValidationError as exception: + raise ValidationError(exception) + + return attrs diff --git a/mayan/apps/acls/tests/test_api.py b/mayan/apps/acls/tests/test_api.py index 9c7eade47c..55705eb152 100644 --- a/mayan/apps/acls/tests/test_api.py +++ b/mayan/apps/acls/tests/test_api.py @@ -144,3 +144,20 @@ class ACLAPITestCase(APITestCase): self.assertEqual( response.data['pk'], permission_document_view.pk ) + + def test_object_acl_post_no_permissions_added_view(self): + response = self.client.post( + reverse( + 'rest_api:accesscontrollist-list', + args=( + self.document_content_type.app_label, + self.document_content_type.model, + self.document.pk + ) + ), data={'role_pk': self.role.pk} + ) + + self.assertEqual(response.status_code, 201) + self.assertEqual( + self.document.acls.first().content_object, self.document + )