Refactor ACL app API
Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
@@ -3,201 +3,125 @@ from __future__ import absolute_import, unicode_literals
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics
|
||||
from rest_framework import generics, status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from mayan.apps.common.mixins import ContentTypeViewMixin, ExternalObjectMixin
|
||||
from mayan.apps.rest_api.viewsets import (
|
||||
MayanAPIGenericViewSet, MayanAPIModelViewSet, MayanAPIReadOnlyModelViewSet
|
||||
)
|
||||
from mayan.apps.permissions.serializers import (
|
||||
PermissionSerializer, RolePermissionAddRemoveSerializer
|
||||
)
|
||||
|
||||
from .models import AccessControlList
|
||||
from .permissions import permission_acl_edit, permission_acl_view
|
||||
from .serializers import (
|
||||
AccessControlListPermissionSerializer, AccessControlListSerializer,
|
||||
WritableAccessControlListPermissionSerializer,
|
||||
WritableAccessControlListSerializer
|
||||
)
|
||||
from .serializers import AccessControlListSerializer
|
||||
|
||||
|
||||
class APIObjectACLListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
get: Returns a list of all the object's access control lists
|
||||
post: Create a new access control list for the selected object.
|
||||
"""
|
||||
def get_content_object(self):
|
||||
content_type = get_object_or_404(
|
||||
klass=ContentType, app_label=self.kwargs['app_label'],
|
||||
model=self.kwargs['model']
|
||||
)
|
||||
|
||||
content_object = get_object_or_404(
|
||||
klass=content_type.model_class(), pk=self.kwargs['object_id']
|
||||
)
|
||||
|
||||
if self.request.method == 'GET':
|
||||
permission_required = permission_acl_view
|
||||
else:
|
||||
permission_required = permission_acl_edit
|
||||
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_required, user=self.request.user,
|
||||
obj=content_object
|
||||
)
|
||||
|
||||
return content_object
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_content_object().acls.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""
|
||||
Extra context provided to the serializer class.
|
||||
"""
|
||||
context = super(APIObjectACLListView, self).get_serializer_context()
|
||||
if self.kwargs:
|
||||
context.update(
|
||||
{
|
||||
'content_object': self.get_content_object(),
|
||||
}
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
return None
|
||||
|
||||
return super(APIObjectACLListView, self).get_serializer(*args, **kwargs)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == 'GET':
|
||||
return AccessControlListSerializer
|
||||
else:
|
||||
return WritableAccessControlListSerializer
|
||||
|
||||
|
||||
class APIObjectACLView(generics.RetrieveDestroyAPIView):
|
||||
"""
|
||||
delete: Delete the selected access control list.
|
||||
get: Returns the details of the selected access control list.
|
||||
"""
|
||||
class ObjectACLAPIViewSet(ContentTypeViewMixin, ExternalObjectMixin, MayanAPIModelViewSet):
|
||||
content_type_url_kw_args = {
|
||||
'app_label': 'app_label',
|
||||
'model': 'model_name'
|
||||
}
|
||||
external_object_pk_url_kwarg = 'object_id'
|
||||
lookup_url_kwarg = 'acl_id'
|
||||
serializer_class = AccessControlListSerializer
|
||||
|
||||
def get_content_object(self):
|
||||
if self.request.method == 'GET':
|
||||
permission_required = permission_acl_view
|
||||
else:
|
||||
permission_required = permission_acl_edit
|
||||
|
||||
content_type = get_object_or_404(
|
||||
klass=ContentType, app_label=self.kwargs['app_label'],
|
||||
model=self.kwargs['model']
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.validated_data.update(
|
||||
{
|
||||
'object_id': self.external_object.pk,
|
||||
'content_type': self.get_content_type(),
|
||||
}
|
||||
)
|
||||
self.perform_create(serializer)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||
|
||||
content_object = get_object_or_404(
|
||||
klass=content_type.model_class(), pk=self.kwargs['object_id']
|
||||
)
|
||||
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_required, user=self.request.user,
|
||||
obj=content_object
|
||||
)
|
||||
|
||||
return content_object
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_content_object().acls.all()
|
||||
|
||||
|
||||
class APIObjectACLPermissionListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
get: Returns the access control list permission list.
|
||||
post: Add a new permission to the selected access control list.
|
||||
"""
|
||||
def get_acl(self):
|
||||
return get_object_or_404(
|
||||
klass=self.get_content_object().acls, pk=self.kwargs['acl_pk']
|
||||
)
|
||||
|
||||
def get_content_object(self):
|
||||
content_type = get_object_or_404(
|
||||
klass=ContentType, app_label=self.kwargs['app_label'],
|
||||
model=self.kwargs['model']
|
||||
)
|
||||
|
||||
content_object = get_object_or_404(
|
||||
klass=content_type.model_class(), pk=self.kwargs['object_id']
|
||||
)
|
||||
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_acl_view, user=self.request.user,
|
||||
obj=content_object
|
||||
)
|
||||
|
||||
return content_object
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_acl().permissions.all()
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
def get_external_object_permission(self):
|
||||
action = getattr(self, 'action', None)
|
||||
if action is None:
|
||||
return None
|
||||
|
||||
return super(APIObjectACLPermissionListView, self).get_serializer(*args, **kwargs)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == 'GET':
|
||||
return AccessControlListPermissionSerializer
|
||||
elif action in ['list', 'retrieve', 'permission_list', 'permission_inherited_list']:
|
||||
return permission_acl_view
|
||||
else:
|
||||
return WritableAccessControlListPermissionSerializer
|
||||
return permission_acl_edit
|
||||
|
||||
def get_serializer_context(self):
|
||||
context = super(APIObjectACLPermissionListView, self).get_serializer_context()
|
||||
if self.kwargs:
|
||||
context.update(
|
||||
{
|
||||
'acl': self.get_acl(),
|
||||
}
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class APIObjectACLPermissionView(generics.RetrieveDestroyAPIView):
|
||||
"""
|
||||
delete: Remove the permission from the selected access control list.
|
||||
get: Returns the details of the selected access control list permission.
|
||||
"""
|
||||
lookup_url_kwarg = 'permission_pk'
|
||||
serializer_class = AccessControlListPermissionSerializer
|
||||
|
||||
def get_acl(self):
|
||||
return get_object_or_404(
|
||||
klass=self.get_content_object().acls, pk=self.kwargs['acl_pk']
|
||||
)
|
||||
|
||||
def get_content_object(self):
|
||||
content_type = get_object_or_404(
|
||||
klass=ContentType, app_label=self.kwargs['app_label'],
|
||||
model=self.kwargs['model']
|
||||
)
|
||||
|
||||
content_object = get_object_or_404(
|
||||
klass=content_type.model_class(), pk=self.kwargs['object_id']
|
||||
)
|
||||
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_acl_view, user=self.request.user,
|
||||
obj=content_object
|
||||
)
|
||||
|
||||
return content_object
|
||||
def get_external_object_queryset(self):
|
||||
# Here we get a queryset the object model for which the event
|
||||
# will be accessed.
|
||||
return self.get_content_type().get_all_objects_for_this_type()
|
||||
|
||||
def get_queryset(self):
|
||||
return self.get_acl().permissions.all()
|
||||
obj = self.get_external_object()
|
||||
return obj.acls.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
context = super(APIObjectACLPermissionView, self).get_serializer_context()
|
||||
if self.kwargs:
|
||||
context.update(
|
||||
{
|
||||
'acl': self.get_acl(),
|
||||
}
|
||||
)
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='acl_id', methods=('post',),
|
||||
serializer_class=RolePermissionAddRemoveSerializer,
|
||||
url_name='permission-add', url_path='permissions/add'
|
||||
)
|
||||
def permission_add(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.permissions_add(instance=instance)
|
||||
headers = self.get_success_headers(data=serializer.data)
|
||||
return Response(
|
||||
serializer.data, headers=headers, status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
return context
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='acl_id',
|
||||
serializer_class=PermissionSerializer, url_name='permission-list',
|
||||
url_path='permissions'
|
||||
)
|
||||
def permission_list(self, request, *args, **kwargs):
|
||||
queryset = self.get_object().permissions.all()
|
||||
page = self.paginate_queryset(queryset)
|
||||
|
||||
serializer = self.get_serializer(
|
||||
queryset, many=True, context={'request': request}
|
||||
)
|
||||
|
||||
if page is not None:
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='acl_id',
|
||||
serializer_class=PermissionSerializer,
|
||||
url_name='permission-inherited-list', url_path='permissions/inherited'
|
||||
)
|
||||
def permission_inherited_list(self, request, *args, **kwargs):
|
||||
queryset = self.get_object().get_inherited_permissions()
|
||||
page = self.paginate_queryset(queryset)
|
||||
|
||||
serializer = self.get_serializer(
|
||||
queryset, many=True, context={'request': request}
|
||||
)
|
||||
|
||||
if page is not None:
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='acl_id',
|
||||
methods=('post',), serializer_class=RolePermissionAddRemoveSerializer,
|
||||
url_name='permission-remove', url_path='permissions/remove'
|
||||
)
|
||||
def permission_remove(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.permissions_remove(instance=instance)
|
||||
headers = self.get_success_headers(data=serializer.data)
|
||||
return Response(
|
||||
serializer.data, headers=headers, status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
@@ -12,205 +12,143 @@ from rest_framework.reverse import reverse
|
||||
from mayan.apps.common.serializers import ContentTypeSerializer
|
||||
from mayan.apps.permissions import Permission
|
||||
from mayan.apps.permissions.models import Role, StoredPermission
|
||||
from mayan.apps.permissions.permissions import permission_role_edit
|
||||
from mayan.apps.permissions.serializers import (
|
||||
PermissionSerializer, RoleSerializer
|
||||
)
|
||||
from mayan.apps.rest_api.mixins import (
|
||||
ExternalObjectListSerializerMixin, ExternalObjectSerializerMixin
|
||||
)
|
||||
from mayan.apps.rest_api.relations import MultiKwargHyperlinkedIdentityField
|
||||
|
||||
from .models import AccessControlList
|
||||
|
||||
|
||||
class AccessControlListSerializer(serializers.ModelSerializer):
|
||||
#TODO: Inherited permissions
|
||||
class AccessControlListSerializer(ExternalObjectSerializerMixin, serializers.ModelSerializer):
|
||||
content_type = ContentTypeSerializer(read_only=True)
|
||||
permissions_url = serializers.SerializerMethodField(
|
||||
help_text=_(
|
||||
'API URL pointing to the list of permissions for this access '
|
||||
'control list.'
|
||||
)
|
||||
)
|
||||
role = RoleSerializer(read_only=True)
|
||||
url = serializers.SerializerMethodField()
|
||||
permission_add_url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'content_type__app_label', 'lookup_url_kwarg': 'app_label',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'content_type__model', 'lookup_url_kwarg': 'model_name',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'object_id', 'lookup_url_kwarg': 'object_id',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk', 'lookup_url_kwarg': 'acl_id',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:object-acl-permission-add'
|
||||
)
|
||||
permission_list_url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'content_type__app_label', 'lookup_url_kwarg': 'app_label',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'content_type__model', 'lookup_url_kwarg': 'model_name',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'object_id', 'lookup_url_kwarg': 'object_id',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk', 'lookup_url_kwarg': 'acl_id',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:object-acl-permission-list'
|
||||
)
|
||||
permission_list_inherited_url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'content_type__app_label', 'lookup_url_kwarg': 'app_label',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'content_type__model', 'lookup_url_kwarg': 'model_name',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'object_id', 'lookup_url_kwarg': 'object_id',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk', 'lookup_url_kwarg': 'acl_id',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:object-acl-permission-inherited-list'
|
||||
)
|
||||
permission_remove_url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'content_type__app_label', 'lookup_url_kwarg': 'app_label',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'content_type__model', 'lookup_url_kwarg': 'model_name',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'object_id', 'lookup_url_kwarg': 'object_id',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk', 'lookup_url_kwarg': 'acl_id',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:object-acl-permission-remove'
|
||||
)
|
||||
role_id = serializers.CharField(
|
||||
label=_('Role ID'),
|
||||
help_text=_(
|
||||
'Primary key of the role of the ACL that will be created or edited.'
|
||||
), required=False, write_only=True
|
||||
)
|
||||
url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'content_type__app_label', 'lookup_url_kwarg': 'app_label',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'content_type__model', 'lookup_url_kwarg': 'model_name',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'object_id', 'lookup_url_kwarg': 'object_id',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk', 'lookup_url_kwarg': 'acl_id',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:object-acl-detail'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
external_object_model = Role
|
||||
external_object_pk_field = 'role_id'
|
||||
external_object_permission = permission_role_edit
|
||||
fields = (
|
||||
'content_type', 'id', 'object_id', 'permissions_url', 'role', 'url'
|
||||
'content_type', 'id', 'object_id', 'permission_add_url',
|
||||
'permission_list_url', 'permission_list_inherited_url',
|
||||
'permission_remove_url', 'role', 'role_id',
|
||||
'url'
|
||||
)
|
||||
model = AccessControlList
|
||||
|
||||
def get_permissions_url(self, instance):
|
||||
return reverse(
|
||||
viewname='rest_api:accesscontrollist-permission-list', kwargs={
|
||||
'app_label': instance.content_type.app_label,
|
||||
'model': instance.content_type.model,
|
||||
'object_id': instance.object_id,
|
||||
'acl_pk': instance.pk
|
||||
}, request=self.context['request'], format=self.context['format']
|
||||
)
|
||||
|
||||
def get_url(self, instance):
|
||||
return reverse(
|
||||
'rest_api:accesscontrollist-detail', kwargs={
|
||||
'app_label': instance.content_type.app_label,
|
||||
'model': instance.content_type.model,
|
||||
'object_id': instance.object_id,
|
||||
'acl_pk': instance.pk
|
||||
}, request=self.context['request'], format=self.context['format']
|
||||
)
|
||||
|
||||
|
||||
class AccessControlListPermissionSerializer(PermissionSerializer):
|
||||
acl_permission_url = serializers.SerializerMethodField(
|
||||
help_text=_(
|
||||
'API URL pointing to a permission in relation to the '
|
||||
'access control list to which it is attached. This URL is '
|
||||
'different than the canonical workflow URL.'
|
||||
)
|
||||
)
|
||||
acl_url = serializers.SerializerMethodField()
|
||||
|
||||
def get_acl_permission_url(self, instance):
|
||||
return reverse(
|
||||
'rest_api:accesscontrollist-permission-detail', kwargs={
|
||||
'app_label': self.context['acl'].content_type.app_label,
|
||||
'model': self.context['acl'].content_type.model,
|
||||
'object_id': self.context['acl'].object_id,
|
||||
'acl_pk': self.context['acl'].pk,
|
||||
'permission_pk': instance.stored_permission.pk
|
||||
}, request=self.context['request'], format=self.context['format']
|
||||
)
|
||||
|
||||
def get_acl_url(self, instance):
|
||||
return reverse(
|
||||
'rest_api:accesscontrollist-detail', kwargs={
|
||||
'app_label': self.context['acl'].content_type.app_label,
|
||||
'model': self.context['acl'].content_type.model,
|
||||
'object_id': self.context['acl'].object_id,
|
||||
'acl_pk': self.context['acl'].pk
|
||||
}, request=self.context['request'], format=self.context['format']
|
||||
)
|
||||
|
||||
|
||||
class WritableAccessControlListPermissionSerializer(AccessControlListPermissionSerializer):
|
||||
permission_pk = serializers.CharField(
|
||||
help_text=_(
|
||||
'Primary key of the new permission to grant to the access control '
|
||||
'list.'
|
||||
), write_only=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
fields = ('namespace',)
|
||||
read_only_fields = ('namespace',)
|
||||
read_only_fields = ('object_id',)
|
||||
|
||||
def create(self, validated_data):
|
||||
for permission in validated_data['permissions']:
|
||||
self.context['acl'].permissions.add(permission)
|
||||
role = self.get_external_object()
|
||||
|
||||
return validated_data['permissions'][0]
|
||||
if role:
|
||||
validated_data['role'] = role
|
||||
|
||||
def validate(self, attrs):
|
||||
permissions_pk_list = attrs.pop('permission_pk', None)
|
||||
permissions_result = []
|
||||
|
||||
if permissions_pk_list:
|
||||
for pk in permissions_pk_list.split(','):
|
||||
try:
|
||||
permission = Permission.get(pk=pk)
|
||||
except KeyError:
|
||||
raise ValidationError(_('No such permission: %s') % pk)
|
||||
else:
|
||||
# Accumulate valid stored permission pks
|
||||
permissions_result.append(permission.pk)
|
||||
|
||||
attrs['permissions'] = StoredPermission.objects.filter(
|
||||
pk__in=permissions_result
|
||||
)
|
||||
return attrs
|
||||
|
||||
|
||||
class WritableAccessControlListSerializer(serializers.ModelSerializer):
|
||||
content_type = ContentTypeSerializer(read_only=True)
|
||||
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.'
|
||||
), write_only=True
|
||||
)
|
||||
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 get_permissions_url(self, instance):
|
||||
return reverse(
|
||||
'rest_api:accesscontrollist-permission-list', kwargs={
|
||||
'app_label': instance.content_type.app_label,
|
||||
'model': instance.content_type.model,
|
||||
'object_id': instance.object_id,
|
||||
'acl_pk': instance.pk
|
||||
}, request=self.context['request'], format=self.context['format']
|
||||
return super(AccessControlListSerializer, self).create(
|
||||
validated_data=validated_data
|
||||
)
|
||||
|
||||
def get_url(self, instance):
|
||||
return reverse(
|
||||
'rest_api:accesscontrollist-detail', kwargs={
|
||||
'app_label': instance.content_type.app_label,
|
||||
'model': instance.content_type.model,
|
||||
'object_id': instance.object_id,
|
||||
'acl_pk': instance.pk
|
||||
}, request=self.context['request'], format=self.context['format']
|
||||
def update(self, instance, validated_data):
|
||||
role = self.get_external_object()
|
||||
|
||||
if role:
|
||||
validated_data['role'] = role
|
||||
|
||||
return super(AccessControlListSerializer, self).update(
|
||||
instance=instance, validated_data=validated_data
|
||||
)
|
||||
|
||||
def validate(self, attrs):
|
||||
attrs['content_type'] = ContentType.objects.get_for_model(
|
||||
self.context['content_object']
|
||||
)
|
||||
attrs['object_id'] = self.context['content_object'].pk
|
||||
|
||||
try:
|
||||
attrs['role'] = Role.objects.get(pk=attrs.pop('role_pk'))
|
||||
except Role.DoesNotExist as exception:
|
||||
raise ValidationError(force_text(exception))
|
||||
|
||||
permissions_pk_list = attrs.pop('permissions_pk_list', None)
|
||||
permissions_result = []
|
||||
|
||||
if permissions_pk_list:
|
||||
for pk in permissions_pk_list.split(','):
|
||||
try:
|
||||
permission = Permission.get(pk=pk)
|
||||
except KeyError:
|
||||
raise ValidationError(_('No such permission: %s') % pk)
|
||||
else:
|
||||
# Accumulate valid stored permission pks
|
||||
permissions_result.append(permission.pk)
|
||||
|
||||
instance = AccessControlList(**attrs)
|
||||
|
||||
try:
|
||||
instance.full_clean()
|
||||
except DjangoValidationError as exception:
|
||||
raise ValidationError(exception)
|
||||
|
||||
# Add a queryset of valid stored permissions so that they get added
|
||||
# after the ACL gets created.
|
||||
attrs['permissions'] = StoredPermission.objects.filter(
|
||||
pk__in=permissions_result
|
||||
)
|
||||
return attrs
|
||||
|
||||
@@ -4,72 +4,93 @@ from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from rest_framework import status
|
||||
|
||||
from mayan.apps.common.tests.mixins import TestModelTestMixin
|
||||
from mayan.apps.documents.permissions import permission_document_view
|
||||
from mayan.apps.documents.tests import DocumentTestMixin
|
||||
from mayan.apps.permissions.tests.literals import TEST_ROLE_LABEL
|
||||
from mayan.apps.permissions.tests.mixins import PermissionTestMixin, RoleTestMixin
|
||||
from mayan.apps.rest_api.tests import BaseAPITestCase
|
||||
|
||||
from ..classes import ModelPermission
|
||||
from ..models import AccessControlList
|
||||
from ..permissions import permission_acl_view
|
||||
from ..permissions import permission_acl_edit, permission_acl_view
|
||||
|
||||
from .mixins import ACLTestMixin
|
||||
|
||||
|
||||
class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
class ACLAPITestCase(ACLTestMixin, RoleTestMixin, PermissionTestMixin, TestModelTestMixin, BaseAPITestCase):
|
||||
def setUp(self):
|
||||
super(ACLAPITestCase, self).setUp()
|
||||
self.login_admin_user()
|
||||
|
||||
self.document_content_type = ContentType.objects.get_for_model(
|
||||
self.document
|
||||
self._create_test_model()
|
||||
self._create_test_object()
|
||||
self._create_test_acl()
|
||||
ModelPermission.register(
|
||||
model=self.test_object._meta.model, permissions=(
|
||||
permission_acl_edit, permission_acl_view,
|
||||
)
|
||||
)
|
||||
|
||||
def _create_acl(self):
|
||||
self.acl = AccessControlList.objects.create(
|
||||
content_object=self.document,
|
||||
role=self.role
|
||||
self._create_test_permission()
|
||||
ModelPermission.register(
|
||||
model=self.test_object._meta.model, permissions=(
|
||||
self.test_permission,
|
||||
)
|
||||
)
|
||||
self.test_acl.permissions.add(self.test_permission.stored_permission)
|
||||
self._inject_test_object_content_type()
|
||||
|
||||
def _request_object_acl_list_api_view(self):
|
||||
return self.get(
|
||||
viewname='rest_api:object-acl-list',
|
||||
kwargs=self.test_content_object_view_kwargs
|
||||
)
|
||||
|
||||
self.acl.permissions.add(permission_document_view.stored_permission)
|
||||
def test_object_acl_list_api_view_no_permission(self):
|
||||
response = self._request_object_acl_list_api_view()
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def test_object_acl_list_view(self):
|
||||
self._create_acl()
|
||||
def test_object_acl_list_api_view_with_access(self):
|
||||
self.grant_access(obj=self.test_object, permission=permission_acl_view)
|
||||
|
||||
response = self.get(
|
||||
viewname='rest_api:accesscontrollist-list',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'object_id': self.document.pk
|
||||
}
|
||||
)
|
||||
response = self._request_object_acl_list_api_view()
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
response.data['results'][0]['content_type']['app_label'],
|
||||
self.document_content_type.app_label
|
||||
self.test_object_content_type.app_label
|
||||
)
|
||||
self.assertEqual(
|
||||
response.data['results'][0]['role']['label'], TEST_ROLE_LABEL
|
||||
response.data['results'][0]['role']['label'],
|
||||
self.test_acl.role.label
|
||||
)
|
||||
|
||||
def test_object_acl_delete_view(self):
|
||||
self._create_acl()
|
||||
def _request_acl_delete_api_view(self):
|
||||
kwargs = self.test_content_object_view_kwargs.copy()
|
||||
kwargs['acl_id'] = self.test_acl.pk
|
||||
|
||||
response = self.delete(
|
||||
viewname='rest_api:accesscontrollist-detail',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'object_id': self.document.pk,
|
||||
'acl_pk': self.acl.pk
|
||||
}
|
||||
return self.delete(
|
||||
viewname='rest_api:object-acl-detail',
|
||||
kwargs=kwargs
|
||||
)
|
||||
|
||||
def test_object_acl_delete_api_view_with_access(self):
|
||||
self.grant_access(obj=self.test_object, permission=permission_acl_edit)
|
||||
response = self._request_acl_delete_api_view()
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
self.assertEqual(AccessControlList.objects.count(), 0)
|
||||
self.assertTrue(self.test_acl not in AccessControlList.objects.all())
|
||||
|
||||
def test_object_acl_delete_api_view_no_permission(self):
|
||||
response = self._request_acl_delete_api_view()
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertTrue(self.test_acl in AccessControlList.objects.all())
|
||||
|
||||
def test_object_acl_detail_view(self):
|
||||
self._create_acl()
|
||||
|
||||
response = self.get(
|
||||
viewname='rest_api:accesscontrollist-detail',
|
||||
viewname='rest_api:object-acl-detail',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
@@ -90,12 +111,12 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
permission = self.acl.permissions.first()
|
||||
|
||||
response = self.delete(
|
||||
viewname='rest_api:accesscontrollist-permission-detail',
|
||||
viewname='rest_api:object-acl-permission-detail',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'model_name': self.document_content_type.model,
|
||||
'object_id': self.document.pk,
|
||||
'acl_pk': self.acl.pk, 'permission_pk': permission.pk
|
||||
'acl_id': self.acl.pk, 'permission_id': permission.pk
|
||||
}
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
@@ -106,10 +127,10 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
permission = self.acl.permissions.first()
|
||||
|
||||
response = self.get(
|
||||
viewname='rest_api:accesscontrollist-permission-detail',
|
||||
viewname='rest_api:object-acl-permission-detail',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'model_name': self.document_content_type.model,
|
||||
'object_id': self.document.pk, 'acl_pk': self.acl.pk,
|
||||
'permission_pk': permission.pk
|
||||
}
|
||||
@@ -123,12 +144,12 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
self._create_acl()
|
||||
|
||||
response = self.get(
|
||||
viewname='rest_api:accesscontrollist-permission-list',
|
||||
viewname='rest_api:object-acl-permission-list',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'model_name': self.document_content_type.model,
|
||||
'object_id': self.document.pk,
|
||||
'acl_pk': self.acl.pk
|
||||
'acl_id': self.acl.pk
|
||||
}
|
||||
)
|
||||
|
||||
@@ -141,12 +162,12 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
self._create_acl()
|
||||
|
||||
response = self.post(
|
||||
viewname='rest_api:accesscontrollist-permission-list',
|
||||
viewname='rest_api:object-acl-permission-list',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'model_name': self.document_content_type.model,
|
||||
'object_id': self.document.pk, 'acl_pk': self.acl.pk
|
||||
}, data={'permission_pk': permission_acl_view.pk}
|
||||
}, data={'permission_id': permission_acl_view.pk}
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
@@ -159,17 +180,17 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
|
||||
def test_object_acl_post_no_permissions_added_view(self):
|
||||
response = self.post(
|
||||
viewname='rest_api:accesscontrollist-list',
|
||||
viewname='rest_api:object-acl-list',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'model_name': self.document_content_type.model,
|
||||
'object_id': self.document.pk
|
||||
}, data={'role_pk': self.role.pk}
|
||||
}, data={'role_id': self.test_role.pk}
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(
|
||||
self.document.acls.first().role, self.role
|
||||
self.document.acls.first().role, self.test_role
|
||||
)
|
||||
self.assertEqual(
|
||||
self.document.acls.first().content_object, self.document
|
||||
@@ -180,13 +201,13 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
|
||||
def test_object_acl_post_with_permissions_added_view(self):
|
||||
response = self.post(
|
||||
viewname='rest_api:accesscontrollist-list',
|
||||
viewname='rest_api:object-acl-list',
|
||||
kwargs={
|
||||
'app_label': self.document_content_type.app_label,
|
||||
'model': self.document_content_type.model,
|
||||
'object_id': self.document.pk
|
||||
}, data={
|
||||
'role_pk': self.role.pk,
|
||||
'role_pk': self.test_role.pk,
|
||||
'permissions_pk_list': permission_acl_view.pk
|
||||
|
||||
}
|
||||
@@ -197,7 +218,7 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
|
||||
self.document.acls.first().content_object, self.document
|
||||
)
|
||||
self.assertEqual(
|
||||
self.document.acls.first().role, self.role
|
||||
self.document.acls.first().role, self.test_role
|
||||
)
|
||||
self.assertEqual(
|
||||
self.document.acls.first().permissions.first(),
|
||||
|
||||
@@ -129,7 +129,6 @@ class PermissionTestCase(DocumentTestMixin, BaseTestCase):
|
||||
|
||||
# Since document_1 and document_2 are of document_type_1
|
||||
# they are the only ones that should be returned
|
||||
|
||||
self.assertTrue(self.test_document_1 in result)
|
||||
self.assertTrue(self.test_document_2 in result)
|
||||
self.assertTrue(self.test_document_3 not in result)
|
||||
@@ -230,9 +229,6 @@ class InheritedPermissionTestCase(TestModelTestMixin, PermissionTestMixin, RoleT
|
||||
|
||||
self.assertTrue(self.test_permission.stored_permission in queryset)
|
||||
|
||||
#self._delete_test_model(model_name='TestModelParent')
|
||||
#elf._delete_test_model(model_name='TestModelChild')
|
||||
|
||||
def test_retrieve_inherited_related_grandparent_parent_child_permission(self):
|
||||
self._create_test_permission()
|
||||
|
||||
@@ -291,7 +287,3 @@ class InheritedPermissionTestCase(TestModelTestMixin, PermissionTestMixin, RoleT
|
||||
)
|
||||
|
||||
self.assertTrue(self.test_permission.stored_permission in queryset)
|
||||
|
||||
#self._delete_test_model(model_name='TestModelGrandParent')
|
||||
#self._delete_test_model(model_name='TestModelParent')
|
||||
#self._delete_test_model(model_name='TestModelChild')
|
||||
|
||||
@@ -2,10 +2,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .api_views import (
|
||||
APIObjectACLListView, APIObjectACLPermissionListView,
|
||||
APIObjectACLPermissionView, APIObjectACLView
|
||||
)
|
||||
from .api_views import ObjectACLAPIViewSet
|
||||
from .views import (
|
||||
ACLCreateView, ACLDeleteView, ACLListView, ACLPermissionsView
|
||||
)
|
||||
@@ -29,6 +26,14 @@ urlpatterns = [
|
||||
),
|
||||
]
|
||||
|
||||
api_router_entries = (
|
||||
{
|
||||
'prefix': r'apps/(?P<app_label>[^/.]+)/models/(?P<model_name>[^/.]+)/objects/(?P<object_id>[^/.]+)/acls',
|
||||
'viewset': ObjectACLAPIViewSet, 'basename': 'object-acl'
|
||||
},
|
||||
)
|
||||
|
||||
'''
|
||||
api_urls = [
|
||||
url(
|
||||
regex=r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/acls/$',
|
||||
@@ -49,3 +54,4 @@ api_urls = [
|
||||
view=APIObjectACLPermissionView.as_view()
|
||||
),
|
||||
]
|
||||
'''
|
||||
|
||||
@@ -58,7 +58,7 @@ class ACLCreateView(ContentTypeViewMixin, ExternalObjectMixin, SingleObjectCreat
|
||||
pk__in=self.get_external_object().acls.values('role')
|
||||
),
|
||||
'widget_attributes': {'class': 'select2'},
|
||||
'_user': self.request.user
|
||||
'user': self.request.user
|
||||
}
|
||||
|
||||
def get_instance_extra_data(self):
|
||||
@@ -174,12 +174,10 @@ class ACLPermissionsView(AddRemoveView):
|
||||
|
||||
def get_disabled_choices(self):
|
||||
"""
|
||||
Get permissions from a parent's ACLs. We return a list since that is
|
||||
what the form widget's can process.
|
||||
Get permissions from a parent's ACLs or directly granted to the role.
|
||||
We return a list since that is what the form widget's can process.
|
||||
"""
|
||||
return self.main_object.get_inherited_permissions().values_list(
|
||||
'pk', flat=True
|
||||
)
|
||||
return self.main_object.get_inherited_permissions().values_list('pk', flat=True)
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -195,9 +193,10 @@ class ACLPermissionsView(AddRemoveView):
|
||||
def get_list_added_help_text(self):
|
||||
if self.main_object.get_inherited_permissions():
|
||||
return _(
|
||||
'Disabled permissions are inherited from a parent object and '
|
||||
'can\'t be removed from this view, they need to be removed '
|
||||
'from the parent object\'s ACL view.'
|
||||
'Disabled permissions are inherited from a parent object or '
|
||||
'directly granted to the role and can\'t be removed from this '
|
||||
'view. Inherited permissions need to be removed from the '
|
||||
'parent object\'s ACL or from them role via the Setup menu.'
|
||||
)
|
||||
|
||||
def get_list_added_queryset(self):
|
||||
@@ -210,9 +209,10 @@ class ACLPermissionsView(AddRemoveView):
|
||||
remove from the parent first to enable the choice in the form,
|
||||
remove it from the ACL and then re-add it to the parent ACL.
|
||||
"""
|
||||
queryset = super(ACLPermissionsView, self).get_list_added_queryset()
|
||||
queryset_acl = super(ACLPermissionsView, self).get_list_added_queryset()
|
||||
|
||||
return (
|
||||
queryset | self.main_object.get_inherited_permissions()
|
||||
queryset_acl | self.main_object.get_inherited_permissions()
|
||||
).distinct()
|
||||
|
||||
def get_secondary_object_source_queryset(self):
|
||||
|
||||
@@ -259,6 +259,11 @@ class TestModelTestMixin(object):
|
||||
|
||||
self.__class__._test_models.append(model_name)
|
||||
|
||||
def _create_test_object(self, model_name='TestModel', **kwargs):
|
||||
TestModel = getattr(self, model_name)
|
||||
|
||||
self.test_object = TestModel.objects.create(**kwargs)
|
||||
|
||||
def _delete_test_model(self, model_name='TestModel'):
|
||||
TestModel = getattr(self, model_name)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user