diff --git a/mayan/apps/permissions/api_views.py b/mayan/apps/permissions/api_views.py index 3e0b1db34c..c6777bc76c 100644 --- a/mayan/apps/permissions/api_views.py +++ b/mayan/apps/permissions/api_views.py @@ -11,7 +11,9 @@ from .permissions import ( permission_role_create, permission_role_delete, permission_role_edit, permission_role_view ) -from .serializers import PermissionSerializer, RoleSerializer +from .serializers import ( + PermissionSerializer, RoleSerializer, WritableRoleSerializer +) class APIPermissionList(generics.ListAPIView): @@ -27,13 +29,11 @@ class APIPermissionList(generics.ListAPIView): class APIRoleListView(generics.ListCreateAPIView): - serializer_class = RoleSerializer - queryset = Role.objects.all() - - permission_classes = (MayanPermission,) filter_backends = (MayanObjectPermissionsFilter,) mayan_object_permissions = {'GET': (permission_role_view,)} mayan_view_permissions = {'POST': (permission_role_create,)} + permission_classes = (MayanPermission,) + queryset = Role.objects.all() def get(self, *args, **kwargs): """ @@ -42,6 +42,12 @@ class APIRoleListView(generics.ListCreateAPIView): return super(APIRoleListView, self).get(*args, **kwargs) + def get_serializer_class(self): + if self.request.method == 'GET': + return RoleSerializer + elif self.request.method == 'POST': + return WritableRoleSerializer + def post(self, *args, **kwargs): """ Create a new role. @@ -51,16 +57,14 @@ class APIRoleListView(generics.ListCreateAPIView): class APIRoleView(generics.RetrieveUpdateDestroyAPIView): - serializer_class = RoleSerializer - queryset = Role.objects.all() - - permission_classes = (MayanPermission,) mayan_object_permissions = { 'GET': (permission_role_view,), 'PUT': (permission_role_edit,), 'PATCH': (permission_role_edit,), 'DELETE': (permission_role_delete,) } + permission_classes = (MayanPermission,) + queryset = Role.objects.all() def delete(self, *args, **kwargs): """ @@ -76,6 +80,12 @@ class APIRoleView(generics.RetrieveUpdateDestroyAPIView): return super(APIRoleView, self).get(*args, **kwargs) + def get_serializer_class(self): + if self.request.method == 'GET': + return RoleSerializer + elif self.request.method in ('PATCH', 'PUT'): + return WritableRoleSerializer + def patch(self, *args, **kwargs): """ Edit the selected role. diff --git a/mayan/apps/permissions/models.py b/mayan/apps/permissions/models.py index 35b55cf0b9..af35e599ea 100644 --- a/mayan/apps/permissions/models.py +++ b/mayan/apps/permissions/models.py @@ -8,6 +8,7 @@ from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ +from .classes import Permission from .managers import RoleManager, StoredPermissionManager logger = logging.getLogger(__name__) @@ -21,8 +22,6 @@ class StoredPermission(models.Model): objects = StoredPermissionManager() def __init__(self, *args, **kwargs): - from .classes import Permission - super(StoredPermission, self).__init__(*args, **kwargs) try: self.volatile_permission = Permission.get( diff --git a/mayan/apps/permissions/serializers.py b/mayan/apps/permissions/serializers.py index 8850b5db45..f311070999 100644 --- a/mayan/apps/permissions/serializers.py +++ b/mayan/apps/permissions/serializers.py @@ -1,7 +1,14 @@ from __future__ import unicode_literals -from rest_framework import serializers +from django.contrib.auth.models import Group +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers +from rest_framework.exceptions import ValidationError + +from user_management.serializers import GroupSerializer + +from .classes import Permission from .models import Role, StoredPermission @@ -21,7 +28,83 @@ class PermissionSerializer(serializers.Serializer): ) -class RoleSerializer(serializers.ModelSerializer): +class RoleSerializer(serializers.HyperlinkedModelSerializer): + groups = GroupSerializer(many=True) + class Meta: - fields = ('id', 'label') + fields = ('groups', 'id', 'label') model = Role + + +class WritableRoleSerializer(serializers.HyperlinkedModelSerializer): + groups_pk_list = serializers.CharField( + help_text=_( + 'Comma separated list of groups primary keys to add to, or replace' + ' in this role.' + ), required=False + ) + + permissions_pk_list = serializers.CharField( + help_text=_( + 'Comma separated list of permission primary keys to grant to this ' + 'role.' + ), required=False + ) + + class Meta: + fields = ('groups_pk_list', 'id', 'label', 'permissions_pk_list') + model = Role + + def create(self, validated_data): + result = validated_data.copy() + + self.groups_pk_list = validated_data.pop('groups_pk_list', '') + self.permissions_pk_list = validated_data.pop( + 'permissions_pk_list', '' + ) + + instance = super(WritableRoleSerializer, self).create(validated_data) + + if self.groups_pk_list: + self._add_groups(instance=instance) + + if self.permissions_pk_list: + self._add_permissions(instance=instance) + + return result + + def _add_groups(self, instance): + instance.groups.add( + *Group.objects.filter(pk__in=self.groups_pk_list.split(',')) + ) + + 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 update(self, instance, validated_data): + result = validated_data.copy() + + self.groups_pk_list = validated_data.pop('groups_pk_list', '') + self.permissions_pk_list = validated_data.pop( + 'permissions_pk_list', '' + ) + + result = super(WritableRoleSerializer, self).update( + instance, validated_data + ) + + if self.groups_pk_list: + instance.groups.clear() + self._add_groups(instance=instance) + + if self.permissions_pk_list: + instance.permissions.clear() + self._add_permissions(instance=instance) + + return result diff --git a/mayan/apps/permissions/tests/test_api.py b/mayan/apps/permissions/tests/test_api.py new file mode 100644 index 0000000000..c00399d072 --- /dev/null +++ b/mayan/apps/permissions/tests/test_api.py @@ -0,0 +1,165 @@ +from __future__ import unicode_literals + +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group +from django.core.urlresolvers import reverse + +from rest_framework.test import APITestCase + +from user_management.tests.literals import ( + TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME, TEST_GROUP +) + +from ..classes import Permission +from ..models import Role +from ..permissions import permission_role_view + +from .literals import TEST_ROLE_LABEL, TEST_ROLE_LABEL_EDITED + + +class PermissionAPITestCase(APITestCase): + def setUp(self): + super(PermissionAPITestCase, self).setUp() + self.admin_user = get_user_model().objects.create_superuser( + username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, + password=TEST_ADMIN_PASSWORD + ) + + self.client.login( + username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD + ) + + Permission.invalidate_cache() + + def test_permissions_list_view(self): + response = self.client.get(reverse('rest_api:permission-list')) + self.assertEqual(response.status_code, 200) + + def _create_role(self): + self.role = Role.objects.create(label=TEST_ROLE_LABEL) + + def test_roles_list_view(self): + self._create_role() + + response = self.client.get(reverse('rest_api:role-list')) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data['results'][0]['label'], TEST_ROLE_LABEL) + + def _role_create_request(self, extra_data=None): + data = { + 'label': TEST_ROLE_LABEL + } + + if extra_data: + data.update(extra_data) + + return self.client.post( + reverse('rest_api:role-list'), data=data + ) + + def test_role_create_view(self): + response = self._role_create_request() + self.assertEqual(response.status_code, 201) + self.assertEqual(Role.objects.count(), 1) + self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL) + + def _create_group(self): + self.group = Group.objects.create(name=TEST_GROUP) + + def test_role_create_complex_view(self): + self._create_group() + + response = self._role_create_request( + extra_data={ + 'groups_pk_list': '{}'.format(self.group.pk), + 'permissions_pk_list': '{}'.format(permission_role_view.pk) + } + ) + self.assertEqual(response.status_code, 201) + self.assertEqual(Role.objects.count(), 1) + self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL) + self.assertQuerysetEqual( + Role.objects.first().groups.all(), (repr(self.group),) + ) + self.assertQuerysetEqual( + Role.objects.first().permissions.all(), + (repr(permission_role_view.stored_permission),) + ) + + def _role_edit_request(self, extra_data=None, request_type='patch'): + data = { + 'label': TEST_ROLE_LABEL_EDITED + } + + if extra_data: + data.update(extra_data) + + return getattr(self.client, request_type)( + reverse('rest_api:role-detail', args=(self.role.pk,)), data=data + ) + + def test_role_edit_via_patch(self): + self._create_role() + response = self._role_edit_request() + self.assertEqual(response.status_code, 200) + self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL_EDITED) + + def test_role_edit_complex_via_patch(self): + Role.objects.all().delete() + Group.objects.all().delete() + + self._create_role() + self._create_group() + + response = self._role_edit_request( + extra_data={ + 'groups_pk_list': '{}'.format(self.group.pk), + 'permissions_pk_list': '{}'.format(permission_role_view.pk) + } + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL_EDITED) + self.assertQuerysetEqual( + Role.objects.first().groups.all(), (repr(self.group),) + ) + self.assertQuerysetEqual( + Role.objects.first().permissions.all(), + (repr(permission_role_view.stored_permission),) + ) + + def test_role_edit_via_put(self): + self._create_role() + response = self._role_edit_request(request_type='put') + self.assertEqual(response.status_code, 200) + self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL_EDITED) + + def test_role_edit_complex_via_put(self): + Role.objects.all().delete() + Group.objects.all().delete() + + self._create_role() + self._create_group() + + response = self._role_edit_request( + extra_data={ + 'groups_pk_list': '{}'.format(self.group.pk), + 'permissions_pk_list': '{}'.format(permission_role_view.pk) + }, request_type='put' + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL_EDITED) + self.assertQuerysetEqual( + Role.objects.first().groups.all(), (repr(self.group),) + ) + self.assertQuerysetEqual( + Role.objects.first().permissions.all(), + (repr(permission_role_view.stored_permission),) + ) + + def test_role_delete_view(self): + self._create_role() + response = self.client.delete( + reverse('rest_api:role-detail', args=(self.role.pk,)) + ) + self.assertEqual(response.status_code, 204) + self.assertEqual(Role.objects.count(), 0) diff --git a/mayan/apps/permissions/urls.py b/mayan/apps/permissions/urls.py index 17923c3d63..04670cf0a9 100644 --- a/mayan/apps/permissions/urls.py +++ b/mayan/apps/permissions/urls.py @@ -29,7 +29,7 @@ urlpatterns = patterns( api_urls = patterns( '', + url(r'^permissions/$', APIPermissionList.as_view(), name='permission-list'), url(r'^roles/$', APIRoleListView.as_view(), name='role-list'), url(r'^roles/(?P[0-9]+)/$', APIRoleView.as_view(), name='role-detail'), - url(r'^permissions/$', APIPermissionList.as_view(), name='permission-list'), )