From cf99201b89238e43e83a1b8df812d60f390ee5f9 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Mon, 2 Apr 2018 02:36:20 -0400 Subject: [PATCH] Add support for Role ACLs. Signed-off-by: Roberto Rosario --- HISTORY.rst | 1 + mayan/apps/permissions/apps.py | 16 +- mayan/apps/permissions/links.py | 8 +- mayan/apps/permissions/tests/test_views.py | 176 ++++++++++++++++----- mayan/apps/permissions/urls.py | 8 +- mayan/apps/permissions/views.py | 12 +- 6 files changed, 160 insertions(+), 61 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 34a867cea0..12ae34e55a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -103,6 +103,7 @@ - Mark the feature to detect and fix the orientatin of PDF as experimental. - Don't show documents with 0 duplicates in the duplicated document list. - Clean up the duplicated document model after a document is deleted. +- Add support Role ACLs. 2.7.3 (2017-09-11) ================== diff --git a/mayan/apps/permissions/apps.py b/mayan/apps/permissions/apps.py index 5c1319d42d..ae3e611eff 100644 --- a/mayan/apps/permissions/apps.py +++ b/mayan/apps/permissions/apps.py @@ -4,6 +4,9 @@ from django.apps import apps from django.utils.translation import ugettext_lazy as _ from acls import ModelPermission +from acls.links import link_acl_list +from acls.permissions import permission_acl_edit, permission_acl_view + from common import ( MayanAppConfig, menu_multi_item, menu_object, menu_secondary, menu_setup ) @@ -11,9 +14,9 @@ from common.signals import perform_upgrade from .handlers import purge_permissions from .links import ( - link_group_members, link_permission_grant, link_permission_revoke, - link_role_create, link_role_delete, link_role_edit, link_role_list, - link_role_members, link_role_permissions + link_group_roles, link_permission_grant, link_permission_revoke, + link_role_create, link_role_delete, link_role_edit, link_role_groups, + link_role_list, link_role_permissions ) from .permissions import ( permission_role_delete, permission_role_edit, permission_role_view @@ -35,18 +38,19 @@ class PermissionsApp(MayanAppConfig): ModelPermission.register( model=Role, permissions=( + permission_acl_edit, permission_acl_view, permission_role_delete, permission_role_edit, permission_role_view ) ) menu_object.bind_links( - links=(link_group_members,), position=98, sources=(Group,) + links=(link_group_roles,), position=98, sources=(Group,) ) menu_object.bind_links( links=( - link_role_edit, link_role_members, link_role_permissions, - link_role_delete + link_role_edit, link_role_groups, link_role_permissions, + link_acl_list, link_role_delete ), sources=(Role,) ) menu_multi_item.bind_links( diff --git a/mayan/apps/permissions/links.py b/mayan/apps/permissions/links.py index a167108069..8375508fbb 100644 --- a/mayan/apps/permissions/links.py +++ b/mayan/apps/permissions/links.py @@ -11,9 +11,9 @@ from .permissions import ( permission_role_view ) -link_group_members = Link( +link_group_roles = Link( permissions=(permission_group_edit,), text=_('Roles'), - view='permissions:group_members', args='object.id' + view='permissions:group_roles', args='object.id' ) link_permission_grant = Link( permissions=(permission_permission_grant,), text=_('Grant'), @@ -39,9 +39,9 @@ link_role_list = Link( icon='fa fa-user-secret', permissions=(permission_role_view,), text=_('Roles'), view='permissions:role_list' ) -link_role_members = Link( +link_role_groups = Link( permissions=(permission_role_edit,), text=_('Groups'), - view='permissions:role_members', args='object.id' + view='permissions:role_groups', args='object.id' ) link_role_permissions = Link( permissions=(permission_permission_grant, permission_permission_revoke), diff --git a/mayan/apps/permissions/tests/test_views.py b/mayan/apps/permissions/tests/test_views.py index f9b824fff7..019af9f7f8 100644 --- a/mayan/apps/permissions/tests/test_views.py +++ b/mayan/apps/permissions/tests/test_views.py @@ -1,64 +1,158 @@ from __future__ import unicode_literals -from django.test.client import Client -from django.urls import reverse +from django.contrib.auth.models import Group -from common.tests import BaseTestCase -from user_management.tests import TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME +from common.tests import GenericViewTestCase +from user_management.permissions import permission_group_edit +from user_management.tests.literals import TEST_GROUP_2_NAME from ..models import Role +from ..permissions import ( + permission_role_create, permission_role_delete, permission_role_edit, + permission_role_view +) -from .literals import TEST_ROLE_LABEL, TEST_ROLE_LABEL_EDITED +from .literals import TEST_ROLE_2_LABEL, TEST_ROLE_LABEL_EDITED -class PermissionsViewsTestCase(BaseTestCase): +class PermissionsViewsTestCase(GenericViewTestCase): def setUp(self): super(PermissionsViewsTestCase, self).setUp() - self.client = Client() - # Login the admin user - logged_in = self.client.login( - username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD - ) - self.assertTrue(logged_in) - self.assertTrue(self.admin_user.is_authenticated) + self.login_user() - def test_role_creation_view(self): - self.role.delete() - - response = self.client.post( - reverse( - 'permissions:role_create', - ), data={ - 'label': TEST_ROLE_LABEL, - }, follow=True + def _request_create_role_view(self): + return self.post( + viewname='permissions:role_create', data={ + 'label': TEST_ROLE_2_LABEL, + } ) - self.assertContains(response, 'created', status_code=200) - + def test_role_creation_view_no_permission(self): + response = self._request_create_role_view() + self.assertEqual(response.status_code, 403) self.assertEqual(Role.objects.count(), 1) - self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL) + self.assertFalse(TEST_ROLE_2_LABEL in Role.objects.values_list('label', flat=True)) - def test_role_delete_view(self): - response = self.client.post( - reverse( - 'permissions:role_delete', args=(self.role.pk,), - ), follow=True + def test_role_creation_view_with_permission(self): + self.grant_permission(permission=permission_role_create) + response = self._request_create_role_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(Role.objects.count(), 2) + self.assertTrue(TEST_ROLE_2_LABEL in Role.objects.values_list('label', flat=True)) + + def _request_role_delete_view(self): + return self.post( + viewname='permissions:role_delete', args=(self.role_2.pk,), ) - self.assertContains(response, 'deleted', status_code=200) + def _create_role(self): + self.role_2 = Role.objects.create(label=TEST_ROLE_2_LABEL) - self.assertEqual(Role.objects.count(), 0) + def test_role_delete_view_no_access(self): + self._create_role() + response = self._request_role_delete_view() + self.assertEqual(response.status_code, 403) + self.assertEqual(Role.objects.count(), 2) + self.assertTrue(TEST_ROLE_2_LABEL in Role.objects.values_list('label', flat=True)) - def test_role_edit_view(self): - response = self.client.post( - reverse( - 'permissions:role_edit', args=(self.role.pk,), - ), data={ + def test_role_delete_view_with_access(self): + self._create_role() + self.grant_access(permission=permission_role_delete, obj=self.role_2) + response = self._request_role_delete_view() + self.assertEqual(response.status_code, 302) + self.assertEqual(Role.objects.count(), 1) + self.assertFalse(TEST_ROLE_2_LABEL in Role.objects.values_list('label', flat=True)) + + def _request_role_edit_view(self): + return self.post( + viewname='permissions:role_edit', args=(self.role_2.pk,), data={ 'label': TEST_ROLE_LABEL_EDITED, - }, follow=True + } ) - self.assertContains(response, 'update', status_code=200) + def test_role_edit_view_no_access(self): + self._create_role() + response = self._request_role_edit_view() - self.assertEqual(Role.objects.count(), 1) - self.assertEqual(Role.objects.first().label, TEST_ROLE_LABEL_EDITED) + self.assertEqual(response.status_code, 403) + + self.role_2.refresh_from_db() + self.assertEqual(Role.objects.count(), 2) + self.assertEqual(self.role_2.label, TEST_ROLE_2_LABEL) + + def test_role_edit_view_with_access(self): + self._create_role() + self.grant_access(permission=permission_role_edit, obj=self.role_2) + response = self._request_role_edit_view() + + self.assertEqual(response.status_code, 302) + self.role_2.refresh_from_db() + + self.assertEqual(Role.objects.count(), 2) + self.assertEqual(self.role_2.label, TEST_ROLE_LABEL_EDITED) + + def _request_role_list_view(self): + return self.get(viewname='permissions:role_list') + + def test_role_list_view_no_access(self): + self._create_role() + response = self._request_role_list_view() + self.assertEqual(response.status_code, 200) + self.assertNotContains(response, text=TEST_ROLE_2_LABEL, status_code=200) + + def test_role_list_view_with_access(self): + self._create_role() + self.grant_access(permission=permission_role_view, obj=self.role_2) + response = self._request_role_list_view() + self.assertContains(response, text=TEST_ROLE_2_LABEL, status_code=200) + + def _request_role_permissions_view(self): + return self.get( + viewname='permissions:role_permissions', args=(self.role_2.pk,) + ) + + def test_role_permissions_view_no_access(self): + self._create_role() + response = self._request_role_permissions_view() + self.assertEqual(response.status_code, 403) + + def test_role_permissions_view_with_access(self): + self._create_role() + self.grant_access(permission=permission_role_edit, obj=self.role_2) + response = self._request_role_permissions_view() + self.assertEqual(response.status_code, 200) + + def _request_role_groups_view(self): + return self.get( + viewname='permissions:role_groups', args=(self.role_2.pk,) + ) + + def test_role_groups_view_no_access(self): + self._create_role() + response = self._request_role_groups_view() + self.assertEqual(response.status_code, 403) + + def test_role_groups_view_with_access(self): + self._create_role() + self.grant_access(permission=permission_role_edit, obj=self.role_2) + response = self._request_role_groups_view() + self.assertEqual(response.status_code, 200) + + def _create_group(self): + self.group_2 = Group.objects.create(name=TEST_GROUP_2_NAME) + + def _request_group_roles_view(self): + return self.get( + viewname='permissions:group_roles', args=(self.group_2.pk,) + ) + + def test_group_roles_view_no_access(self): + self._create_group() + response = self._request_group_roles_view() + self.assertEqual(response.status_code, 403) + + def test_group_roles_view_with_access(self): + self._create_group() + self.grant_access(permission=permission_group_edit, obj=self.group_2) + response = self._request_group_roles_view() + self.assertEqual(response.status_code, 200) diff --git a/mayan/apps/permissions/urls.py b/mayan/apps/permissions/urls.py index a7698f0236..3c620c2059 100644 --- a/mayan/apps/permissions/urls.py +++ b/mayan/apps/permissions/urls.py @@ -10,8 +10,8 @@ from .views import ( urlpatterns = [ url( - r'^group/(?P\d+)/members/$', GroupRoleMembersView.as_view(), - name='group_members' + r'^group/(?P\d+)/roles/$', GroupRoleMembersView.as_view(), + name='group_roles' ), url(r'^role/list/$', RoleListView.as_view(), name='role_list'), url(r'^role/create/$', RoleCreateView.as_view(), name='role_create'), @@ -25,8 +25,8 @@ urlpatterns = [ name='role_delete' ), url( - r'^role/(?P\d+)/members/$', SetupRoleMembersView.as_view(), - name='role_members' + r'^role/(?P\d+)/groups/$', SetupRoleMembersView.as_view(), + name='role_groups' ), ] diff --git a/mayan/apps/permissions/views.py b/mayan/apps/permissions/views.py index e066e2a4dc..ad70d28630 100644 --- a/mayan/apps/permissions/views.py +++ b/mayan/apps/permissions/views.py @@ -27,7 +27,7 @@ class GroupRoleMembersView(AssignRemoveView): grouped = False left_list_title = _('Available roles') right_list_title = _('Group roles') - view_permission = permission_group_edit + object_permission = permission_group_edit def add(self, item): role = get_object_or_404(Role, pk=item) @@ -66,21 +66,21 @@ class RoleCreateView(SingleObjectCreateView): class RoleDeleteView(SingleObjectDeleteView): model = Role - view_permission = permission_role_delete + object_permission = permission_role_delete post_action_redirect = reverse_lazy('permissions:role_list') class RoleEditView(SingleObjectEditView): fields = ('label',) model = Role - view_permission = permission_role_edit + object_permission = permission_role_edit class SetupRoleMembersView(AssignRemoveView): grouped = False left_list_title = _('Available groups') right_list_title = _('Role groups') - view_permission = permission_role_edit + object_permission = permission_role_edit def add(self, item): group = get_object_or_404(Group, pk=item) @@ -114,7 +114,7 @@ class SetupRolePermissionsView(AssignRemoveView): grouped = True left_list_title = _('Available permissions') right_list_title = _('Granted permissions') - view_permission = permission_role_view + object_permission = permission_role_view def add(self, item): Permission.check_permissions( @@ -173,4 +173,4 @@ class RoleListView(SingleObjectListView): } model = Role - view_permission = permission_role_view + object_permission = permission_role_view