Refactor the permissions app
Use the new AddRemove View for the Role's group and permissions views as well as the Group's role views. Convert the API to use viewsets. Add more tests. Add role created and edited events. Add event subscription support to roles. Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
@@ -1,77 +1,180 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rest_framework import generics
|
||||
from rest_framework import status, viewsets
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import action
|
||||
|
||||
from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter
|
||||
from mayan.apps.rest_api.permissions import MayanPermission
|
||||
from mayan.apps.rest_api.viewsets import MayanAPIModelViewSet
|
||||
from mayan.apps.user_management.permissions import permission_group_view
|
||||
from mayan.apps.user_management.serializers import GroupSerializer
|
||||
|
||||
from .classes import Permission
|
||||
from .classes import PermissionNamespace
|
||||
from .models import Role
|
||||
from .permissions import (
|
||||
permission_role_create, permission_role_delete, permission_role_edit,
|
||||
permission_role_view
|
||||
)
|
||||
from .serializers import (
|
||||
PermissionSerializer, RoleSerializer, WritableRoleSerializer
|
||||
PermissionNamespaceSerializer, PermissionSerializer, RoleGroupAddRemoveSerializer,
|
||||
RolePermissionAddRemoveSerializer, RoleSerializer
|
||||
)
|
||||
|
||||
|
||||
class APIPermissionList(generics.ListAPIView):
|
||||
"""
|
||||
get: Returns a list of all the available permissions.
|
||||
"""
|
||||
class PermissionNamespaceViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
lookup_field = 'name'
|
||||
lookup_url_kwarg = 'permission_namespace_name'
|
||||
serializer_class = PermissionNamespaceSerializer
|
||||
|
||||
def get_object(self):
|
||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
|
||||
return PermissionNamespace.get(**filter_kwargs)
|
||||
|
||||
@action(
|
||||
detail=True, serializer_class=PermissionSerializer,
|
||||
url_name='permission-list', url_path='permissions'
|
||||
)
|
||||
def permission_list(self, request, *args, **kwargs):
|
||||
queryset = self.get_object().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)
|
||||
|
||||
def get_queryset(self):
|
||||
return PermissionNamespace.all()
|
||||
|
||||
|
||||
class PermissionViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
lookup_field = 'pk'
|
||||
lookup_url_kwarg = 'permission_name'
|
||||
lookup_value_regex = r'[\w\.]+'
|
||||
serializer_class = PermissionSerializer
|
||||
queryset = Permission.all()
|
||||
|
||||
def get_object(self):
|
||||
namespace = PermissionNamespace.get(name=self.kwargs['permission_namespace_name'])
|
||||
permissions = namespace.get_permissions()
|
||||
return permissions.get(self.kwargs['permission_name'])
|
||||
|
||||
|
||||
class APIRoleListView(generics.ListCreateAPIView):
|
||||
"""
|
||||
get: Returns a list of all the roles.
|
||||
post: Create a new role.
|
||||
"""
|
||||
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_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
return None
|
||||
|
||||
return super(APIRoleListView, self).get_serializer(*args, **kwargs)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == 'GET':
|
||||
return RoleSerializer
|
||||
elif self.request.method == 'POST':
|
||||
return WritableRoleSerializer
|
||||
|
||||
|
||||
class APIRoleView(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
delete: Delete the selected role.
|
||||
get: Return the details of the selected role.
|
||||
patch: Edit the selected role.
|
||||
put: Edit the selected role.
|
||||
"""
|
||||
mayan_object_permissions = {
|
||||
'GET': (permission_role_view,),
|
||||
'PUT': (permission_role_edit,),
|
||||
'PATCH': (permission_role_edit,),
|
||||
'DELETE': (permission_role_delete,)
|
||||
class RoleAPIViewSet(MayanAPIModelViewSet):
|
||||
lookup_url_kwarg = 'role_id'
|
||||
object_permission_map = {
|
||||
'destroy': permission_role_delete,
|
||||
'group_add': permission_role_edit,
|
||||
'group_list': permission_role_view,
|
||||
'group_remove': permission_role_edit,
|
||||
'list': permission_role_view,
|
||||
'partial_update': permission_role_edit,
|
||||
'retrieve': permission_role_view,
|
||||
'update': permission_role_edit,
|
||||
}
|
||||
permission_classes = (MayanPermission,)
|
||||
queryset = Role.objects.all()
|
||||
serializer_class = RoleSerializer
|
||||
view_permission_map = {
|
||||
'create': permission_role_create
|
||||
}
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
if not self.request:
|
||||
return None
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='role_id', methods=('post',),
|
||||
serializer_class=RoleGroupAddRemoveSerializer,
|
||||
url_name='group-add', url_path='groups/add'
|
||||
)
|
||||
def group_add(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.groups_add(instance=instance)
|
||||
headers = self.get_success_headers(data=serializer.data)
|
||||
return Response(
|
||||
serializer.data, status=status.HTTP_200_OK, headers=headers
|
||||
)
|
||||
|
||||
return super(APIRoleView, self).get_serializer(*args, **kwargs)
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='role_id',
|
||||
serializer_class=GroupSerializer, url_name='group-list',
|
||||
url_path='groups'
|
||||
)
|
||||
def group_list(self, request, *args, **kwargs):
|
||||
queryset = self.get_object().get_groups(
|
||||
permission=permission_group_view, user=self.request.user
|
||||
)
|
||||
page = self.paginate_queryset(queryset)
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method == 'GET':
|
||||
return RoleSerializer
|
||||
elif self.request.method in ('PATCH', 'PUT'):
|
||||
return WritableRoleSerializer
|
||||
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='role_id',
|
||||
methods=('post',), serializer_class=RoleGroupAddRemoveSerializer,
|
||||
url_name='group-remove', url_path='groups/remove'
|
||||
)
|
||||
def group_remove(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.groups_remove(instance=instance)
|
||||
headers = self.get_success_headers(data=serializer.data)
|
||||
return Response(
|
||||
serializer.data, status=status.HTTP_200_OK, headers=headers
|
||||
)
|
||||
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='role_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, status=status.HTTP_200_OK, headers=headers
|
||||
)
|
||||
|
||||
@action(
|
||||
detail=True, lookup_url_kwarg='role_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='role_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, status=status.HTTP_200_OK, headers=headers
|
||||
)
|
||||
|
||||
@@ -13,14 +13,20 @@ from mayan.apps.common import (
|
||||
menu_secondary, menu_setup
|
||||
)
|
||||
from mayan.apps.common.signals import perform_upgrade
|
||||
from mayan.apps.events import ModelEventType
|
||||
from mayan.apps.events.links import (
|
||||
link_events_for_object, link_object_event_types_user_subcriptions_list
|
||||
)
|
||||
from mayan.apps.navigation import SourceColumn
|
||||
|
||||
from .events import event_role_created, event_role_edited
|
||||
from .handlers import handler_purge_permissions
|
||||
from .links import (
|
||||
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 .methods import method_group_roles_add, method_group_roles_remove
|
||||
from .permissions import (
|
||||
permission_role_delete, permission_role_edit, permission_role_view
|
||||
)
|
||||
@@ -37,10 +43,18 @@ class PermissionsApp(MayanAppConfig):
|
||||
|
||||
def ready(self):
|
||||
super(PermissionsApp, self).ready()
|
||||
from actstream import registry
|
||||
|
||||
Role = self.get_model('Role')
|
||||
Group = apps.get_model(app_label='auth', model_name='Group')
|
||||
|
||||
Group.add_to_class(name='roles_add', value=method_group_roles_add)
|
||||
Group.add_to_class(name='roles_remove', value=method_group_roles_remove)
|
||||
|
||||
ModelEventType.register(
|
||||
event_types=(event_role_created, event_role_edited), model=Role
|
||||
)
|
||||
|
||||
ModelPermission.register(
|
||||
model=Role, permissions=(
|
||||
permission_acl_edit, permission_acl_view,
|
||||
@@ -53,7 +67,9 @@ class PermissionsApp(MayanAppConfig):
|
||||
|
||||
menu_list_facet.bind_links(
|
||||
links=(
|
||||
link_acl_list, link_role_groups, link_role_permissions,
|
||||
link_acl_list, link_events_for_object,
|
||||
link_object_event_types_user_subcriptions_list,
|
||||
link_role_groups, link_role_permissions,
|
||||
), sources=(Role,)
|
||||
)
|
||||
menu_list_facet.bind_links(
|
||||
@@ -78,3 +94,5 @@ class PermissionsApp(MayanAppConfig):
|
||||
dispatch_uid='permissions_handler_purge_permissions',
|
||||
receiver=handler_purge_permissions
|
||||
)
|
||||
|
||||
registry.register(Role)
|
||||
|
||||
@@ -2,15 +2,12 @@ from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.common.warnings import InterfaceWarning
|
||||
|
||||
from .exceptions import InvalidNamespace
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -49,6 +46,13 @@ class PermissionNamespace(object):
|
||||
self.permissions.append(permission)
|
||||
return permission
|
||||
|
||||
def get_permissions(self):
|
||||
result = {}
|
||||
for permission in self.permissions:
|
||||
result[permission.pk] = permission
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Permission(object):
|
||||
|
||||
16
mayan/apps/permissions/events.py
Normal file
16
mayan/apps/permissions/events.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.events import EventTypeNamespace
|
||||
|
||||
namespace = EventTypeNamespace(
|
||||
label=_('Permissions'), name='permissions'
|
||||
)
|
||||
|
||||
event_role_created = namespace.add_event_type(
|
||||
label=_('Role created'), name='role_created'
|
||||
)
|
||||
event_role_edited = namespace.add_event_type(
|
||||
label=_('Role edited'), name='role_edited'
|
||||
)
|
||||
43
mayan/apps/permissions/methods.py
Normal file
43
mayan/apps/permissions/methods.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import transaction
|
||||
|
||||
from mayan.apps.user_management.events import event_group_edited
|
||||
|
||||
from .events import event_role_edited
|
||||
|
||||
|
||||
def method_group_get_roles(self, permission, _user):
|
||||
AccessControlList = apps.get_model(
|
||||
app_label='acls', model_name='AccessControlList'
|
||||
)
|
||||
|
||||
return AccessControlList.objects.restrict_queryset(
|
||||
permission=permission, queryset=self.roles.all(),
|
||||
user=_user
|
||||
)
|
||||
|
||||
|
||||
def method_group_roles_add(self, queryset, _user):
|
||||
with transaction.atomic():
|
||||
event_group_edited.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
for role in queryset:
|
||||
self.roles.add(role)
|
||||
event_role_edited.commit(
|
||||
actor=_user, action_object=self, target=role
|
||||
)
|
||||
|
||||
|
||||
def method_group_roles_remove(self, queryset, _user):
|
||||
with transaction.atomic():
|
||||
event_group_edited.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
for role in queryset:
|
||||
self.roles.remove(role)
|
||||
event_role_edited.commit(
|
||||
actor=_user, action_object=self, target=role
|
||||
)
|
||||
@@ -2,13 +2,17 @@ from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth.models import Group
|
||||
from django.db import models
|
||||
from django.db import models, transaction
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.user_management.events import event_group_edited
|
||||
|
||||
from .classes import Permission
|
||||
from .events import event_role_created, event_role_edited
|
||||
from .managers import RoleManager, StoredPermissionManager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -118,9 +122,70 @@ class Role(models.Model):
|
||||
def grant(self, permission):
|
||||
self.permissions.add(permission.stored_permission)
|
||||
|
||||
def get_groups(self, permission, user):
|
||||
AccessControlList = apps.get_model(
|
||||
app_label='acls', model_name='AccessControlList'
|
||||
)
|
||||
|
||||
return AccessControlList.objects.restrict_queryset(
|
||||
permission=permission, queryset=self.groups.all(),
|
||||
user=user
|
||||
)
|
||||
|
||||
def groups_add(self, queryset, _user=None):
|
||||
with transaction.atomic():
|
||||
event_role_edited.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
for obj in queryset:
|
||||
self.groups.add(obj)
|
||||
event_group_edited.commit(
|
||||
actor=_user, action_object=self, target=obj
|
||||
)
|
||||
|
||||
def groups_remove(self, queryset, _user=None):
|
||||
with transaction.atomic():
|
||||
event_role_edited.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
for obj in queryset:
|
||||
self.groups.remove(obj)
|
||||
event_group_edited.commit(
|
||||
actor=_user, action_object=self, target=obj
|
||||
)
|
||||
|
||||
def natural_key(self):
|
||||
return (self.label,)
|
||||
natural_key.dependencies = ['auth.Group', 'permissions.StoredPermission']
|
||||
|
||||
def permissions_add(self, queryset, _user=None):
|
||||
with transaction.atomic():
|
||||
event_role_edited.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
self.permissions.add(*queryset)
|
||||
|
||||
def permissions_remove(self, queryset, _user=None):
|
||||
with transaction.atomic():
|
||||
event_role_edited.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
self.permissions.remove(*queryset)
|
||||
|
||||
def revoke(self, permission):
|
||||
self.permissions.remove(permission.stored_permission)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
_user = kwargs.pop('_user', None)
|
||||
|
||||
with transaction.atomic():
|
||||
is_new = not self.pk
|
||||
super(Role, self).save(*args, **kwargs)
|
||||
if is_new:
|
||||
event_role_created.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
else:
|
||||
event_role_edited.commit(
|
||||
actor=_user, target=self
|
||||
)
|
||||
|
||||
@@ -4,18 +4,45 @@ 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 mayan.apps.user_management.serializers import GroupSerializer
|
||||
from mayan.apps.rest_api.mixins import ExternalObjectListSerializerMixin
|
||||
from mayan.apps.rest_api.relations import MultiKwargHyperlinkedIdentityField
|
||||
from mayan.apps.user_management.permissions import permission_group_edit
|
||||
|
||||
from .classes import Permission
|
||||
from .models import Role, StoredPermission
|
||||
|
||||
|
||||
class PermissionNamespaceSerializer(serializers.Serializer):
|
||||
name = serializers.CharField(read_only=True)
|
||||
label = serializers.CharField(read_only=True)
|
||||
permissions_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_field='name',
|
||||
lookup_url_kwarg='permission_namespace_name',
|
||||
view_name='rest_api:permission_namespace-permission-list'
|
||||
)
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
lookup_field='name',
|
||||
lookup_url_kwarg='permission_namespace_name',
|
||||
view_name='rest_api:permission_namespace-detail'
|
||||
)
|
||||
|
||||
|
||||
class PermissionSerializer(serializers.Serializer):
|
||||
namespace = serializers.CharField(read_only=True)
|
||||
pk = serializers.CharField(read_only=True)
|
||||
label = serializers.CharField(read_only=True)
|
||||
url = MultiKwargHyperlinkedIdentityField(
|
||||
view_kwargs=(
|
||||
{
|
||||
'lookup_field': 'namespace.name', 'lookup_url_kwarg': 'permission_namespace_name',
|
||||
},
|
||||
{
|
||||
'lookup_field': 'pk', 'lookup_url_kwarg': 'permission_name',
|
||||
}
|
||||
),
|
||||
view_name='rest_api:permission-detail'
|
||||
)
|
||||
|
||||
def to_representation(self, instance):
|
||||
if isinstance(instance, StoredPermission):
|
||||
@@ -28,85 +55,85 @@ class PermissionSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
|
||||
class RoleGroupAddRemoveSerializer(ExternalObjectListSerializerMixin, serializers.Serializer):
|
||||
group_id_list = serializers.CharField(
|
||||
help_text=_(
|
||||
'Comma separated list of group primary keys that will be added or '
|
||||
'removed.'
|
||||
), required=False, write_only=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
external_object_list_model = Group
|
||||
external_object_list_permission = permission_group_edit
|
||||
external_object_list_pk_list_field = 'group_id_list'
|
||||
|
||||
def groups_add(self, instance):
|
||||
instance.groups_add(
|
||||
queryset=self.get_external_object_list(),
|
||||
_user=self.context['request'].user
|
||||
)
|
||||
|
||||
def groups_remove(self, instance):
|
||||
instance.groups_remove(
|
||||
queryset=self.get_external_object_list(),
|
||||
_user=self.context['request'].user
|
||||
)
|
||||
|
||||
|
||||
class RolePermissionAddRemoveSerializer(ExternalObjectListSerializerMixin, serializers.Serializer):
|
||||
permission_id_list = serializers.CharField(
|
||||
help_text=_(
|
||||
'Comma separated list of permission primary keys that will be added or '
|
||||
'removed.'
|
||||
), required=False, write_only=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
external_object_list_model = Permission
|
||||
external_object_list_pk_list_field = 'permission_id_list'
|
||||
|
||||
def permissions_add(self, instance):
|
||||
instance.permissions.add(
|
||||
*self.get_external_object_list()
|
||||
)
|
||||
|
||||
def permissions_remove(self, instance):
|
||||
instance.permissions.remove(
|
||||
*self.get_external_object_list()
|
||||
)
|
||||
|
||||
|
||||
class RoleSerializer(serializers.HyperlinkedModelSerializer):
|
||||
groups = GroupSerializer(many=True, read_only=True)
|
||||
permissions = PermissionSerializer(many=True, read_only=True)
|
||||
group_add_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_url_kwarg='role_id', view_name='rest_api:role-group-add'
|
||||
)
|
||||
group_list_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_url_kwarg='role_id', view_name='rest_api:role-group-list'
|
||||
)
|
||||
group_remove_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_url_kwarg='role_id', view_name='rest_api:role-group-remove'
|
||||
)
|
||||
permission_add_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_url_kwarg='role_id', view_name='rest_api:role-permission-add'
|
||||
)
|
||||
permission_list_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_url_kwarg='role_id', view_name='rest_api:role-permission-list'
|
||||
)
|
||||
permission_remove_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_url_kwarg='role_id', view_name='rest_api:role-permission-remove'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
extra_kwargs = {
|
||||
'url': {'view_name': 'rest_api:role-detail'},
|
||||
'url': {
|
||||
'lookup_url_kwarg': 'role_id',
|
||||
'view_name': 'rest_api:role-detail'
|
||||
}
|
||||
}
|
||||
fields = ('groups', 'id', 'label', 'permissions', 'url')
|
||||
fields = (
|
||||
'id', 'label', 'url', 'group_add_url', 'group_list_url',
|
||||
'group_remove_url', 'permission_add_url', 'permission_list_url',
|
||||
'permission_remove_url'
|
||||
)
|
||||
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):
|
||||
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 instance
|
||||
|
||||
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(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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
TEST_CASE_ROLE_LABEL = 'test case role'
|
||||
TEST_CASE_ROLE_LABEL = 'test case role label'
|
||||
TEST_INVALID_PERMISSION_NAMESPACE_NAME = 'invalid namespace'
|
||||
TEST_INVALID_PERMISSION_NAME = 'invalid name'
|
||||
TEST_PERMISSION_NAMESPACE_LABEL = 'test namespace label'
|
||||
TEST_PERMISSION_NAMESPACE_NAME = 'test namespace'
|
||||
TEST_PERMISSION_LABEL = 'test name label'
|
||||
TEST_PERMISSION_NAME = 'test name'
|
||||
TEST_ROLE_LABEL = 'test role 2'
|
||||
TEST_PERMISSION_NAMESPACE_LABEL = 'test permission namespace label'
|
||||
TEST_PERMISSION_NAMESPACE_NAME = 'test_permission_namespace_name'
|
||||
TEST_PERMISSION_LABEL = 'test permission name label'
|
||||
TEST_PERMISSION_NAME = '{}.{}'.format(TEST_PERMISSION_NAMESPACE_NAME, 'test_permission_name')
|
||||
TEST_ROLE_LABEL = 'test role label'
|
||||
TEST_ROLE_LABEL_EDITED = 'test role label edited'
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..classes import PermissionNamespace
|
||||
from ..models import Role
|
||||
|
||||
from .literals import TEST_CASE_ROLE_LABEL, TEST_ROLE_LABEL
|
||||
from .literals import (
|
||||
TEST_CASE_ROLE_LABEL, TEST_PERMISSION_LABEL, TEST_PERMISSION_NAME,
|
||||
TEST_PERMISSION_NAMESPACE_LABEL, TEST_PERMISSION_NAMESPACE_NAME,
|
||||
TEST_ROLE_LABEL
|
||||
)
|
||||
|
||||
|
||||
class PermissionTestMixin(object):
|
||||
def _create_test_permission(self):
|
||||
self.test_permission_namespace = PermissionNamespace(
|
||||
label=TEST_PERMISSION_NAMESPACE_LABEL,
|
||||
name=TEST_PERMISSION_NAMESPACE_NAME
|
||||
)
|
||||
self.test_permission = self.test_permission_namespace.add_permission(
|
||||
label=TEST_PERMISSION_LABEL,
|
||||
name=TEST_PERMISSION_NAME
|
||||
)
|
||||
|
||||
|
||||
class RoleTestCaseMixin(object):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,9 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .api_views import APIPermissionList, APIRoleListView, APIRoleView
|
||||
from .api_views import (
|
||||
PermissionNamespaceViewSet, PermissionViewSet, RoleAPIViewSet
|
||||
)
|
||||
from .views import (
|
||||
GroupRolesView, RoleCreateView, RoleDeleteView, RoleEditView,
|
||||
RoleGroupsView, RoleListView, RolePermissionsView
|
||||
@@ -36,14 +38,14 @@ urlpatterns = [
|
||||
url(regex=r'^roles/list/$', name='role_list', view=RoleListView.as_view()),
|
||||
]
|
||||
|
||||
api_urls = [
|
||||
url(
|
||||
regex=r'^permissions/$', name='permission-list',
|
||||
view=APIPermissionList.as_view(),
|
||||
),
|
||||
url(regex=r'^roles/$', name='role-list', view=APIRoleListView.as_view()),
|
||||
url(
|
||||
regex=r'^roles/(?P<role_id>[0-9]+)/$', name='role-detail',
|
||||
view=APIRoleView.as_view()
|
||||
),
|
||||
]
|
||||
api_router_entries = (
|
||||
{
|
||||
'prefix': r'permission_namespaces', 'viewset': PermissionNamespaceViewSet,
|
||||
'basename': 'permission_namespace'
|
||||
},
|
||||
{
|
||||
'prefix': r'permission_namespaces/(?P<permission_namespace_name>[^/.]+)/permissions',
|
||||
'viewset': PermissionViewSet, 'basename': 'permission'
|
||||
},
|
||||
{'prefix': r'roles', 'viewset': RoleAPIViewSet, 'basename': 'role'},
|
||||
)
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mayan.apps.acls.models import AccessControlList
|
||||
from mayan.apps.common.generics import (
|
||||
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
|
||||
AddRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
|
||||
SingleObjectEditView, SingleObjectListView
|
||||
)
|
||||
from mayan.apps.user_management.permissions import permission_group_edit
|
||||
|
||||
from .classes import Permission, PermissionNamespace
|
||||
from .icons import icon_role_list
|
||||
from .links import link_role_create
|
||||
from .models import Role, StoredPermission
|
||||
@@ -26,45 +21,33 @@ from .permissions import (
|
||||
)
|
||||
|
||||
|
||||
class GroupRolesView(AssignRemoveView):
|
||||
grouped = False
|
||||
left_list_title = _('Available roles')
|
||||
right_list_title = _('Group roles')
|
||||
object_permission = permission_group_edit
|
||||
class GroupRolesView(AddRemoveView):
|
||||
action_add_method = 'roles_add'
|
||||
action_remove_method = 'roles_remove'
|
||||
main_object_model = Group
|
||||
main_object_permission = permission_group_edit
|
||||
main_object_pk_url_kwarg = 'group_id'
|
||||
secondary_object_model = Role
|
||||
secondary_object_permission = permission_role_edit
|
||||
list_available_title = _('Available roles')
|
||||
list_added_title = _('Group roles')
|
||||
related_field = 'roles'
|
||||
|
||||
def add(self, item):
|
||||
role = get_object_or_404(klass=Role, pk=item)
|
||||
self.get_object().roles.add(role)
|
||||
def get_actions_extra_kwargs(self):
|
||||
return {'_user': self.request.user}
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'object': self.get_object(),
|
||||
'title': _('Roles of group: %s') % self.get_object()
|
||||
'object': self.main_object,
|
||||
'title': _('Roles of group: %s') % self.main_object,
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(klass=Group, pk=self.kwargs['group_id'])
|
||||
|
||||
def left_list(self):
|
||||
return [
|
||||
(force_text(role.pk), role.label) for role in set(Role.objects.all()) - set(self.get_object().roles.all())
|
||||
]
|
||||
|
||||
def right_list(self):
|
||||
return [
|
||||
(force_text(role.pk), role.label) for role in self.get_object().roles.all()
|
||||
]
|
||||
|
||||
def remove(self, item):
|
||||
role = get_object_or_404(klass=Role, pk=item)
|
||||
self.get_object().roles.remove(role)
|
||||
|
||||
|
||||
class RoleCreateView(SingleObjectCreateView):
|
||||
fields = ('label',)
|
||||
model = Role
|
||||
view_permission = permission_role_create
|
||||
post_action_redirect = reverse_lazy(viewname='permissions:role_list')
|
||||
view_permission = permission_role_create
|
||||
|
||||
|
||||
class RoleDeleteView(SingleObjectDeleteView):
|
||||
@@ -81,43 +64,31 @@ class RoleEditView(SingleObjectEditView):
|
||||
pk_url_kwarg = 'role_id'
|
||||
|
||||
|
||||
class RoleGroupsView(AssignRemoveView):
|
||||
grouped = False
|
||||
left_list_title = _('Available groups')
|
||||
right_list_title = _('Role groups')
|
||||
object_permission = permission_role_edit
|
||||
class RoleGroupsView(AddRemoveView):
|
||||
action_add_method = 'groups_add'
|
||||
action_remove_method = 'groups_remove'
|
||||
main_object_model = Role
|
||||
main_object_permission = permission_role_edit
|
||||
main_object_pk_url_kwarg = 'role_id'
|
||||
secondary_object_model = Group
|
||||
secondary_object_permission = permission_group_edit
|
||||
list_available_title = _('Available groups')
|
||||
list_added_title = _('Role groups')
|
||||
related_field = 'groups'
|
||||
|
||||
def add(self, item):
|
||||
group = get_object_or_404(klass=Group, pk=item)
|
||||
self.get_object().groups.add(group)
|
||||
def get_actions_extra_kwargs(self):
|
||||
return {'_user': self.request.user}
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'object': self.get_object(),
|
||||
'title': _('Groups of role: %s') % self.get_object(),
|
||||
'object': self.main_object,
|
||||
'title': _('Groups of role: %s') % self.main_object,
|
||||
'subtitle': _(
|
||||
'Add groups to be part of a role. They will '
|
||||
'inherit the role\'s permissions and access controls.'
|
||||
),
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(klass=Role, pk=self.kwargs['role_id'])
|
||||
|
||||
def left_list(self):
|
||||
return [
|
||||
(force_text(group.pk), group.name) for group in set(Group.objects.all()) - set(self.get_object().groups.all())
|
||||
]
|
||||
|
||||
def remove(self, item):
|
||||
group = get_object_or_404(klass=Group, pk=item)
|
||||
self.get_object().groups.remove(group)
|
||||
|
||||
def right_list(self):
|
||||
return [
|
||||
(force_text(group.pk), group.name) for group in self.get_object().groups.all()
|
||||
]
|
||||
|
||||
|
||||
class RoleListView(SingleObjectListView):
|
||||
model = Role
|
||||
@@ -143,64 +114,48 @@ class RoleListView(SingleObjectListView):
|
||||
}
|
||||
|
||||
|
||||
class RolePermissionsView(AssignRemoveView):
|
||||
class RolePermissionsView(AddRemoveView):
|
||||
action_add_method = 'permissions_add'
|
||||
action_remove_method = 'permissions_remove'
|
||||
grouped = True
|
||||
left_list_title = _('Available permissions')
|
||||
object_permission = permission_role_edit
|
||||
right_list_title = _('Granted permissions')
|
||||
main_object_model = Role
|
||||
main_object_permission = permission_role_edit
|
||||
main_object_pk_url_kwarg = 'role_id'
|
||||
list_available_title = _('Available permissions')
|
||||
list_added_title = _('Granted permissions')
|
||||
related_field = 'permissions'
|
||||
secondary_object_model = StoredPermission
|
||||
|
||||
@staticmethod
|
||||
def generate_choices(entries):
|
||||
results = []
|
||||
def generate_choices(self, queryset):
|
||||
namespaces_dictionary = {}
|
||||
|
||||
entries = sorted(
|
||||
entries, key=lambda x: (
|
||||
x.volatile_permission.namespace.label,
|
||||
x.volatile_permission.label
|
||||
)
|
||||
# Sort permissions by their translatable label
|
||||
object_list = sorted(
|
||||
queryset, key=lambda permission: permission.volatile_permission.label
|
||||
)
|
||||
|
||||
for namespace, permissions in itertools.groupby(entries, lambda entry: entry.namespace):
|
||||
permission_options = [
|
||||
(force_text(permission.pk), permission) for permission in permissions
|
||||
]
|
||||
results.append(
|
||||
(PermissionNamespace.get(name=namespace), permission_options)
|
||||
# Group permissions by namespace
|
||||
for permission in object_list:
|
||||
namespaces_dictionary.setdefault(
|
||||
permission.volatile_permission.namespace.label,
|
||||
[]
|
||||
)
|
||||
namespaces_dictionary[permission.volatile_permission.namespace.label].append(
|
||||
(permission.pk, force_text(permission))
|
||||
)
|
||||
|
||||
return results
|
||||
# Sort permissions by their translatable namespace label
|
||||
return sorted(namespaces_dictionary.items())
|
||||
|
||||
def add(self, item):
|
||||
permission = get_object_or_404(klass=StoredPermission, pk=item)
|
||||
self.get_object().permissions.add(permission)
|
||||
def get_actions_extra_kwargs(self):
|
||||
return {'_user': self.request.user}
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'object': self.get_object(),
|
||||
'object': self.main_object,
|
||||
'subtitle': _(
|
||||
'Permissions granted here will apply to the entire system '
|
||||
'and all objects.'
|
||||
),
|
||||
'title': _('Permissions for role: %s') % self.get_object(),
|
||||
'title': _('Permissions for role: %s') % self.main_object,
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
return get_object_or_404(klass=Role, pk=self.kwargs['role_id'])
|
||||
|
||||
def left_list(self):
|
||||
Permission.refresh()
|
||||
|
||||
return RolePermissionsView.generate_choices(
|
||||
entries=StoredPermission.objects.exclude(
|
||||
id__in=self.get_object().permissions.values_list('pk', flat=True)
|
||||
)
|
||||
)
|
||||
|
||||
def remove(self, item):
|
||||
permission = get_object_or_404(klass=StoredPermission, pk=item)
|
||||
self.get_object().permissions.remove(permission)
|
||||
|
||||
def right_list(self):
|
||||
return RolePermissionsView.generate_choices(
|
||||
entries=self.get_object().permissions.all()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user