Add ACL creation API endpoint.

This commit is contained in:
Roberto Rosario
2017-03-10 01:27:10 -04:00
parent 31580ee51d
commit ffb98cdba6
3 changed files with 161 additions and 6 deletions

View File

@@ -11,13 +11,12 @@ from permissions import Permission
from .models import AccessControlList from .models import AccessControlList
from .permissions import permission_acl_edit, permission_acl_view from .permissions import permission_acl_edit, permission_acl_view
from .serializers import ( from .serializers import (
AccessControlListPermissionSerializer, AccessControlListSerializer AccessControlListPermissionSerializer, AccessControlListSerializer,
WritableAccessControlListSerializer
) )
class APIObjectACLListView(generics.ListAPIView): class APIObjectACLListView(generics.ListCreateAPIView):
serializer_class = AccessControlListSerializer
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
""" """
Returns a list of all the object's access control lists 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'] 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: try:
Permission.check_permissions( Permission.check_permissions(
self.request.user, permissions=(permission_acl_view,) self.request.user, permissions=(permission_required,)
) )
except PermissionDenied: except PermissionDenied:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permission_acl_view, self.request.user, content_object permission_required, self.request.user, content_object
) )
return content_object return content_object
@@ -55,11 +59,25 @@ class APIObjectACLListView(generics.ListAPIView):
""" """
return { return {
'content_object': self.get_content_object(),
'format': self.format_kwarg, 'format': self.format_kwarg,
'request': self.request, 'request': self.request,
'view': self '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): class APIObjectACLView(generics.RetrieveAPIView):
serializer_class = AccessControlListSerializer serializer_class = AccessControlListSerializer

View File

@@ -1,11 +1,17 @@
from __future__ import absolute_import, unicode_literals 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 django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from common.serializers import ContentTypeSerializer from common.serializers import ContentTypeSerializer
from permissions import Permission
from permissions.models import Role
from permissions.serializers import PermissionSerializer, RoleSerializer from permissions.serializers import PermissionSerializer, RoleSerializer
from .models import AccessControlList from .models import AccessControlList
@@ -72,3 +78,117 @@ class AccessControlListPermissionSerializer(PermissionSerializer):
instance.stored_permission.pk instance.stored_permission.pk
), request=self.context['request'], format=self.context['format'] ), 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

View File

@@ -144,3 +144,20 @@ class ACLAPITestCase(APITestCase):
self.assertEqual( self.assertEqual(
response.data['pk'], permission_document_view.pk 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
)