Refactor ACL app API

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-03-02 16:03:29 -04:00
parent 0cbd9e0d45
commit 2a67cf271e
7 changed files with 324 additions and 438 deletions
+106 -182
View File
@@ -3,201 +3,125 @@ from __future__ import absolute_import, unicode_literals
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404 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 .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 AccessControlListSerializer
AccessControlListPermissionSerializer, AccessControlListSerializer,
WritableAccessControlListPermissionSerializer,
WritableAccessControlListSerializer
)
class APIObjectACLListView(generics.ListCreateAPIView): class ObjectACLAPIViewSet(ContentTypeViewMixin, ExternalObjectMixin, MayanAPIModelViewSet):
""" content_type_url_kw_args = {
get: Returns a list of all the object's access control lists 'app_label': 'app_label',
post: Create a new access control list for the selected object. 'model': 'model_name'
""" }
def get_content_object(self): external_object_pk_url_kwarg = 'object_id'
content_type = get_object_or_404( lookup_url_kwarg = 'acl_id'
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.
"""
serializer_class = AccessControlListSerializer serializer_class = AccessControlListSerializer
def get_content_object(self): def create(self, request, *args, **kwargs):
if self.request.method == 'GET': serializer = self.get_serializer(data=request.data)
permission_required = permission_acl_view serializer.is_valid(raise_exception=True)
else: serializer.validated_data.update(
permission_required = permission_acl_edit {
'object_id': self.external_object.pk,
content_type = get_object_or_404( 'content_type': self.get_content_type(),
klass=ContentType, app_label=self.kwargs['app_label'], }
model=self.kwargs['model']
) )
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( def get_external_object_permission(self):
klass=content_type.model_class(), pk=self.kwargs['object_id'] action = getattr(self, 'action', None)
) if action is None:
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:
return None return None
elif action in ['list', 'retrieve', 'permission_list', 'permission_inherited_list']:
return super(APIObjectACLPermissionListView, self).get_serializer(*args, **kwargs) return permission_acl_view
def get_serializer_class(self):
if self.request.method == 'GET':
return AccessControlListPermissionSerializer
else: else:
return WritableAccessControlListPermissionSerializer return permission_acl_edit
def get_serializer_context(self): def get_external_object_queryset(self):
context = super(APIObjectACLPermissionListView, self).get_serializer_context() # Here we get a queryset the object model for which the event
if self.kwargs: # will be accessed.
context.update( return self.get_content_type().get_all_objects_for_this_type()
{
'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_queryset(self): def get_queryset(self):
return self.get_acl().permissions.all() obj = self.get_external_object()
return obj.acls.all()
def get_serializer_context(self): @action(
context = super(APIObjectACLPermissionView, self).get_serializer_context() detail=True, lookup_url_kwarg='acl_id', methods=('post',),
if self.kwargs: serializer_class=RolePermissionAddRemoveSerializer,
context.update( url_name='permission-add', url_path='permissions/add'
{ )
'acl': self.get_acl(), 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
)
+119 -181
View File
@@ -12,205 +12,143 @@ from rest_framework.reverse import reverse
from mayan.apps.common.serializers import ContentTypeSerializer from mayan.apps.common.serializers import ContentTypeSerializer
from mayan.apps.permissions import Permission from mayan.apps.permissions import Permission
from mayan.apps.permissions.models import Role, StoredPermission from mayan.apps.permissions.models import Role, StoredPermission
from mayan.apps.permissions.permissions import permission_role_edit
from mayan.apps.permissions.serializers import ( from mayan.apps.permissions.serializers import (
PermissionSerializer, RoleSerializer PermissionSerializer, RoleSerializer
) )
from mayan.apps.rest_api.mixins import (
ExternalObjectListSerializerMixin, ExternalObjectSerializerMixin
)
from mayan.apps.rest_api.relations import MultiKwargHyperlinkedIdentityField
from .models import AccessControlList from .models import AccessControlList
class AccessControlListSerializer(serializers.ModelSerializer): #TODO: Inherited permissions
class AccessControlListSerializer(ExternalObjectSerializerMixin, serializers.ModelSerializer):
content_type = ContentTypeSerializer(read_only=True) 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) 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: class Meta:
external_object_model = Role
external_object_pk_field = 'role_id'
external_object_permission = permission_role_edit
fields = ( 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 model = AccessControlList
read_only_fields = ('object_id',)
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',)
def create(self, validated_data): def create(self, validated_data):
for permission in validated_data['permissions']: role = self.get_external_object()
self.context['acl'].permissions.add(permission)
return validated_data['permissions'][0] if role:
validated_data['role'] = role
def validate(self, attrs): return super(AccessControlListSerializer, self).create(
permissions_pk_list = attrs.pop('permission_pk', None) validated_data=validated_data
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']
) )
def get_url(self, instance): def update(self, instance, validated_data):
return reverse( role = self.get_external_object()
'rest_api:accesscontrollist-detail', kwargs={
'app_label': instance.content_type.app_label, if role:
'model': instance.content_type.model, validated_data['role'] = role
'object_id': instance.object_id,
'acl_pk': instance.pk return super(AccessControlListSerializer, self).update(
}, request=self.context['request'], format=self.context['format'] 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
+73 -52
View File
@@ -4,72 +4,93 @@ from django.contrib.contenttypes.models import ContentType
from rest_framework import status 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.permissions import permission_document_view
from mayan.apps.documents.tests import DocumentTestMixin from mayan.apps.documents.tests import DocumentTestMixin
from mayan.apps.permissions.tests.literals import TEST_ROLE_LABEL 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 mayan.apps.rest_api.tests import BaseAPITestCase
from ..classes import ModelPermission
from ..models import AccessControlList 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): def setUp(self):
super(ACLAPITestCase, self).setUp() super(ACLAPITestCase, self).setUp()
self.login_admin_user()
self.document_content_type = ContentType.objects.get_for_model( self._create_test_model()
self.document 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._create_test_permission()
self.acl = AccessControlList.objects.create( ModelPermission.register(
content_object=self.document, model=self.test_object._meta.model, permissions=(
role=self.role 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): def test_object_acl_list_api_view_with_access(self):
self._create_acl() self.grant_access(obj=self.test_object, permission=permission_acl_view)
response = self.get( response = self._request_object_acl_list_api_view()
viewname='rest_api:accesscontrollist-list', self.assertEqual(response.status_code, status.HTTP_200_OK)
kwargs={
'app_label': self.document_content_type.app_label,
'model': self.document_content_type.model,
'object_id': self.document.pk
}
)
self.assertEqual( self.assertEqual(
response.data['results'][0]['content_type']['app_label'], response.data['results'][0]['content_type']['app_label'],
self.document_content_type.app_label self.test_object_content_type.app_label
) )
self.assertEqual( 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): def _request_acl_delete_api_view(self):
self._create_acl() kwargs = self.test_content_object_view_kwargs.copy()
kwargs['acl_id'] = self.test_acl.pk
response = self.delete( return self.delete(
viewname='rest_api:accesscontrollist-detail', viewname='rest_api:object-acl-detail',
kwargs={ kwargs=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
}
) )
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(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): def test_object_acl_detail_view(self):
self._create_acl() self._create_acl()
response = self.get( response = self.get(
viewname='rest_api:accesscontrollist-detail', viewname='rest_api:object-acl-detail',
kwargs={ kwargs={
'app_label': self.document_content_type.app_label, 'app_label': self.document_content_type.app_label,
'model': self.document_content_type.model, 'model': self.document_content_type.model,
@@ -90,12 +111,12 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
permission = self.acl.permissions.first() permission = self.acl.permissions.first()
response = self.delete( response = self.delete(
viewname='rest_api:accesscontrollist-permission-detail', viewname='rest_api:object-acl-permission-detail',
kwargs={ kwargs={
'app_label': self.document_content_type.app_label, '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, '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) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
@@ -106,10 +127,10 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
permission = self.acl.permissions.first() permission = self.acl.permissions.first()
response = self.get( response = self.get(
viewname='rest_api:accesscontrollist-permission-detail', viewname='rest_api:object-acl-permission-detail',
kwargs={ kwargs={
'app_label': self.document_content_type.app_label, '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, 'object_id': self.document.pk, 'acl_pk': self.acl.pk,
'permission_pk': permission.pk 'permission_pk': permission.pk
} }
@@ -123,12 +144,12 @@ class ACLAPITestCase(DocumentTestMixin, BaseAPITestCase):
self._create_acl() self._create_acl()
response = self.get( response = self.get(
viewname='rest_api:accesscontrollist-permission-list', viewname='rest_api:object-acl-permission-list',
kwargs={ kwargs={
'app_label': self.document_content_type.app_label, '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, '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() self._create_acl()
response = self.post( response = self.post(
viewname='rest_api:accesscontrollist-permission-list', viewname='rest_api:object-acl-permission-list',
kwargs={ kwargs={
'app_label': self.document_content_type.app_label, '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 '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) 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): def test_object_acl_post_no_permissions_added_view(self):
response = self.post( response = self.post(
viewname='rest_api:accesscontrollist-list', viewname='rest_api:object-acl-list',
kwargs={ kwargs={
'app_label': self.document_content_type.app_label, '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 '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(response.status_code, status.HTTP_201_CREATED)
self.assertEqual( self.assertEqual(
self.document.acls.first().role, self.role self.document.acls.first().role, self.test_role
) )
self.assertEqual( self.assertEqual(
self.document.acls.first().content_object, self.document 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): def test_object_acl_post_with_permissions_added_view(self):
response = self.post( response = self.post(
viewname='rest_api:accesscontrollist-list', viewname='rest_api:object-acl-list',
kwargs={ kwargs={
'app_label': self.document_content_type.app_label, 'app_label': self.document_content_type.app_label,
'model': self.document_content_type.model, 'model': self.document_content_type.model,
'object_id': self.document.pk 'object_id': self.document.pk
}, data={ }, data={
'role_pk': self.role.pk, 'role_pk': self.test_role.pk,
'permissions_pk_list': permission_acl_view.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.document.acls.first().content_object, self.document
) )
self.assertEqual( self.assertEqual(
self.document.acls.first().role, self.role self.document.acls.first().role, self.test_role
) )
self.assertEqual( self.assertEqual(
self.document.acls.first().permissions.first(), self.document.acls.first().permissions.first(),
-8
View File
@@ -129,7 +129,6 @@ class PermissionTestCase(DocumentTestMixin, BaseTestCase):
# Since document_1 and document_2 are of document_type_1 # Since document_1 and document_2 are of document_type_1
# they are the only ones that should be returned # they are the only ones that should be returned
self.assertTrue(self.test_document_1 in result) self.assertTrue(self.test_document_1 in result)
self.assertTrue(self.test_document_2 in result) self.assertTrue(self.test_document_2 in result)
self.assertTrue(self.test_document_3 not 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.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): def test_retrieve_inherited_related_grandparent_parent_child_permission(self):
self._create_test_permission() self._create_test_permission()
@@ -291,7 +287,3 @@ class InheritedPermissionTestCase(TestModelTestMixin, PermissionTestMixin, RoleT
) )
self.assertTrue(self.test_permission.stored_permission in queryset) 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')
+10 -4
View File
@@ -2,10 +2,7 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from .api_views import ( from .api_views import ObjectACLAPIViewSet
APIObjectACLListView, APIObjectACLPermissionListView,
APIObjectACLPermissionView, APIObjectACLView
)
from .views import ( from .views import (
ACLCreateView, ACLDeleteView, ACLListView, ACLPermissionsView 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 = [ api_urls = [
url( url(
regex=r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/acls/$', 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() view=APIObjectACLPermissionView.as_view()
), ),
] ]
'''
+11 -11
View File
@@ -58,7 +58,7 @@ class ACLCreateView(ContentTypeViewMixin, ExternalObjectMixin, SingleObjectCreat
pk__in=self.get_external_object().acls.values('role') pk__in=self.get_external_object().acls.values('role')
), ),
'widget_attributes': {'class': 'select2'}, 'widget_attributes': {'class': 'select2'},
'_user': self.request.user 'user': self.request.user
} }
def get_instance_extra_data(self): def get_instance_extra_data(self):
@@ -174,12 +174,10 @@ class ACLPermissionsView(AddRemoveView):
def get_disabled_choices(self): def get_disabled_choices(self):
""" """
Get permissions from a parent's ACLs. We return a list since that is Get permissions from a parent's ACLs or directly granted to the role.
what the form widget's can process. We return a list since that is what the form widget's can process.
""" """
return self.main_object.get_inherited_permissions().values_list( return self.main_object.get_inherited_permissions().values_list('pk', flat=True)
'pk', flat=True
)
def get_extra_context(self): def get_extra_context(self):
return { return {
@@ -195,9 +193,10 @@ class ACLPermissionsView(AddRemoveView):
def get_list_added_help_text(self): def get_list_added_help_text(self):
if self.main_object.get_inherited_permissions(): if self.main_object.get_inherited_permissions():
return _( return _(
'Disabled permissions are inherited from a parent object and ' 'Disabled permissions are inherited from a parent object or '
'can\'t be removed from this view, they need to be removed ' 'directly granted to the role and can\'t be removed from this '
'from the parent object\'s ACL view.' '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): 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 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. 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 ( return (
queryset | self.main_object.get_inherited_permissions() queryset_acl | self.main_object.get_inherited_permissions()
).distinct() ).distinct()
def get_secondary_object_source_queryset(self): def get_secondary_object_source_queryset(self):
+5
View File
@@ -259,6 +259,11 @@ class TestModelTestMixin(object):
self.__class__._test_models.append(model_name) 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'): def _delete_test_model(self, model_name='TestModel'):
TestModel = getattr(self, model_name) TestModel = getattr(self, model_name)