Add support for users ACLs. Add support for groups ACLs.
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
@@ -103,7 +103,11 @@
|
||||
- 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.
|
||||
- Add support for roles ACLs.
|
||||
- Add support for users ACLs.
|
||||
- Add support for groups ACLs.
|
||||
- Sort permission namespaces and permissions in the role permission views.
|
||||
|
||||
|
||||
2.7.3 (2017-09-11)
|
||||
==================
|
||||
|
||||
@@ -5,6 +5,8 @@ from django.contrib.auth import get_user_model
|
||||
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 menu_multi_item, menu_object, menu_secondary, menu_setup
|
||||
from common.apps import MayanAppConfig
|
||||
from common.widgets import two_state_template
|
||||
@@ -70,12 +72,14 @@ class UserManagementApp(MayanAppConfig):
|
||||
)
|
||||
ModelPermission.register(
|
||||
model=Group, permissions=(
|
||||
permission_acl_edit, permission_acl_view,
|
||||
permission_group_delete, permission_group_edit,
|
||||
permission_group_view,
|
||||
)
|
||||
)
|
||||
ModelPermission.register(
|
||||
model=User, permissions=(
|
||||
permission_acl_edit, permission_acl_view,
|
||||
permission_user_delete, permission_user_edit,
|
||||
permission_user_view
|
||||
)
|
||||
@@ -112,12 +116,12 @@ class UserManagementApp(MayanAppConfig):
|
||||
sources=(Group,)
|
||||
)
|
||||
menu_object.bind_links(
|
||||
links=(link_group_delete,), position=99, sources=(Group,)
|
||||
links=(link_acl_list, link_group_delete,), position=99, sources=(Group,)
|
||||
)
|
||||
menu_object.bind_links(
|
||||
links=(
|
||||
link_user_edit, link_user_set_password, link_user_groups,
|
||||
link_user_delete
|
||||
link_acl_list, link_user_delete
|
||||
), sources=(User,)
|
||||
)
|
||||
menu_secondary.bind_links(
|
||||
|
||||
@@ -14,10 +14,14 @@ from metadata.tests.literals import (
|
||||
)
|
||||
|
||||
from ..permissions import (
|
||||
permission_user_delete, permission_user_edit, permission_user_view
|
||||
permission_user_create, permission_user_delete, permission_user_edit,
|
||||
permission_user_view
|
||||
)
|
||||
|
||||
from .literals import TEST_USER_PASSWORD_EDITED, TEST_USER_USERNAME
|
||||
from .literals import (
|
||||
TEST_USER_PASSWORD_EDITED, TEST_USER_USERNAME, TEST_USER_2_USERNAME,
|
||||
TEST_USER_2_USERNAME_EDITED
|
||||
)
|
||||
|
||||
TEST_USER_TO_DELETE_USERNAME = 'user_to_delete'
|
||||
|
||||
@@ -27,157 +31,157 @@ class UserManagementViewTestCase(GenericViewTestCase):
|
||||
super(UserManagementViewTestCase, self).setUp()
|
||||
self.login_user()
|
||||
|
||||
def _request_user_create_view(self):
|
||||
return self.post(
|
||||
viewname='user_management:user_add', data={
|
||||
'username': TEST_USER_2_USERNAME
|
||||
}
|
||||
)
|
||||
|
||||
def test_user_create_view_no_permission(self):
|
||||
response = self._request_user_create_view()
|
||||
self.assertEqual(response.status_code, 403)
|
||||
self.assertEqual(get_user_model().objects.count(), 2)
|
||||
self.assertFalse(TEST_USER_2_USERNAME in get_user_model().objects.values_list('username', flat=True))
|
||||
|
||||
def test_user_create_view_with_permission(self):
|
||||
self.grant_permission(permission=permission_user_create)
|
||||
response = self._request_user_create_view()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(get_user_model().objects.count(), 3)
|
||||
self.assertTrue(TEST_USER_2_USERNAME in get_user_model().objects.values_list('username', flat=True))
|
||||
|
||||
def _request_set_password(self, password):
|
||||
return self.post(
|
||||
'user_management:user_set_password', args=(self.user.pk,), data={
|
||||
viewname='user_management:user_set_password', args=(self.user_2.pk,),
|
||||
data={
|
||||
'new_password1': password, 'new_password2': password
|
||||
}
|
||||
)
|
||||
|
||||
def _create_test_user_2(self):
|
||||
self.user_2 = get_user_model().objects.create(
|
||||
username=TEST_USER_2_USERNAME
|
||||
)
|
||||
|
||||
def test_user_set_password_view_no_access(self):
|
||||
self._create_test_user_2()
|
||||
response = self._request_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
self.logout()
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
self.login(
|
||||
username=TEST_USER_2_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
response = self.get('common:current_user_details')
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_user_set_password_view_with_access(self):
|
||||
self._create_test_user_2()
|
||||
self.grant_access(permission=permission_user_edit, obj=self.user_2)
|
||||
|
||||
response = self._request_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
self.logout()
|
||||
self.login(
|
||||
username=TEST_USER_2_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
response = self.get('common:current_user_details')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def _request_multiple_user_set_password(self, password):
|
||||
return self.post(
|
||||
'user_management:user_multiple_set_password', data={
|
||||
'id_list': self.user.pk,
|
||||
'id_list': self.user_2.pk,
|
||||
'new_password1': password,
|
||||
'new_password2': password
|
||||
}, follow=True
|
||||
)
|
||||
|
||||
def test_user_set_password_view_no_permissions(self):
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
|
||||
response = self._request_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
self.logout()
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
self.login(
|
||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
response = self.get('common:current_user_details')
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_user_set_password_view_with_permissions(self):
|
||||
self.grant_permission(permission=permission_user_edit)
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
|
||||
response = self._request_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
self.logout()
|
||||
self.login(
|
||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
response = self.get('common:current_user_details')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_user_multiple_set_password_view_no_permissions(self):
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
|
||||
response = self._request_multiple_user_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
self.logout()
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
self.login(
|
||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
response = self.get('common:current_user_details')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_user_multiple_set_password_view_with_permissions(self):
|
||||
self.grant_permission(permission=permission_user_edit)
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
|
||||
response = self._request_multiple_user_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.logout()
|
||||
self.login(
|
||||
username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
response = self.get('common:current_user_details')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_user_delete_view_no_permissions(self):
|
||||
user = get_user_model().objects.create(
|
||||
username=TEST_USER_TO_DELETE_USERNAME
|
||||
)
|
||||
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
|
||||
response = self.post(
|
||||
'user_management:user_delete', args=(user.pk,)
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
self.assertEqual(get_user_model().objects.count(), 3)
|
||||
|
||||
def test_user_delete_view_with_permissions(self):
|
||||
user = get_user_model().objects.create(
|
||||
username=TEST_USER_TO_DELETE_USERNAME
|
||||
)
|
||||
|
||||
self.grant_permission(permission=permission_user_delete)
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
|
||||
response = self.post(
|
||||
'user_management:user_delete', args=(user.pk,), follow=True
|
||||
)
|
||||
|
||||
self.assertContains(response, text='deleted', status_code=200)
|
||||
self.assertEqual(get_user_model().objects.count(), 2)
|
||||
|
||||
def test_user_multiple_delete_view_no_permissions(self):
|
||||
user = get_user_model().objects.create(
|
||||
username=TEST_USER_TO_DELETE_USERNAME
|
||||
)
|
||||
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
|
||||
response = self.post(
|
||||
'user_management:user_multiple_delete', data={
|
||||
'id_list': user.pk
|
||||
}
|
||||
)
|
||||
|
||||
def test_user_multiple_set_password_view_no_access(self):
|
||||
self._create_test_user_2()
|
||||
response = self._request_multiple_user_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
self.logout()
|
||||
|
||||
with self.assertRaises(AssertionError):
|
||||
self.login(
|
||||
username=TEST_USER_2_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
response = self.get('common:current_user_details')
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_user_multiple_set_password_view_with_access(self):
|
||||
self._create_test_user_2()
|
||||
self.grant_access(permission=permission_user_edit, obj=self.user_2)
|
||||
|
||||
response = self._request_multiple_user_set_password(
|
||||
password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
self.logout()
|
||||
self.login(
|
||||
username=TEST_USER_2_USERNAME, password=TEST_USER_PASSWORD_EDITED
|
||||
)
|
||||
response = self.get('common:current_user_details')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def _request_user_delete(self):
|
||||
return self.post(
|
||||
viewname='user_management:user_delete', args=(self.user_2.pk,)
|
||||
)
|
||||
|
||||
def test_user_delete_view_no_access(self):
|
||||
self._create_test_user_2()
|
||||
response = self._request_user_delete()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(get_user_model().objects.count(), 3)
|
||||
|
||||
def test_user_multiple_delete_view_with_permissions(self):
|
||||
user = get_user_model().objects.create(
|
||||
username=TEST_USER_TO_DELETE_USERNAME
|
||||
def test_user_delete_view_with_access(self):
|
||||
self._create_test_user_2()
|
||||
self.grant_access(permission=permission_user_delete, obj=self.user_2)
|
||||
response = self._request_user_delete()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(get_user_model().objects.count(), 2)
|
||||
|
||||
def _request_user_multiple_delete_view(self):
|
||||
return self.post(
|
||||
viewname='user_management:user_multiple_delete', data={
|
||||
'id_list': self.user_2.pk
|
||||
}
|
||||
)
|
||||
|
||||
self.grant_permission(permission=permission_user_delete)
|
||||
self.grant_permission(permission=permission_user_view)
|
||||
def test_user_multiple_delete_view_no_access(self):
|
||||
self._create_test_user_2()
|
||||
response = self._request_user_multiple_delete_view()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(get_user_model().objects.count(), 3)
|
||||
|
||||
response = self.post(
|
||||
'user_management:user_multiple_delete', data={
|
||||
'id_list': user.pk,
|
||||
}, follow=True
|
||||
)
|
||||
|
||||
self.assertContains(response, text='deleted', status_code=200)
|
||||
def test_user_multiple_delete_view_with_access(self):
|
||||
self._create_test_user_2()
|
||||
self.grant_access(permission=permission_user_delete, obj=self.user_2)
|
||||
response = self._request_user_multiple_delete_view()
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(get_user_model().objects.count(), 2)
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.forms import SetPasswordForm
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse, reverse_lazy
|
||||
@@ -35,8 +36,8 @@ class GroupCreateView(SingleObjectCreateView):
|
||||
class GroupEditView(SingleObjectEditView):
|
||||
fields = ('name',)
|
||||
model = Group
|
||||
object_permission = permission_group_edit
|
||||
post_action_redirect = reverse_lazy('user_management:group_list')
|
||||
view_permission = permission_group_edit
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -51,13 +52,13 @@ class GroupListView(SingleObjectListView):
|
||||
'title': _('Groups'),
|
||||
}
|
||||
model = Group
|
||||
view_permission = permission_group_view
|
||||
object_permission = permission_group_view
|
||||
|
||||
|
||||
class GroupDeleteView(SingleObjectDeleteView):
|
||||
model = Group
|
||||
object_permission = permission_group_delete
|
||||
post_action_redirect = reverse_lazy('user_management:group_list')
|
||||
view_permission = permission_group_delete
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -70,7 +71,7 @@ class GroupMembersView(AssignRemoveView):
|
||||
decode_content_type = True
|
||||
left_list_title = _('Available users')
|
||||
right_list_title = _('Users in group')
|
||||
view_permission = permission_group_edit
|
||||
object_permission = permission_group_edit
|
||||
|
||||
@staticmethod
|
||||
def generate_choices(choices):
|
||||
@@ -133,11 +134,11 @@ class UserCreateView(SingleObjectCreateView):
|
||||
|
||||
class UserDeleteView(MultipleObjectConfirmActionView):
|
||||
model = get_user_model()
|
||||
object_permission = permission_user_delete
|
||||
success_message = _('User delete request performed on %(count)d user')
|
||||
success_message_plural = _(
|
||||
'User delete request performed on %(count)d users'
|
||||
)
|
||||
view_permission = permission_user_delete
|
||||
|
||||
def get_extra_context(self):
|
||||
queryset = self.get_queryset()
|
||||
@@ -187,11 +188,11 @@ class UserDeleteView(MultipleObjectConfirmActionView):
|
||||
|
||||
class UserEditView(SingleObjectEditView):
|
||||
fields = ('username', 'first_name', 'last_name', 'email', 'is_active',)
|
||||
object_permission = permission_user_edit
|
||||
post_action_redirect = reverse_lazy('user_management:user_list')
|
||||
queryset = get_user_model().objects.filter(
|
||||
is_superuser=False, is_staff=False
|
||||
)
|
||||
view_permission = permission_user_edit
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -204,7 +205,7 @@ class UserGroupsView(AssignRemoveView):
|
||||
decode_content_type = True
|
||||
left_list_title = _('Available groups')
|
||||
right_list_title = _('Groups joined')
|
||||
view_permission = permission_user_edit
|
||||
object_permission = permission_user_edit
|
||||
|
||||
def add(self, item):
|
||||
item.user_set.add(self.get_object())
|
||||
@@ -233,7 +234,7 @@ class UserGroupsView(AssignRemoveView):
|
||||
|
||||
|
||||
class UserListView(SingleObjectListView):
|
||||
view_permission = permission_user_view
|
||||
object_permission = permission_user_view
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -250,11 +251,11 @@ class UserListView(SingleObjectListView):
|
||||
class UserSetPasswordView(MultipleObjectFormActionView):
|
||||
form_class = SetPasswordForm
|
||||
model = get_user_model()
|
||||
object_permission = permission_user_edit
|
||||
success_message = _('Password change request performed on %(count)d user')
|
||||
success_message_plural = _(
|
||||
'Password change request performed on %(count)d users'
|
||||
)
|
||||
view_permission = permission_user_edit
|
||||
|
||||
def get_extra_context(self):
|
||||
queryset = self.get_queryset()
|
||||
@@ -283,8 +284,9 @@ class UserSetPasswordView(MultipleObjectFormActionView):
|
||||
result = {}
|
||||
if queryset:
|
||||
result['user'] = queryset.first()
|
||||
|
||||
return result
|
||||
return result
|
||||
else:
|
||||
raise PermissionDenied
|
||||
|
||||
def object_action(self, form, instance):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user