ACL app updates
Update the ACL permission view to use the new AddRemoveView. Add ACL created and ACL edit events. Add permission adding and removal accesors to the ACL model. Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
@@ -2,12 +2,16 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from mayan.apps.common import (
|
from mayan.apps.common import MayanAppConfig, menu_object, menu_secondary
|
||||||
MayanAppConfig, menu_object, menu_secondary, menu_sidebar
|
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.events.permissions import permission_events_view
|
||||||
from mayan.apps.navigation import SourceColumn
|
from mayan.apps.navigation import SourceColumn
|
||||||
|
|
||||||
from .classes import ModelPermission
|
from .classes import ModelPermission
|
||||||
|
from .events import event_acl_created, event_acl_edited
|
||||||
from .links import link_acl_create, link_acl_delete, link_acl_permissions
|
from .links import link_acl_create, link_acl_delete, link_acl_permissions
|
||||||
|
|
||||||
|
|
||||||
@@ -21,25 +25,33 @@ class ACLsApp(MayanAppConfig):
|
|||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
super(ACLsApp, self).ready()
|
super(ACLsApp, self).ready()
|
||||||
|
from actstream import registry
|
||||||
|
|
||||||
AccessControlList = self.get_model(model_name='AccessControlList')
|
AccessControlList = self.get_model(model_name='AccessControlList')
|
||||||
|
|
||||||
|
ModelEventType.register(
|
||||||
|
event_types=(event_acl_created, event_acl_edited),
|
||||||
|
model=AccessControlList
|
||||||
|
)
|
||||||
ModelPermission.register_inheritance(
|
ModelPermission.register_inheritance(
|
||||||
model=AccessControlList, related='content_object',
|
model=AccessControlList, related='content_object',
|
||||||
)
|
)
|
||||||
|
|
||||||
SourceColumn(
|
SourceColumn(
|
||||||
attribute='role', is_identifier=True, is_sortable=True,
|
attribute='role', is_identifier=True, is_sortable=True,
|
||||||
source=AccessControlList
|
source=AccessControlList
|
||||||
)
|
)
|
||||||
SourceColumn(
|
|
||||||
attribute='get_permission_titles', include_label=True,
|
|
||||||
source=AccessControlList
|
|
||||||
)
|
|
||||||
|
|
||||||
menu_object.bind_links(
|
menu_object.bind_links(
|
||||||
links=(link_acl_permissions, link_acl_delete,),
|
links=(
|
||||||
|
link_acl_permissions, link_acl_delete,
|
||||||
|
link_events_for_object,
|
||||||
|
link_object_event_types_user_subcriptions_list
|
||||||
|
),
|
||||||
sources=(AccessControlList,)
|
sources=(AccessControlList,)
|
||||||
)
|
)
|
||||||
menu_sidebar.bind_links(
|
menu_secondary.bind_links(
|
||||||
links=(link_acl_create,), sources=('acls:acl_list',)
|
links=(link_acl_create,), sources=('acls:acl_list',)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
registry.register(AccessControlList)
|
||||||
|
|||||||
16
mayan/apps/acls/events.py
Normal file
16
mayan/apps/acls/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=_('Access control lists'), name='acls'
|
||||||
|
)
|
||||||
|
|
||||||
|
event_acl_created = namespace.add_event_type(
|
||||||
|
label=_('ACL created'), name='acl_created'
|
||||||
|
)
|
||||||
|
event_acl_edited = namespace.add_event_type(
|
||||||
|
label=_('ACL edited'), name='acl_edited'
|
||||||
|
)
|
||||||
@@ -151,7 +151,7 @@ class AccessControlListManager(models.Manager):
|
|||||||
else:
|
else:
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
def get_inherited_permissions(self, role, obj):
|
def get_inherited_permissions(self, obj, role):
|
||||||
try:
|
try:
|
||||||
instance = obj.first()
|
instance = obj.first()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@@ -177,11 +177,11 @@ class AccessControlListManager(models.Manager):
|
|||||||
parent_object = return_related(
|
parent_object = return_related(
|
||||||
instance=instance, related_field=parent_accessor
|
instance=instance, related_field=parent_accessor
|
||||||
)
|
)
|
||||||
content_type = ContentType.objects.get_for_model(parent_object)
|
content_type = ContentType.objects.get_for_model(model=parent_object)
|
||||||
try:
|
try:
|
||||||
return self.get(
|
return self.get(
|
||||||
role=role, content_type=content_type,
|
content_type=content_type, object_id=parent_object.pk,
|
||||||
object_id=parent_object.pk
|
role=role
|
||||||
).permissions.all()
|
).permissions.all()
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
return StoredPermission.objects.none()
|
return StoredPermission.objects.none()
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import logging
|
|||||||
|
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from mayan.apps.permissions.models import Role, StoredPermission
|
from mayan.apps.permissions.models import Role, StoredPermission
|
||||||
|
|
||||||
|
from .events import event_acl_created, event_acl_edited
|
||||||
from .managers import AccessControlListManager
|
from .managers import AccessControlListManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -58,11 +59,10 @@ class AccessControlList(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _(
|
return _(
|
||||||
'Permissions "%(permissions)s" to role "%(role)s" for "%(object)s"'
|
'Role "%(role)s" permission\'s for "%(object)s"'
|
||||||
) % {
|
) % {
|
||||||
'permissions': self.get_permission_titles(),
|
|
||||||
'object': self.content_object,
|
'object': self.content_object,
|
||||||
'role': self.role
|
'role': self.role,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
@@ -85,3 +85,32 @@ class AccessControlList(models.Model):
|
|||||||
|
|
||||||
return result or _('None')
|
return result or _('None')
|
||||||
get_permission_titles.short_description = _('Permissions')
|
get_permission_titles.short_description = _('Permissions')
|
||||||
|
|
||||||
|
def permissions_add(self, queryset, _user=None):
|
||||||
|
with transaction.atomic():
|
||||||
|
event_acl_edited.commit(
|
||||||
|
actor=_user, target=self
|
||||||
|
)
|
||||||
|
self.permissions.add(*queryset)
|
||||||
|
|
||||||
|
def permissions_remove(self, queryset, _user=None):
|
||||||
|
with transaction.atomic():
|
||||||
|
event_acl_edited.commit(
|
||||||
|
actor=_user, target=self
|
||||||
|
)
|
||||||
|
self.permissions.remove(*queryset)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
_user = kwargs.pop('_user', None)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
is_new = not self.pk
|
||||||
|
super(AccessControlList, self).save(*args, **kwargs)
|
||||||
|
if is_new:
|
||||||
|
event_acl_created.commit(
|
||||||
|
actor=_user, target=self
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
event_acl_edited.commit(
|
||||||
|
actor=_user, target=self
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
import itertools
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
@@ -13,11 +11,10 @@ from mayan.apps.common.mixins import (
|
|||||||
ContentTypeViewMixin, ExternalObjectMixin
|
ContentTypeViewMixin, ExternalObjectMixin
|
||||||
)
|
)
|
||||||
from mayan.apps.common.generics import (
|
from mayan.apps.common.generics import (
|
||||||
AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
|
AddRemoveView, SingleObjectCreateView, SingleObjectDeleteView,
|
||||||
SingleObjectListView
|
SingleObjectListView
|
||||||
)
|
)
|
||||||
from mayan.apps.permissions import Permission, PermissionNamespace
|
from mayan.apps.permissions.models import Role
|
||||||
from mayan.apps.permissions.models import Role, StoredPermission
|
|
||||||
|
|
||||||
from .classes import ModelPermission
|
from .classes import ModelPermission
|
||||||
from .forms import ACLCreateForm
|
from .forms import ACLCreateForm
|
||||||
@@ -60,7 +57,8 @@ class ACLCreateView(ContentTypeViewMixin, ExternalObjectMixin, SingleObjectCreat
|
|||||||
'queryset': Role.objects.exclude(
|
'queryset': Role.objects.exclude(
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_instance_extra_data(self):
|
def get_instance_extra_data(self):
|
||||||
@@ -140,109 +138,84 @@ class ACLListView(ContentTypeViewMixin, ExternalObjectMixin, SingleObjectListVie
|
|||||||
return self.get_external_object().acls.all()
|
return self.get_external_object().acls.all()
|
||||||
|
|
||||||
|
|
||||||
class ACLPermissionsView(AssignRemoveView):
|
class ACLPermissionsView(AddRemoveView):
|
||||||
grouped = True
|
action_add_method = 'permissions_add'
|
||||||
left_list_title = _('Available permissions')
|
action_remove_method = 'permissions_remove'
|
||||||
right_list_title = _('Granted permissions')
|
main_object_model = AccessControlList
|
||||||
|
main_object_permission = permission_acl_edit
|
||||||
|
main_object_pk_url_kwarg = 'acl_id'
|
||||||
|
list_added_title = _('Granted permissions')
|
||||||
|
list_available_title = _('Available permissions')
|
||||||
|
related_field = 'permissions'
|
||||||
|
|
||||||
@staticmethod
|
def generate_choices(self, queryset):
|
||||||
def generate_choices(entries):
|
namespaces_dictionary = {}
|
||||||
results = []
|
|
||||||
|
|
||||||
entries = sorted(
|
# Sort permissions by their translatable label
|
||||||
entries, key=lambda x: (
|
object_list = sorted(
|
||||||
x.volatile_permission.namespace.label,
|
queryset, key=lambda permission: permission.volatile_permission.label
|
||||||
x.volatile_permission.label
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for namespace, permissions in itertools.groupby(entries, lambda entry: entry.namespace):
|
# Group permissions by namespace
|
||||||
permission_options = [
|
for permission in object_list:
|
||||||
(force_text(permission.pk), permission) for permission in permissions
|
namespaces_dictionary.setdefault(
|
||||||
]
|
permission.volatile_permission.namespace.label,
|
||||||
results.append(
|
[]
|
||||||
(PermissionNamespace.get(name=namespace), permission_options)
|
)
|
||||||
|
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):
|
def get_actions_extra_kwargs(self):
|
||||||
permission = get_object_or_404(klass=StoredPermission, pk=item)
|
return {'_user': self.request.user}
|
||||||
self.get_object().permissions.add(permission)
|
|
||||||
|
|
||||||
def get_available_list(self):
|
|
||||||
return ModelPermission.get_for_instance(
|
|
||||||
instance=self.get_object().content_object
|
|
||||||
).exclude(id__in=self.get_granted_list().values_list('pk', flat=True))
|
|
||||||
|
|
||||||
def get_disabled_choices(self):
|
def get_disabled_choices(self):
|
||||||
"""
|
"""
|
||||||
Get permissions from a parent's acls but remove the permissions we
|
Get permissions from a parent's ACLs. We return a list since that is
|
||||||
already hold for this object
|
what the form widget's can process.
|
||||||
"""
|
"""
|
||||||
return map(
|
return self.main_object.get_inherited_permissions().values_list(
|
||||||
str, set(
|
'pk', flat=True
|
||||||
self.get_object().get_inherited_permissions().values_list(
|
|
||||||
'pk', flat=True
|
|
||||||
)
|
|
||||||
).difference(
|
|
||||||
self.get_object().permissions.values_list('pk', flat=True)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_extra_context(self):
|
def get_extra_context(self):
|
||||||
acl = self.get_object()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'acl': acl,
|
'acl': self.main_object,
|
||||||
'object': acl.content_object,
|
'object': self.main_object.content_object,
|
||||||
'navigation_object_list': ('object', 'acl'),
|
'navigation_object_list': ('object', 'acl'),
|
||||||
'title': _('Role "%(role)s" permission\'s for "%(object)s"') % {
|
'title': _('Role "%(role)s" permission\'s for "%(object)s".') % {
|
||||||
'role': acl.role,
|
'role': self.main_object.role,
|
||||||
'object': acl.content_object,
|
'object': self.main_object.content_object,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_granted_list(self):
|
def get_list_added_help_text(self):
|
||||||
"""
|
if self.main_object.get_inherited_permissions():
|
||||||
Merge of permissions we hold for this object and the permissions we
|
|
||||||
hold for this object's parent via another ACL.
|
|
||||||
"""
|
|
||||||
merged_pks = self.get_object().permissions.values_list(
|
|
||||||
'pk', flat=True
|
|
||||||
) | self.get_object().get_inherited_permissions().values_list(
|
|
||||||
'pk', flat=True
|
|
||||||
)
|
|
||||||
return StoredPermission.objects.filter(pk__in=merged_pks)
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
return get_object_or_404(
|
|
||||||
klass=self.get_queryset(), pk=self.kwargs['acl_id']
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return AccessControlList.objects.restrict_queryset(
|
|
||||||
permission=permission_acl_edit,
|
|
||||||
queryset=AccessControlList.objects.all(), user=self.request.user
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_right_list_help_text(self):
|
|
||||||
if self.get_object().get_inherited_permissions():
|
|
||||||
return _(
|
return _(
|
||||||
'Disabled permissions are inherited from a parent object and '
|
'Disabled permissions are inherited from a parent object and '
|
||||||
'can\'t be removed from this view, they need to be removed '
|
'can\'t be removed from this view, they need to be removed '
|
||||||
'from the parent object\'s ACL view.'
|
'from the parent object\'s ACL view.'
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.right_list_help_text
|
def get_list_added_queryset(self):
|
||||||
|
"""
|
||||||
|
Merge of permissions we hold for this object and the permissions we
|
||||||
|
hold for this object's parents via another ACL. .distinct() is added
|
||||||
|
in case the permission was added to the ACL and then added to a
|
||||||
|
parent ACL's and thus inherited and would appear twice. If
|
||||||
|
order to remove the double permission from the ACL it would need to be
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
queryset = super(ACLPermissionsView, self).get_list_added_queryset()
|
||||||
|
return (
|
||||||
|
queryset | self.main_object.get_inherited_permissions()
|
||||||
|
).distinct()
|
||||||
|
|
||||||
def left_list(self):
|
def get_secondary_object_source_queryset(self):
|
||||||
Permission.refresh()
|
return ModelPermission.get_for_instance(
|
||||||
return ACLPermissionsView.generate_choices(self.get_available_list())
|
instance=self.main_object.content_object
|
||||||
|
)
|
||||||
def remove(self, item):
|
|
||||||
permission = get_object_or_404(klass=StoredPermission, pk=item)
|
|
||||||
self.get_object().permissions.remove(permission)
|
|
||||||
|
|
||||||
def right_list(self):
|
|
||||||
return ACLPermissionsView.generate_choices(self.get_granted_list())
|
|
||||||
|
|||||||
Reference in New Issue
Block a user