Backport ACL computation improvements

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2019-05-04 03:27:30 -04:00
parent d271f1503d
commit 8e731d6280
61 changed files with 701 additions and 609 deletions

View File

@@ -212,6 +212,16 @@
* Add new app to handle all dependencies. * Add new app to handle all dependencies.
* Remove the licenses.py module and replace * Remove the licenses.py module and replace
it with a dependencies.py module. it with a dependencies.py module.
* Backport ACL computation improvements.
* Remove model permission proxy models.
* Remove related access control argument. This is
now handled by the related field registration.
* Allow nested access control checking.
* check_access's permissions argument must now be
an interable.
* Remove permissions_related from links.
* Remove mayan_permission_attribute_check from
API permission.
3.1.11 (2019-04-XX) 3.1.11 (2019-04-XX)
=================== ===================

View File

@@ -245,6 +245,16 @@ Other changes
* Add new app to handle all dependencies. * Add new app to handle all dependencies.
* Remove the licenses.py module and replace * Remove the licenses.py module and replace
it with a dependencies.py module. it with a dependencies.py module.
* Backport ACL computation improvements.
* Remove model permission proxy models.
* Remove related access control argument. This is
now handled by the related field registration.
* Allow nested access control checking.
* check_access's permissions argument must now be
an interable.
* Remove permissions_related from links.
* Remove mayan_permission_attribute_check from
API permission.
Removals Removals
-------- --------

View File

@@ -35,8 +35,8 @@ class APIObjectACLListView(generics.ListCreateAPIView):
permission_required = permission_acl_edit permission_required = permission_acl_edit
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=content_object, permissions=(permission_required,),
obj=content_object user=self.request.user
) )
return content_object return content_object
@@ -94,8 +94,8 @@ class APIObjectACLView(generics.RetrieveDestroyAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=content_object, permissions=(permission_required,),
obj=content_object user=self.request.user
) )
return content_object return content_object
@@ -130,7 +130,8 @@ class APIObjectACLPermissionListView(generics.ListCreateAPIView):
permission = permission_acl_edit permission = permission_acl_edit
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=content_object, permissions=permission, user=self.request.user obj=content_object, permissions=(permission,),
user=self.request.user
) )
return content_object return content_object
@@ -191,7 +192,8 @@ class APIObjectACLPermissionView(generics.RetrieveDestroyAPIView):
permission = permission_acl_edit permission = permission_acl_edit
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=content_object, permissions=permission, user=self.request.user obj=content_object, permissions=(permission,),
user=self.request.user
) )
return content_object return content_object

View File

@@ -10,6 +10,7 @@ from mayan.apps.events.links import (
) )
from mayan.apps.navigation.classes import SourceColumn from mayan.apps.navigation.classes import SourceColumn
from .classes import ModelPermission
from .events import event_acl_created, event_acl_edited 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
@@ -33,6 +34,10 @@ class ACLsApp(MayanAppConfig):
model=AccessControlList model=AccessControlList
) )
ModelPermission.register_inheritance(
model=AccessControlList, related='content_object',
)
SourceColumn( SourceColumn(
attribute='role', is_sortable=True, source=AccessControlList, attribute='role', is_sortable=True, source=AccessControlList,
) )

View File

@@ -8,8 +8,8 @@ logger = logging.getLogger(__name__)
class ModelPermission(object): class ModelPermission(object):
_functions = {}
_inheritances = {} _inheritances = {}
_proxies = {}
_registry = {} _registry = {}
@classmethod @classmethod
@@ -63,24 +63,23 @@ class ModelPermission(object):
if class_permissions: if class_permissions:
permissions.extend(class_permissions) permissions.extend(class_permissions)
proxy = cls._proxies.get(type(instance))
if proxy:
permissions.extend(cls._registry.get(proxy))
pks = [ pks = [
permission.stored_permission.pk for permission in set(permissions) permission.stored_permission.pk for permission in set(permissions)
] ]
return StoredPermission.objects.filter(pk__in=pks) return StoredPermission.objects.filter(pk__in=pks)
@classmethod @classmethod
def register_proxy(cls, source, model): def get_function(cls, model):
cls._proxies[model] = source return cls._functions[model]
@classmethod
def register_inheritance(cls, model, related):
cls._inheritances[model] = related
@classmethod @classmethod
def get_inheritance(cls, model): def get_inheritance(cls, model):
return cls._inheritances[model] return cls._inheritances[model]
@classmethod
def register_function(cls, model, function):
cls._functions[model] = function
@classmethod
def register_inheritance(cls, model, related):
cls._inheritances[model] = related

View File

@@ -29,23 +29,22 @@ def get_kwargs_factory(variable_name):
return get_kwargs return get_kwargs
link_acl_delete = Link(
args='resolved_object.pk', icon_class=icon_acl_delete,
permissions=(permission_acl_edit,), permissions_related='content_object',
tags='dangerous', text=_('Delete'), view='acls:acl_delete',
)
link_acl_list = Link(
icon_class=icon_acl_list, kwargs=get_kwargs_factory('resolved_object'),
permissions=(permission_acl_view,), text=_('ACLs'), view='acls:acl_list'
)
link_acl_create = Link( link_acl_create = Link(
icon_class=icon_acl_new, kwargs=get_kwargs_factory('resolved_object'), icon_class=icon_acl_new, kwargs=get_kwargs_factory('resolved_object'),
permissions=(permission_acl_edit,), text=_('New ACL'), permissions=(permission_acl_edit,), text=_('New ACL'),
view='acls:acl_create' view='acls:acl_create'
) )
link_acl_delete = Link(
args='resolved_object.pk', icon_class=icon_acl_delete,
permissions=(permission_acl_edit,), tags='dangerous', text=_('Delete'),
view='acls:acl_delete'
)
link_acl_list = Link(
icon_class=icon_acl_list, kwargs=get_kwargs_factory('resolved_object'),
permissions=(permission_acl_view,), text=_('ACLs'), view='acls:acl_list'
)
link_acl_permissions = Link( link_acl_permissions = Link(
args='resolved_object.pk', icon_class=icon_acl_permissions, args='resolved_object.pk', icon_class=icon_acl_permissions,
permissions=(permission_acl_edit,), permissions=(permission_acl_edit,),
permissions_related='content_object', text=_('Permissions'), text=_('Permissions'), view='acls:acl_permissions'
view='acls:acl_permissions',
) )

View File

@@ -1,17 +1,23 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from functools import reduce
import logging import logging
import operator
import warnings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import CharField, Value as V, Q
from django.db.models.functions import Concat
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext from django.utils.translation import ugettext
from mayan.apps.common.utils import ( from mayan.apps.common.utils import (
resolve_attribute, return_attrib, return_related get_related_field, resolve_attribute, return_related
) )
from mayan.apps.common.warnings import InterfaceWarning
from mayan.apps.permissions import Permission from mayan.apps.permissions import Permission
from mayan.apps.permissions.models import StoredPermission from mayan.apps.permissions.models import StoredPermission
@@ -26,170 +32,235 @@ class AccessControlListManager(models.Manager):
Implement a 3 tier permission system, involving a permissions, an actor Implement a 3 tier permission system, involving a permissions, an actor
and an object and an object
""" """
def check_access(self, permissions, user, obj, related=None): def _get_acl_filters(self, queryset, stored_permission, user, related_field_name=None):
if user.is_superuser or user.is_staff: """
logger.debug( This method does the bulk of the work. It generates filters for the
'Permissions "%s" on "%s" granted to user "%s" as superuser ' AccessControlList model to determine if there are ACL entries for the
'or staff', permissions, obj, user members of the queryset's model provided.
"""
# Determine which of the cases we need to address
# 1: No related field
# 2: Related field
# 3: Related field that is Generic Foreign Key
# 4: No related field, but has an inherited related field, solved by
# recursion, branches to #2 or #3.
# 5: Inherited field of a related field
# -- Not addressed yet --
# 6: Inherited field of a related field that is Generic Foreign Key
# 7: Has a related function
result = []
if related_field_name:
related_field = get_related_field(
model=queryset.model, related_field_name=related_field_name
) )
return True
try: if isinstance(related_field, GenericForeignKey):
return Permission.check_permissions( # Case 3: Generic Foreign Key, multiple ContentTypes + object
permissions=permissions, user=user # id combinations
content_type_object_id_queryset = queryset.annotate(
ct_fk_combination=Concat(
related_field.ct_field, V('-'), related_field.fk_field,
output_field=CharField()
) )
except PermissionDenied: ).values('ct_fk_combination')
try:
stored_permissions = [
permission.stored_permission for permission in permissions
]
except TypeError:
# Not a list of permissions, just one
stored_permissions = (permissions.stored_permission,)
if related: acl_filter = self.annotate(
obj = return_attrib(obj, related) ct_fk_combination=Concat(
'content_type', V('-'), 'object_id', output_field=CharField()
try:
parent_accessor = ModelPermission.get_inheritance(
model=obj._meta.model
) )
except AttributeError: ).filter(
# AttributeError means non model objects: ie Statistics permissions=stored_permission, role__groups__user=user,
# These can't have ACLs so we raise PermissionDenied ct_fk_combination__in=content_type_object_id_queryset
).values('object_id')
# Force object to text to avoid UnicodeDecodeError field_lookup = 'object_id__in'
raise PermissionDenied(
ugettext('Insufficient access for: %s') % force_text(obj) result.append(Q(**{field_lookup: acl_filter}))
else:
# Case 2: Related field of a single type, single ContentType,
# multiple object id
content_type = ContentType.objects.get_for_model(
model=related_field.related_model
)
field_lookup = '{}_id__in'.format(related_field_name)
acl_filter = self.filter(
content_type=content_type, permissions=stored_permission,
role__groups__user=user
).values('object_id')
# Don't add empty filters otherwise the default AND operator
# of the Q object will return an empty queryset when reduced
# and filter out objects that should be in the final queryset.
if acl_filter:
result.append(Q(**{field_lookup: acl_filter}))
# Case 5: Related field, has an inherited related field itself
# Bubble up permssion check
# TODO: Add relationship support: OR or AND
# TODO: OR for document pages, version, doc, and types
# TODO: AND for new cabinet levels ACLs
try:
related_field_model_related_fields = (
ModelPermission.get_inheritance(
model=related_field.related_model
),
) )
except KeyError: except KeyError:
pass pass
else: else:
try: relation_result = []
return self.check_access( for related_field_model_related_field_name in related_field_model_related_fields:
obj=getattr(obj, parent_accessor), related_field_name = '{}__{}'.format(related_field_name, related_field_model_related_field_name)
permissions=permissions, user=user related_field_inherited_acl_queries = self._get_acl_filters(
queryset=queryset, stored_permission=stored_permission,
user=user, related_field_name=related_field_name
) )
except AttributeError: if related_field_inherited_acl_queries:
# Has no such attribute, try it as a related field relation_result.append(reduce(operator.and_, related_field_inherited_acl_queries))
if relation_result:
result.append(reduce(operator.or_, relation_result))
else:
# Case 1: Original model, single ContentType, multiple object id
content_type = ContentType.objects.get_for_model(model=queryset.model)
field_lookup = 'id__in'
acl_filter = self.filter(
content_type=content_type, permissions=stored_permission,
role__groups__user=user
).values('object_id')
result.append(Q(**{field_lookup: acl_filter}))
# Case 4: Original model, has an inherited related field
try: try:
return self.check_access( related_fields = (
obj=return_related( ModelPermission.get_inheritance(
instance=obj, related_field=parent_accessor model=queryset.model
), permissions=permissions, user=user ),
) )
except PermissionDenied: except KeyError:
pass
except PermissionDenied:
pass pass
else:
relation_result = []
user_roles = [] for related_field_name in related_fields:
for group in user.groups.all(): inherited_acl_queries = self._get_acl_filters(
for role in group.roles.all(): queryset=queryset, stored_permission=stored_permission,
if set(stored_permissions).intersection(set(self.get_inherited_permissions(role=role, obj=obj))): related_field_name=related_field_name, user=user
logger.debug(
'Permissions "%s" on "%s" granted to user "%s" through role "%s" via inherited ACL',
permissions, obj, user, role
) )
return True if inherited_acl_queries:
relation_result.append(reduce(operator.and_, inherited_acl_queries))
user_roles.append(role) if relation_result:
result.append(reduce(operator.or_, relation_result))
if not self.filter(content_type=ContentType.objects.get_for_model(obj), object_id=obj.pk, permissions__in=stored_permissions, role__in=user_roles).exists():
logger.debug(
'Permissions "%s" on "%s" denied for user "%s"',
permissions, obj, user
)
raise PermissionDenied(
ugettext('Insufficient access for: %s') % force_text(obj)
)
logger.debug(
'Permissions "%s" on "%s" granted to user "%s" through roles "%s" by direct ACL',
permissions, obj, user, user_roles
)
def filter_by_access(self, permission, user, queryset):
if user.is_superuser or user.is_staff:
logger.debug(
'Unfiltered queryset returned to user "%s" as superuser or staff',
user
)
return queryset
# Case 7: Has a function
try: try:
Permission.check_permissions( field_query_function = ModelPermission.get_function(
permissions=(permission,), user=user
)
except PermissionDenied:
user_roles = []
for group in user.groups.all():
for role in group.roles.all():
user_roles.append(role)
try:
parent_accessor = ModelPermission.get_inheritance(
model=queryset.model model=queryset.model
) )
except KeyError: except KeyError:
parent_acl_query = Q()
else:
instance = queryset.first()
if instance:
parent_object = return_related(
instance=instance, related_field=parent_accessor
)
try:
# Try to see if parent_object is a function
parent_object()
except TypeError:
# Is not a function, try it as a field
parent_content_type = ContentType.objects.get_for_model(
parent_object
)
parent_queryset = self.filter(
content_type=parent_content_type, role__in=user_roles,
permissions=permission.stored_permission
)
parent_acl_query = Q(
**{
'{}__pk__in'.format(
parent_accessor
): parent_queryset.values_list(
'object_id', flat=True
)
}
)
else:
# Is a function. Can't perform Q object filtering.
# Perform iterative filtering.
result = []
for entry in queryset:
try:
self.check_access(permissions=permission, user=user, obj=entry)
except PermissionDenied:
pass pass
else: else:
result.append(entry.pk) function_results = field_query_function()
return queryset.filter(pk__in=result) # Filter by the model's content type
else: content_type = ContentType.objects.get_for_model(
parent_acl_query = Q() model=queryset.model
)
acl_filter = self.filter(
content_type=content_type, permissions=stored_permission,
role__groups__user=user
).values('object_id')
# Obtain an queryset of filtered, authorized model instances
acl_queryset = queryset.model._meta.default_manager.filter(
id__in=acl_filter
).filter(**function_results['acl_filter'])
# Directly granted access if 'acl_values' in function_results:
content_type = ContentType.objects.get_for_model(queryset.model) acl_queryset = acl_queryset.values(
acl_query = Q(pk__in=self.filter( *function_results['acl_values']
content_type=content_type, role__in=user_roles,
permissions=permission.stored_permission
).values_list('object_id', flat=True))
logger.debug(
'Filtered queryset returned to user "%s" based on roles "%s"',
user, user_roles
) )
return queryset.filter(parent_acl_query | acl_query) # Get the final query using the filtered queryset as the
# reference
result.append(
Q(**{function_results['field_lookup']: acl_queryset})
)
return result
def check_access(self, obj, permissions, user, related=None):
"""
The `related` argument is ignored.
"""
if related:
warnings.warn(
'Passing the argument `related` to check_access() is '
'deprecated. Use the ModelPermission\'s class '
'.register_inheritance() class method to register the access '
'relationship between two models. The registered relationship '
'will be automatically used by check_access().',
InterfaceWarning
)
meta = getattr(obj, '_meta', None)
if not meta:
logger.debug(
ugettext(
'Object "%s" is not a model and cannot be checked for '
'access.'
) % force_text(obj)
)
return True
else: else:
source_queryset = obj._meta.default_manager.all()
restricted_queryset = obj._meta.default_manager.none()
for permission in permissions:
# Default relationship betweens permissions is OR
# TODO: Add support for AND relationship
restricted_queryset = restricted_queryset | self.restrict_queryset(
permission=permission, queryset=source_queryset, user=user
)
if restricted_queryset.filter(pk=obj.pk).exists():
return True
else:
raise PermissionDenied(
ugettext(message='Insufficient access for: %s') % force_text(
s=obj
)
)
def filter_by_access(self, permission, user, queryset):
return self.restrict_queryset(
permission=permission, queryset=queryset, user=user
)
def restrict_queryset(self, permission, queryset, user):
# Check directly granted permission via a role
try:
Permission.check_user_permissions(
permissions=(permission,), user=user
)
except PermissionDenied:
acl_filters = self._get_acl_filters(
queryset=queryset,
stored_permission=permission.stored_permission, user=user
)
final_query = None
for acl_filter in acl_filters:
if final_query is None:
final_query = acl_filter
else:
final_query = final_query | acl_filter
return queryset.filter(final_query)
else:
# User has direct permission assignment via a role, is superuser or
# is staff. Return the entire queryset.
return queryset return queryset
def get_inherited_permissions(self, obj, role): def get_inherited_permissions(self, obj, role):

View File

@@ -44,7 +44,7 @@ class ACLCreateView(SingleObjectCreateView):
raise Http404 raise Http404
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.content_object, permissions=permission_acl_edit, obj=self.content_object, permissions=(permission_acl_edit,),
user=request.user user=request.user
) )
@@ -86,7 +86,7 @@ class ACLDeleteView(SingleObjectDeleteView):
acl = get_object_or_404(klass=AccessControlList, pk=self.kwargs['pk']) acl = get_object_or_404(klass=AccessControlList, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=acl.content_object, permissions=permission_acl_edit, obj=acl.content_object, permissions=(permission_acl_edit,),
user=request.user user=request.user
) )
@@ -126,7 +126,7 @@ class ACLListView(SingleObjectListView):
raise Http404 raise Http404
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.content_object, permissions=permission_acl_view, obj=self.content_object, permissions=(permission_acl_view,),
user=request.user user=request.user
) )

View File

@@ -89,7 +89,7 @@ class GrantAccessAction(WorkflowAction):
try: try:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_acl_edit, user=request.user, obj=obj obj=obj, permissions=(permission_acl_edit,), user=request.user
) )
except Exception as exception: except Exception as exception:
raise ValidationError(exception) raise ValidationError(exception)

View File

@@ -35,8 +35,8 @@ class APIDocumentCabinetListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user, obj=document, permissions=(permission_document_view,),
obj=document user=self.request.user
) )
queryset = document.get_cabinets() queryset = document.get_cabinets()
@@ -189,8 +189,8 @@ class APICabinetDocumentView(generics.RetrieveDestroyAPIView):
instance = self.get_object() instance = self.get_object()
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user, obj=instance, permissions=(permission_document_view,),
obj=instance user=self.request.user
) )
serializer = self.get_serializer(instance) serializer = self.get_serializer(instance)

View File

@@ -73,8 +73,16 @@ class CabinetsApp(MayanAppConfig):
permission_cabinet_remove_document permission_cabinet_remove_document
) )
) )
ModelPermission.register_inheritance(
model=Cabinet, related='get_root', def get_root_filter():
return {
'acl_filter': {'level': 0},
'acl_values': ('tree_id',),
'field_lookup': 'tree_id__in'
}
ModelPermission.register_function(
model=Cabinet, function=get_root_filter
) )
SourceColumn( SourceColumn(

View File

@@ -63,7 +63,7 @@ class CabinetChildAddView(SingleObjectCreateView):
cabinet = super(CabinetChildAddView, self).get_object(*args, **kwargs) cabinet = super(CabinetChildAddView, self).get_object(*args, **kwargs)
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=cabinet.get_root(), permissions=permission_cabinet_edit, obj=cabinet.get_root(), permissions=(permission_cabinet_edit,),
user=self.request.user user=self.request.user
) )
@@ -142,7 +142,7 @@ class CabinetDetailView(DocumentListView):
permission_object = cabinet.get_root() permission_object = cabinet.get_root()
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=permission_object, permissions=permission_cabinet_view, obj=permission_object, permissions=(permission_cabinet_view,),
user=self.request.user user=self.request.user
) )
@@ -193,7 +193,7 @@ class DocumentCabinetListView(CabinetListView):
self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_view, obj=self.document, permissions=(permission_document_view,),
user=request.user user=request.user
) )
@@ -288,7 +288,7 @@ class DocumentAddToCabinetView(MultipleObjectFormActionView):
for cabinet in form.cleaned_data['cabinets']: for cabinet in form.cleaned_data['cabinets']:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=cabinet, permissions=permission_cabinet_add_document, obj=cabinet, permissions=(permission_cabinet_add_document,),
user=self.request.user user=self.request.user
) )
if cabinet in cabinet_membership: if cabinet in cabinet_membership:
@@ -376,7 +376,7 @@ class DocumentRemoveFromCabinetView(MultipleObjectFormActionView):
for cabinet in form.cleaned_data['cabinets']: for cabinet in form.cleaned_data['cabinets']:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=cabinet, permissions=permission_cabinet_remove_document, obj=cabinet, permissions=(permission_cabinet_remove_document,),
user=self.request.user user=self.request.user
) )

View File

@@ -79,12 +79,13 @@ class APICheckedoutDocumentView(generics.RetrieveDestroyAPIView):
if document.checkout_info().user == request.user: if document.checkout_info().user == request.user:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_check_in, obj=document, permissions=(permission_document_check_in,),
user=request.user user=request.user
) )
else: else:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_check_in_override, obj=document,
permissions=(permission_document_check_in_override,),
user=request.user user=request.user
) )

View File

@@ -42,7 +42,7 @@ class NewDocumentCheckoutSerializer(serializers.ModelSerializer):
document = Document.objects.get(pk=validated_data.pop('document_pk')) document = Document.objects.get(pk=validated_data.pop('document_pk'))
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_check_out, obj=document, permissions=(permission_document_check_out,),
user=self.context['request'].user user=self.context['request'].user
) )

View File

@@ -31,7 +31,7 @@ class CheckoutDocumentView(SingleObjectCreateView):
self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_check_out, obj=self.document, permissions=(permission_document_check_out,),
user=request.user user=request.user
) )
@@ -168,13 +168,13 @@ class DocumentCheckinView(ConfirmView):
if document.get_check_out_info().user == self.request.user: if document.get_check_out_info().user == self.request.user:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_check_in, obj=document, permissions=(permission_document_check_in,),
user=self.request.user user=self.request.user
) )
else: else:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, obj=document,
permissions=permission_document_check_in_override, permissions=(permission_document_check_in_override,),
user=self.request.user user=self.request.user
) )

View File

@@ -286,8 +286,8 @@ class ObjectListPermissionFilterMixin(object):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if self.access_object_retrieve_method and self.object_permission: if self.access_object_retrieve_method and self.object_permission:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(self.object_permission,), user=request.user, obj=getattr(self, self.access_object_retrieve_method)(),
obj=getattr(self, self.access_object_retrieve_method)() permissions=(self.object_permission,), user=request.user
) )
return super(ObjectListPermissionFilterMixin, self).dispatch(request, *args, **kwargs) return super(ObjectListPermissionFilterMixin, self).dispatch(request, *args, **kwargs)
@@ -296,7 +296,8 @@ class ObjectListPermissionFilterMixin(object):
if not self.access_object_retrieve_method and self.object_permission: if not self.access_object_retrieve_method and self.object_permission:
return AccessControlList.objects.filter_by_access( return AccessControlList.objects.filter_by_access(
self.object_permission, self.request.user, queryset=queryset queryset=queryset, permission=self.object_permission,
user=self.request.user
) )
else: else:
return queryset return queryset
@@ -327,9 +328,10 @@ class ObjectPermissionCheckMixin(object):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if self.object_permission: if self.object_permission:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=self.object_permission, user=request.user,
obj=self.get_permission_object(), obj=self.get_permission_object(),
related=getattr(self, 'object_permission_related', None) permissions=(self.object_permission,),
related=getattr(self, 'object_permission_related', None),
user=request.user
) )
return super( return super(
@@ -427,7 +429,7 @@ class ViewPermissionCheckMixin(object):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if self.view_permission: if self.view_permission:
Permission.check_permissions( Permission.check_user_permissions(
permissions=(self.view_permission,), user=self.request.user permissions=(self.view_permission,), user=self.request.user
) )

View File

@@ -33,6 +33,26 @@ def encapsulate(function):
return lambda: function return lambda: function
def get_related_field(model, related_field_name):
try:
local_field_name, remaining_field_path = related_field_name.split(
LOOKUP_SEP, 1
)
except ValueError:
local_field_name = related_field_name
remaining_field_path = None
related_field = model._meta.get_field(local_field_name)
if remaining_field_path:
return get_related_field(
model=related_field.related_model,
related_field_name=remaining_field_path
)
return related_field
def introspect_attribute(attribute_name, obj): def introspect_attribute(attribute_name, obj):
""" """
Resolve the attribute of model. Supports nested reference using dotted Resolve the attribute of model. Supports nested reference using dotted

View File

@@ -145,7 +145,7 @@ class ObjectErrorLogEntryListClearView(ConfirmView):
class ObjectErrorLogEntryListView(SingleObjectListView): class ObjectErrorLogEntryListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_object(), permissions=permission_error_log_view, obj=self.get_object(), permissions=(permission_error_log_view,),
user=request.user user=request.user
) )

View File

@@ -5,3 +5,9 @@ class DatabaseWarning(UserWarning):
""" """
Warning when using unsupported database backends Warning when using unsupported database backends
""" """
class InterfaceWarning(UserWarning):
"""
Warning when using obsolete internal interfaces
"""

View File

@@ -45,7 +45,7 @@ class TransformationCreateView(SingleObjectCreateView):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.content_object, obj=self.content_object,
permissions=permission_transformation_create, user=request.user permissions=(permission_transformation_create,), user=request.user
) )
return super(TransformationCreateView, self).dispatch( return super(TransformationCreateView, self).dispatch(
@@ -96,7 +96,7 @@ class TransformationDeleteView(SingleObjectDeleteView):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.transformation.content_object, obj=self.transformation.content_object,
permissions=permission_transformation_delete, user=request.user permissions=(permission_transformation_delete,), user=request.user
) )
return super(TransformationDeleteView, self).dispatch( return super(TransformationDeleteView, self).dispatch(
@@ -145,7 +145,7 @@ class TransformationEditView(SingleObjectEditView):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.transformation.content_object, obj=self.transformation.content_object,
permissions=permission_transformation_edit, user=request.user permissions=(permission_transformation_edit,), user=request.user
) )
return super(TransformationEditView, self).dispatch( return super(TransformationEditView, self).dispatch(
@@ -202,7 +202,7 @@ class TransformationListView(SingleObjectListView):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.content_object, obj=self.content_object,
permissions=permission_transformation_view, user=request.user permissions=(permission_transformation_view,), user=request.user
) )
return super(TransformationListView, self).dispatch( return super(TransformationListView, self).dispatch(

View File

@@ -30,7 +30,7 @@ class APICommentListView(generics.ListCreateAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_required, obj=document, permissions=(permission_required,),
user=self.request.user user=self.request.user
) )
@@ -85,7 +85,7 @@ class APICommentView(generics.RetrieveDestroyAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_required, obj=document, permissions=(permission_required,),
user=self.request.user user=self.request.user
) )

View File

@@ -26,7 +26,7 @@ class DocumentCommentCreateView(SingleObjectCreateView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_document(), permissions=permission_comment_create, obj=self.get_document(), permissions=(permission_comment_create,),
user=request.user user=request.user
) )
@@ -67,7 +67,7 @@ class DocumentCommentDeleteView(SingleObjectDeleteView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_object().document, obj=self.get_object().document,
permissions=permission_comment_delete, user=request.user permissions=(permission_comment_delete,), user=request.user
) )
return super( return super(
@@ -120,7 +120,7 @@ class DocumentCommentListView(SingleObjectListView):
def get_object_list(self): def get_object_list(self):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_document(), permissions=permission_comment_view, obj=self.get_document(), permissions=(permission_comment_view,),
user=self.request.user user=self.request.user
) )

View File

@@ -67,7 +67,7 @@ class APIIndexNodeInstanceDocumentListView(generics.ListAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=index_node_instance.index, obj=index_node_instance.index,
permissions=permission_document_indexing_view, permissions=(permission_document_indexing_view,),
user=self.request.user user=self.request.user
) )
@@ -113,7 +113,7 @@ class APIDocumentIndexListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_view, obj=document, permissions=(permission_document_view,),
user=self.request.user user=self.request.user
) )

View File

@@ -204,7 +204,7 @@ class TemplateNodeCreateView(SingleObjectCreateView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_parent_node().index, obj=self.get_parent_node().index,
permissions=permission_document_indexing_edit, user=request.user permissions=(permission_document_indexing_edit,), user=request.user
) )
return super( return super(
@@ -311,7 +311,7 @@ class IndexInstanceNodeView(DocumentListView):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.index_instance_node.index(), obj=self.index_instance_node.index(),
permissions=permission_document_indexing_instance_view, permissions=(permission_document_indexing_instance_view,),
user=request.user user=request.user
) )
@@ -378,7 +378,7 @@ class DocumentIndexNodeListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_document(), permissions=permission_document_view, obj=self.get_document(), permissions=(permission_document_view,),
user=request.user user=request.user
) )

View File

@@ -65,6 +65,7 @@ class DocumentSignaturesApp(MayanAppConfig):
app_label='django_gpg', model_name='Key' app_label='django_gpg', model_name='Key'
) )
DetachedSignature = self.get_model(model_name='DetachedSignature')
EmbeddedSignature = self.get_model(model_name='EmbeddedSignature') EmbeddedSignature = self.get_model(model_name='EmbeddedSignature')
SignatureBaseModel = self.get_model(model_name='SignatureBaseModel') SignatureBaseModel = self.get_model(model_name='SignatureBaseModel')
@@ -86,6 +87,12 @@ class DocumentSignaturesApp(MayanAppConfig):
permission_document_version_signature_upload, permission_document_version_signature_upload,
) )
) )
ModelPermission.register_inheritance(
model=SignatureBaseModel, related='document_version'
)
ModelPermission.register_inheritance(
model=DetachedSignature, related='document_version'
)
SourceColumn( SourceColumn(
source=SignatureBaseModel, label=_('Date'), attribute='date' source=SignatureBaseModel, label=_('Date'), attribute='date'

View File

@@ -42,47 +42,44 @@ link_document_version_signature_delete = Link(
args='resolved_object.pk', condition=is_detached_signature, args='resolved_object.pk', condition=is_detached_signature,
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_delete', icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_delete',
permissions=(permission_document_version_signature_delete,), permissions=(permission_document_version_signature_delete,),
permissions_related='document_version.document', tags='dangerous', tags='dangerous', text=_('Delete'),
text=_('Delete'), view='signatures:document_version_signature_delete', view='signatures:document_version_signature_delete',
) )
link_document_version_signature_details = Link( link_document_version_signature_details = Link(
args='resolved_object.pk', args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_details', icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_details',
permissions=(permission_document_version_signature_view,), permissions=(permission_document_version_signature_view,),
permissions_related='document_version.document', text=_('Details'), text=_('Details'), view='signatures:document_version_signature_details',
view='signatures:document_version_signature_details',
) )
link_document_version_signature_list = Link( link_document_version_signature_list = Link(
args='resolved_object.pk', args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_list', icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_list',
permissions=(permission_document_version_signature_view,), permissions=(permission_document_version_signature_view,),
permissions_related='document', text=_('Signatures'), text=_('Signatures'), view='signatures:document_version_signature_list'
view='signatures:document_version_signature_list',
) )
link_document_version_signature_download = Link( link_document_version_signature_download = Link(
args='resolved_object.pk', condition=is_detached_signature, args='resolved_object.pk', condition=is_detached_signature,
permissions=(permission_document_version_signature_download,), permissions=(permission_document_version_signature_download,),
permissions_related='document_version.document', text=_('Download'), text=_('Download'), view='signatures:document_version_signature_download'
view='signatures:document_version_signature_download',
) )
link_document_version_signature_upload = Link( link_document_version_signature_upload = Link(
args='resolved_object.pk', args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_upload', icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_upload',
permissions=(permission_document_version_signature_upload,), permissions=(permission_document_version_signature_upload,),
permissions_related='document', text=_('Upload signature'), text=_('Upload signature'),
view='signatures:document_version_signature_upload', view='signatures:document_version_signature_upload'
) )
link_document_version_signature_detached_create = Link( link_document_version_signature_detached_create = Link(
args='resolved_object.pk', args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_detached_create', icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_detached_create',
permissions=(permission_document_version_sign_detached,), permissions=(permission_document_version_sign_detached,),
permissions_related='document', text=_('Sign detached'), text=_('Sign detached'),
view='signatures:document_version_signature_detached_create', view='signatures:document_version_signature_detached_create'
) )
link_document_version_signature_embedded_create = Link( link_document_version_signature_embedded_create = Link(
args='resolved_object.pk', args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_embedded_create', icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_embedded_create',
permissions=(permission_document_version_sign_embedded,), permissions=(permission_document_version_sign_embedded,),
permissions_related='document', text=_('Sign embedded'), text=_('Sign embedded'),
view='signatures:document_version_signature_embedded_create', view='signatures:document_version_signature_embedded_create'
) )

View File

@@ -54,7 +54,7 @@ class DocumentVersionDetachedSignatureCreateView(FormView):
passphrase = form.cleaned_data['passphrase'] or None passphrase = form.cleaned_data['passphrase'] or None
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=key, permissions=permission_key_sign, user=self.request.user obj=key, permissions=(permission_key_sign,), user=self.request.user
) )
try: try:
@@ -109,7 +109,7 @@ class DocumentVersionDetachedSignatureCreateView(FormView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_document_version().document, obj=self.get_document_version().document,
permissions=permission_document_version_sign_detached, permissions=(permission_document_version_sign_detached,),
user=request.user user=request.user
) )
@@ -146,7 +146,7 @@ class DocumentVersionEmbeddedSignatureCreateView(FormView):
passphrase = form.cleaned_data['passphrase'] or None passphrase = form.cleaned_data['passphrase'] or None
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=key, permissions=permission_key_sign, user=self.request.user obj=key, permissions=(permission_key_sign,), user=self.request.user
) )
try: try:
@@ -206,7 +206,7 @@ class DocumentVersionEmbeddedSignatureCreateView(FormView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_document_version().document, obj=self.get_document_version().document,
permissions=permission_document_version_sign_embedded, permissions=(permission_document_version_sign_embedded,),
user=request.user user=request.user
) )
@@ -283,8 +283,9 @@ class DocumentVersionSignatureDownloadView(SingleObjectDownloadView):
class DocumentVersionSignatureListView(SingleObjectListView): class DocumentVersionSignatureListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_version_signature_view, obj=self.get_document_version(),
user=request.user, obj=self.get_document_version() permissions=(permission_document_version_signature_view,),
user=request.user
) )
return super( return super(
@@ -345,7 +346,7 @@ class DocumentVersionSignatureUploadView(SingleObjectCreateView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_document_version(), obj=self.get_document_version(),
permissions=permission_document_version_signature_upload, permissions=(permission_document_version_signature_upload,),
user=request.user user=request.user
) )

View File

@@ -40,8 +40,8 @@ class APIDocumentTypeWorkflowListView(generics.ListAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_type_view, user=self.request.user, obj=document_type, permissions=(permission_document_type_view,),
obj=document_type user=self.request.user
) )
return document_type return document_type
@@ -105,8 +105,8 @@ class APIWorkflowDocumentTypeList(generics.ListCreateAPIView):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=workflow, permissions=(permission_required,),
obj=workflow user=self.request.user
) )
return workflow return workflow
@@ -158,8 +158,8 @@ class APIWorkflowDocumentTypeView(generics.RetrieveDestroyAPIView):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=workflow, permissions=(permission_required,),
obj=workflow user=self.request.user
) )
return workflow return workflow
@@ -261,8 +261,8 @@ class APIWorkflowStateListView(generics.ListCreateAPIView):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=workflow, permissions=(permission_required,),
obj=workflow user=self.request.user
) )
return workflow return workflow
@@ -304,8 +304,8 @@ class APIWorkflowStateView(generics.RetrieveUpdateDestroyAPIView):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=workflow, permissions=(permission_required,),
obj=workflow user=self.request.user
) )
return workflow return workflow
@@ -357,8 +357,8 @@ class APIWorkflowTransitionListView(generics.ListCreateAPIView):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=workflow, permissions=(permission_required,),
obj=workflow user=self.request.user
) )
return workflow return workflow
@@ -411,8 +411,8 @@ class APIWorkflowTransitionView(generics.RetrieveUpdateDestroyAPIView):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=workflow, permissions=(permission_required,),
obj=workflow user=self.request.user
) )
return workflow return workflow
@@ -435,8 +435,8 @@ class APIWorkflowInstanceListView(generics.ListAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=self.request.user, obj=document, permissions=(permission_workflow_view,),
obj=document user=self.request.user
) )
return document return document
@@ -460,8 +460,8 @@ class APIWorkflowInstanceView(generics.RetrieveAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=self.request.user, obj=document, permissions=(permission_workflow_view,),
obj=document user=self.request.user
) )
return document return document
@@ -488,9 +488,10 @@ class APIWorkflowInstanceLogEntryListView(generics.ListCreateAPIView):
Failing that, check for ACLs for any of the workflow's transitions. Failing that, check for ACLs for any of the workflow's transitions.
Failing that, then raise PermissionDenied Failing that, then raise PermissionDenied
""" """
# TODO: Improvement above
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=self.request.user, obj=document, permissions=(permission_workflow_view,),
obj=document user=self.request.user
) )
return document return document

View File

@@ -417,8 +417,9 @@ class WorkflowInstance(models.Model):
all transition options. all transition options.
""" """
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_transition, obj=self.workflow,
user=_user, obj=self.workflow permissions=(permission_workflow_transition,),
user=_user
) )
except PermissionDenied: except PermissionDenied:
""" """
@@ -427,7 +428,8 @@ class WorkflowInstance(models.Model):
""" """
queryset = AccessControlList.objects.filter_by_access( queryset = AccessControlList.objects.filter_by_access(
permission=permission_workflow_transition, permission=permission_workflow_transition,
user=_user, queryset=queryset queryset=queryset,
user=_user
) )
return queryset return queryset
else: else:

View File

@@ -23,8 +23,8 @@ __all__ = (
class DocumentWorkflowInstanceListView(SingleObjectListView): class DocumentWorkflowInstanceListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=request.user, obj=self.get_document(), permissions=(permission_workflow_view,),
obj=self.get_document() user=request.user
) )
return super( return super(
@@ -58,8 +58,8 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
class WorkflowInstanceDetailView(SingleObjectListView): class WorkflowInstanceDetailView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=request.user, obj=self.get_workflow_instance().document,
obj=self.get_workflow_instance().document permissions=(permission_workflow_view,), user=request.user
) )
return super( return super(

View File

@@ -27,8 +27,8 @@ class WorkflowDocumentListView(DocumentListView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=request.user, obj=self.workflow, permissions=(permission_workflow_view,),
obj=self.workflow user=request.user
) )
return super( return super(
@@ -111,8 +111,8 @@ class WorkflowStateDocumentListView(DocumentListView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=self.request.user, obj=workflow_state.workflow,
obj=workflow_state.workflow permissions=(permission_workflow_view,), user=self.request.user
) )
return workflow_state return workflow_state
@@ -121,8 +121,8 @@ class WorkflowStateDocumentListView(DocumentListView):
class WorkflowStateListView(SingleObjectListView): class WorkflowStateListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=request.user, obj=self.get_workflow(), permissions=(permission_workflow_view,),
obj=self.get_workflow() user=request.user
) )
return super( return super(

View File

@@ -345,7 +345,7 @@ class SetupWorkflowStateCreateView(SingleObjectCreateView):
def get_workflow(self): def get_workflow(self):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission_workflow_edit,), obj=workflow, obj=workflow, permissions=(permission_workflow_edit,),
user=self.request.user user=self.request.user
) )
return workflow return workflow
@@ -380,7 +380,7 @@ class SetupWorkflowStateDeleteView(SingleObjectDeleteView):
def get_workflow(self): def get_workflow(self):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission_workflow_edit,), obj=workflow, obj=workflow, permissions=(permission_workflow_edit,),
user=self.request.user user=self.request.user
) )
return workflow return workflow
@@ -410,8 +410,8 @@ class SetupWorkflowStateListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=request.user, obj=self.get_workflow(), permissions=(permission_workflow_view,),
obj=self.get_workflow() user=request.user
) )
return super( return super(
@@ -492,7 +492,7 @@ class SetupWorkflowTransitionCreateView(SingleObjectCreateView):
def get_workflow(self): def get_workflow(self):
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk']) workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission_workflow_edit,), obj=workflow, obj=workflow, permissions=(permission_workflow_edit,),
user=self.request.user user=self.request.user
) )
return workflow return workflow
@@ -580,8 +580,9 @@ class SetupWorkflowTransitionTriggerEventListView(FormView):
def dispatch(self, *args, **kwargs): def dispatch(self, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_workflow_edit, obj=self.get_object().workflow,
user=self.request.user, obj=self.get_object().workflow permissions=(permission_workflow_edit,),
user=self.request.user
) )
EventType.refresh() EventType.refresh()

View File

@@ -16,7 +16,7 @@ from mayan.apps.rest_api.permissions import MayanPermission
from .literals import DOCUMENT_IMAGE_TASK_TIMEOUT from .literals import DOCUMENT_IMAGE_TASK_TIMEOUT
from .models import ( from .models import (
Document, DocumentType, RecentDocument DeletedDocument, Document, DocumentType, RecentDocument
) )
from .permissions import ( from .permissions import (
permission_document_create, permission_document_delete, permission_document_create, permission_document_delete,
@@ -42,14 +42,14 @@ from .tasks import task_generate_document_page_image
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class APIDeletedDocumentListView(generics.ListAPIView): class APITrashedDocumentListView(generics.ListAPIView):
""" """
Returns a list of all the trashed documents. Returns a list of all the trashed documents.
""" """
filter_backends = (MayanObjectPermissionsFilter,) filter_backends = (MayanObjectPermissionsFilter,)
mayan_object_permissions = {'GET': (permission_document_view,)} mayan_object_permissions = {'GET': (permission_document_view,)}
permission_classes = (MayanPermission,) permission_classes = (MayanPermission,)
queryset = Document.trash.all() queryset = DeletedDocument.objects.all()
serializer_class = DeletedDocumentSerializer serializer_class = DeletedDocumentSerializer
@@ -64,7 +64,7 @@ class APIDeletedDocumentView(generics.RetrieveDestroyAPIView):
'GET': (permission_document_view,) 'GET': (permission_document_view,)
} }
permission_classes = (MayanPermission,) permission_classes = (MayanPermission,)
queryset = Document.trash.all() queryset = DeletedDocument.objects.all()
serializer_class = DeletedDocumentSerializer serializer_class = DeletedDocumentSerializer
@@ -76,7 +76,7 @@ class APIDeletedDocumentRestoreView(generics.GenericAPIView):
'POST': (permission_document_restore,) 'POST': (permission_document_restore,)
} }
permission_classes = (MayanPermission,) permission_classes = (MayanPermission,)
queryset = Document.trash.all() queryset = DeletedDocument.objects.all()
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
return None return None
@@ -143,8 +143,8 @@ class APIDocumentListView(generics.ListCreateAPIView):
def perform_create(self, serializer): def perform_create(self, serializer):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission_document_create,), user=self.request.user, obj=serializer.validated_data['document_type'],
obj=serializer.validated_data['document_type'] permissions=(permission_document_create,), user=self.request.user
) )
serializer.save(_user=self.request.user) serializer.save(_user=self.request.user)
@@ -164,7 +164,8 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
document = get_object_or_404(Document.passthrough, pk=self.kwargs['pk']) document = get_object_or_404(Document.passthrough, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permission_required, self.request.user, document obj=document, permissions=(permission_required,),
user=self.request.user
) )
return document return document
@@ -231,7 +232,8 @@ class APIDocumentPageView(generics.RetrieveUpdateAPIView):
document = get_object_or_404(Document, pk=self.kwargs['pk']) document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permission_required, self.request.user, document obj=document, permissions=(permission_required,),
user=self.request.user
) )
return document return document
@@ -309,8 +311,8 @@ class APIDocumentTypeDocumentListView(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
document_type = get_object_or_404(DocumentType, pk=self.kwargs['pk']) document_type = get_object_or_404(DocumentType, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_type_view, user=self.request.user, obj=document_type, permissions=(permission_document_type_view,),
obj=document_type user=self.request.user
) )
return document_type.documents.all() return document_type.documents.all()
@@ -326,8 +328,8 @@ class APIDocumentVersionDownloadView(DownloadMixin, generics.RetrieveAPIView):
document = get_object_or_404(Document, pk=self.kwargs['pk']) document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission_document_download,), user=self.request.user, obj=document, permissions=(permission_document_download,),
obj=document user=self.request.user
) )
return document return document
@@ -420,7 +422,8 @@ class APIDocumentVersionPageListView(generics.ListAPIView):
document = get_object_or_404(Document, pk=self.kwargs['pk']) document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permission_document_view, self.request.user, document obj=document, permissions=(permission_document_view,),
user=self.request.user
) )
return document return document
@@ -442,7 +445,6 @@ class APIDocumentVersionsListView(generics.ListCreateAPIView):
mayan_object_permissions = { mayan_object_permissions = {
'GET': (permission_document_version_view,), 'GET': (permission_document_version_view,),
} }
mayan_permission_attribute_check = 'document'
permission_classes = (MayanPermission,) permission_classes = (MayanPermission,)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
@@ -471,8 +473,8 @@ class APIDocumentVersionsListView(generics.ListCreateAPIView):
document = get_object_or_404(Document, pk=self.kwargs['pk']) document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission_document_new_version,), obj=document, permissions=(permission_document_new_version,),
user=self.request.user, obj=document user=self.request.user,
) )
serializer.save(document=document, _user=self.request.user) serializer.save(document=document, _user=self.request.user)
@@ -497,7 +499,8 @@ class APIDocumentVersionView(generics.RetrieveUpdateDestroyAPIView):
document = get_object_or_404(Document, pk=self.kwargs['pk']) document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permission_required, self.request.user, document obj=document, permissions=(permission_required,),
user=self.request.user
) )
return document return document

View File

@@ -220,10 +220,6 @@ class DocumentsApp(MayanAppConfig):
) )
) )
ModelPermission.register_proxy(
source=Document, model=DocumentType,
)
ModelPermission.register_inheritance( ModelPermission.register_inheritance(
model=Document, related='document_type', model=Document, related='document_type',
) )

View File

@@ -53,13 +53,13 @@ def task_clear_image_cache():
@app.task(ignore_result=True) @app.task(ignore_result=True)
def task_delete_document(deleted_document_id): def task_delete_document(trashed_document_id):
DeletedDocument = apps.get_model( DeletedDocument = apps.get_model(
app_label='documents', model_name='DeletedDocument' app_label='documents', model_name='DeletedDocument'
) )
logger.debug(msg='Executing') logger.debug(msg='Executing')
deleted_document = DeletedDocument.objects.get(pk=deleted_document_id) deleted_document = DeletedDocument.objects.get(pk=trashed_document_id)
deleted_document.delete() deleted_document.delete()
logger.debug(msg='Finshed') logger.debug(msg='Finshed')

View File

@@ -9,7 +9,7 @@ from ..permissions import (
from .base import GenericDocumentViewTestCase from .base import GenericDocumentViewTestCase
class DeletedDocumentTestCase(GenericDocumentViewTestCase): class TrashedDocumentTestCase(GenericDocumentViewTestCase):
def _request_document_restore_view(self): def _request_document_restore_view(self):
return self.post( return self.post(
viewname='documents:document_restore', kwargs={ viewname='documents:document_restore', kwargs={
@@ -22,7 +22,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
response = self._request_document_restore_view() response = self._request_document_restore_view()
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 302)
self.assertEqual(DeletedDocument.objects.count(), 1) self.assertEqual(DeletedDocument.objects.count(), 1)
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
@@ -50,7 +50,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
def test_document_trash_no_permissions(self): def test_document_trash_no_permissions(self):
response = self._request_document_trash_view() response = self._request_document_trash_view()
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 302)
self.assertEqual(DeletedDocument.objects.count(), 0) self.assertEqual(DeletedDocument.objects.count(), 0)
self.assertEqual(Document.objects.count(), 1) self.assertEqual(Document.objects.count(), 1)
@@ -79,7 +79,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(DeletedDocument.objects.count(), 1) self.assertEqual(DeletedDocument.objects.count(), 1)
response = self._request_document_delete_view() response = self._request_document_delete_view()
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 302)
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
self.assertEqual(DeletedDocument.objects.count(), 1) self.assertEqual(DeletedDocument.objects.count(), 1)

View File

@@ -3,7 +3,7 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from .api_views import ( from .api_views import (
APIDeletedDocumentListView, APIDeletedDocumentRestoreView, APITrashedDocumentListView, APIDeletedDocumentRestoreView,
APIDeletedDocumentView, APIDocumentDownloadView, APIDocumentView, APIDeletedDocumentView, APIDocumentDownloadView, APIDocumentView,
APIDocumentListView, APIDocumentVersionDownloadView, APIDocumentListView, APIDocumentVersionDownloadView,
APIDocumentPageImageView, APIDocumentPageView, APIDocumentPageImageView, APIDocumentPageView,
@@ -13,31 +13,33 @@ from .api_views import (
APIRecentDocumentListView APIRecentDocumentListView
) )
from .views import ( from .views import (
ClearImageCacheView, DeletedDocumentDeleteView, ClearImageCacheView, DocumentDocumentTypeEditView, DocumentDownloadFormView,
DeletedDocumentDeleteManyView, DeletedDocumentListView,
DocumentDocumentTypeEditView, DocumentDownloadFormView,
DocumentDownloadView, DocumentDuplicatesListView, DocumentEditView, DocumentDownloadView, DocumentDuplicatesListView, DocumentEditView,
DocumentListView, DocumentPageListView, DocumentPageNavigationFirst, DocumentListView, DocumentPageListView, DocumentPageNavigationFirst,
DocumentPageNavigationLast, DocumentPageNavigationNext, DocumentPageNavigationLast, DocumentPageNavigationNext,
DocumentPageNavigationPrevious, DocumentPageRotateLeftView, DocumentPageNavigationPrevious, DocumentPageRotateLeftView,
DocumentPageRotateRightView, DocumentPageView, DocumentPageViewResetView, DocumentPageRotateRightView, DocumentPageView, DocumentPageViewResetView,
DocumentPageZoomInView, DocumentPageZoomOutView, DocumentPreviewView, DocumentPageZoomInView, DocumentPageZoomOutView, DocumentPreviewView,
DocumentPrint, DocumentRestoreView, DocumentRestoreManyView, DocumentPrint, DocumentTransformationsClearView,
DocumentTransformationsClearView, DocumentTransformationsCloneView, DocumentTransformationsCloneView, DocumentTypeCreateView,
DocumentTrashView, DocumentTrashManyView, DocumentTypeCreateView,
DocumentTypeDeleteView, DocumentTypeDocumentListView, DocumentTypeDeleteView, DocumentTypeDocumentListView,
DocumentTypeFilenameCreateView, DocumentTypeFilenameDeleteView, DocumentTypeFilenameCreateView, DocumentTypeFilenameDeleteView,
DocumentTypeFilenameEditView, DocumentTypeFilenameListView, DocumentTypeFilenameEditView, DocumentTypeFilenameListView,
DocumentTypeListView, DocumentTypeEditView, DocumentUpdatePageCountView, DocumentTypeListView, DocumentTypeEditView, DocumentUpdatePageCountView,
DocumentVersionDownloadFormView, DocumentVersionDownloadView, DocumentVersionDownloadFormView, DocumentVersionDownloadView,
DocumentVersionListView, DocumentVersionRevertView, DocumentVersionView, DocumentVersionListView, DocumentVersionRevertView, DocumentVersionView,
DocumentView, DuplicatedDocumentListView, EmptyTrashCanView, DocumentView, DuplicatedDocumentListView,
RecentAccessDocumentListView, RecentAddedDocumentListView, RecentAccessDocumentListView, RecentAddedDocumentListView,
ScanDuplicatedDocuments ScanDuplicatedDocuments
) )
from .views.favorite_document_views import ( from .views.favorite_document_views import (
FavoriteAddView, FavoriteDocumentListView, FavoriteRemoveView FavoriteAddView, FavoriteDocumentListView, FavoriteRemoveView
) )
from .views.trashed_document_views import (
DocumentTrashView, EmptyTrashCanView, TrashedDocumentDeleteView,
TrashedDocumentListView, TrashedDocumentRestoreView
)
urlpatterns_favorite_documents = [ urlpatterns_favorite_documents = [
url( url(
@@ -62,6 +64,42 @@ urlpatterns_favorite_documents = [
view=FavoriteRemoveView.as_view(), view=FavoriteRemoveView.as_view(),
name='document_multiple_remove_from_favorites' name='document_multiple_remove_from_favorites'
), ),
url(
regex=r'^trash_can/empty/$', view=EmptyTrashCanView.as_view(),
name='trash_can_empty'
),
]
urlpatterns_trashed_documents = [
url(
regex=r'^(?P<pk>\d+)/trash/$', view=DocumentTrashView.as_view(),
name='document_trash'
),
url(
regex=r'^multiple/trash/$', view=DocumentTrashView.as_view(),
name='document_multiple_trash'
),
url(
regex=r'^list/deleted/$', view=TrashedDocumentListView.as_view(),
name='document_list_deleted'
),
url(
regex=r'^(?P<pk>\d+)/restore/$',
view=TrashedDocumentRestoreView.as_view(), name='document_restore'
),
url(
regex=r'^multiple/restore/$', view=TrashedDocumentRestoreView.as_view(),
name='document_multiple_restore'
),
url(
regex=r'^(?P<pk>\d+)/delete/$',
view=TrashedDocumentDeleteView.as_view(), name='document_delete'
),
url(
regex=r'^multiple/delete/$',
view=TrashedDocumentDeleteView.as_view(),
name='document_multiple_delete'
),
] ]
urlpatterns = [ urlpatterns = [
@@ -78,10 +116,6 @@ urlpatterns = [
view=RecentAddedDocumentListView.as_view(), view=RecentAddedDocumentListView.as_view(),
name='document_list_recent_added' name='document_list_recent_added'
), ),
url(
regex=r'^list/deleted/$', view=DeletedDocumentListView.as_view(),
name='document_list_deleted'
),
url( url(
regex=r'^list/duplicated/$', regex=r'^list/duplicated/$',
view=DuplicatedDocumentListView.as_view(), view=DuplicatedDocumentListView.as_view(),
@@ -100,23 +134,6 @@ urlpatterns = [
view=DocumentDuplicatesListView.as_view(), view=DocumentDuplicatesListView.as_view(),
name='document_duplicates_list' name='document_duplicates_list'
), ),
url(
regex=r'^(?P<pk>\d+)/restore/$', view=DocumentRestoreView.as_view(),
name='document_restore'
),
url(
regex=r'^multiple/restore/$', view=DocumentRestoreManyView.as_view(),
name='document_multiple_restore'
),
url(
regex=r'^(?P<pk>\d+)/delete/$',
view=DeletedDocumentDeleteView.as_view(), name='document_delete'
),
url(
regex=r'^multiple/delete/$',
view=DeletedDocumentDeleteManyView.as_view(),
name='document_multiple_delete'
),
url( url(
regex=r'^(?P<pk>\d+)/type/$', regex=r'^(?P<pk>\d+)/type/$',
view=DocumentDocumentTypeEditView.as_view(), view=DocumentDocumentTypeEditView.as_view(),
@@ -126,14 +143,6 @@ urlpatterns = [
regex=r'^multiple/type/$', view=DocumentDocumentTypeEditView.as_view(), regex=r'^multiple/type/$', view=DocumentDocumentTypeEditView.as_view(),
name='document_multiple_document_type_edit' name='document_multiple_document_type_edit'
), ),
url(
regex=r'^(?P<pk>\d+)/trash/$', view=DocumentTrashView.as_view(),
name='document_trash'
),
url(
regex=r'^multiple/trash/$', view=DocumentTrashManyView.as_view(),
name='document_multiple_trash'
),
url( url(
regex=r'^(?P<pk>\d+)/edit/$', view=DocumentEditView.as_view(), regex=r'^(?P<pk>\d+)/edit/$', view=DocumentEditView.as_view(),
name='document_edit' name='document_edit'
@@ -218,11 +227,6 @@ urlpatterns = [
regex=r'^cache/clear/$', view=ClearImageCacheView.as_view(), regex=r'^cache/clear/$', view=ClearImageCacheView.as_view(),
name='document_clear_image_cache' name='document_clear_image_cache'
), ),
url(
regex=r'^trash_can/empty/$', view=EmptyTrashCanView.as_view(),
name='trash_can_empty'
),
url( url(
regex=r'^page/(?P<pk>\d+)/$', view=DocumentPageView.as_view(), regex=r'^page/(?P<pk>\d+)/$', view=DocumentPageView.as_view(),
name='document_page_view' name='document_page_view'
@@ -323,7 +327,7 @@ urlpatterns = [
), ),
] ]
urlpatterns.extend(urlpatterns_favorite_documents) urlpatterns.extend(urlpatterns_favorite_documents)
urlpatterns.extend(urlpatterns_trashed_documents)
api_urls = [ api_urls = [
url( url(
@@ -384,7 +388,7 @@ api_urls = [
), ),
url( url(
regex=r'^trashed_documents/$', regex=r'^trashed_documents/$',
view=APIDeletedDocumentListView.as_view(), name='trasheddocument-list' view=APITrashedDocumentListView.as_view(), name='trasheddocument-list'
), ),
url( url(
regex=r'^trashed_documents/(?P<pk>[0-9]+)/$', regex=r'^trashed_documents/(?P<pk>[0-9]+)/$',

View File

@@ -38,8 +38,8 @@ logger = logging.getLogger(__name__)
class DocumentPageListView(SingleObjectListView): class DocumentPageListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user, obj=self.get_document(), permissions=(permission_document_view,),
obj=self.get_document() user=self.request.user
) )
return super( return super(
@@ -66,8 +66,8 @@ class DocumentPageNavigationBase(RedirectView):
document_page = self.get_object() document_page = self.get_object()
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=request.user, obj=document_page.document,
obj=document_page.document permissions=(permission_document_view,), user=request.user
) )
return super(DocumentPageNavigationBase, self).dispatch( return super(DocumentPageNavigationBase, self).dispatch(
@@ -170,8 +170,8 @@ class DocumentPageView(SimpleView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=request.user, obj=self.get_object().document,
obj=self.get_object().document permissions=(permission_document_view,), user=request.user
) )
return super( return super(
@@ -214,11 +214,11 @@ class DocumentPageViewResetView(RedirectView):
class DocumentPageInteractiveTransformation(RedirectView): class DocumentPageInteractiveTransformation(RedirectView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
object = self.get_object() obj = self.get_object()
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=request.user, obj=obj, permissions=(permission_document_view,),
obj=object user=request.user
) )
return super(DocumentPageInteractiveTransformation, self).dispatch( return super(DocumentPageInteractiveTransformation, self).dispatch(

View File

@@ -138,8 +138,9 @@ class DocumentTypeFilenameCreateView(SingleObjectCreateView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_type_edit, user=request.user, obj=self.get_document_type(),
obj=self.get_document_type() permissions=(permission_document_type_edit,),
user=request.user
) )
return super(DocumentTypeFilenameCreateView, self).dispatch( return super(DocumentTypeFilenameCreateView, self).dispatch(

View File

@@ -32,8 +32,9 @@ logger = logging.getLogger(__name__)
class DocumentVersionListView(SingleObjectListView): class DocumentVersionListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_version_view, user=request.user, obj=self.get_document(),
obj=self.get_document() permissions=(permission_document_version_view,),
user=request.user
) )
self.get_document().add_as_recent_document_for_user(request.user) self.get_document().add_as_recent_document_for_user(request.user)

View File

@@ -318,8 +318,8 @@ class DocumentDownloadView(SingleObjectDownloadView):
class DocumentDuplicatesListView(DocumentListView): class DocumentDuplicatesListView(DocumentListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user, obj=self.get_document(), permissions=(permission_document_view,),
obj=self.get_document() user=self.request.user
) )
return super( return super(
@@ -582,8 +582,8 @@ class DocumentTransformationsCloneView(FormView):
instance = get_object_or_404(klass=Document, pk=self.kwargs['pk']) instance = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_transformation_edit, obj=instance, permissions=(permission_transformation_edit,),
user=self.request.user, obj=instance user=self.request.user
) )
instance.add_as_recent_document_for_user(self.request.user) instance.add_as_recent_document_for_user(self.request.user)
@@ -597,8 +597,8 @@ class DocumentPrint(FormView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
instance = self.get_object() instance = self.get_object()
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_print, user=self.request.user, obj=instance, permissions=(permission_document_print,),
obj=instance user=self.request.user
) )
instance.add_as_recent_document_for_user(self.request.user) instance.add_as_recent_document_for_user(self.request.user)

View File

@@ -3,13 +3,13 @@ from __future__ import absolute_import, unicode_literals
import logging import logging
from django.contrib import messages from django.contrib import messages
from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy
from django.urls import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _, ungettext
from django.utils.translation import ugettext_lazy as _
from mayan.apps.acls.models import AccessControlList from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.generics import ConfirmView from mayan.apps.common.generics import (
from mayan.apps.common.mixins import MultipleInstanceActionMixin ConfirmView, MultipleObjectConfirmActionView
)
from ..icons import icon_document_list_deleted from ..icons import icon_document_list_deleted
from ..models import DeletedDocument, Document from ..models import DeletedDocument, Document
@@ -23,64 +23,100 @@ from ..tasks import task_delete_document
from .document_views import DocumentListView from .document_views import DocumentListView
__all__ = ( __all__ = (
'DeletedDocumentDeleteView', 'DeletedDocumentDeleteManyView', 'DocumentTrashView', 'EmptyTrashCanView', 'TrashedDocumentDeleteView',
'DeletedDocumentListView', 'DocumentRestoreView', 'DocumentRestoreManyView', 'TrashedDocumentListView', 'TrashedDocumentRestoreView'
'DocumentTrashView', 'DocumentTrashManyView', 'EmptyTrashCanView'
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class DeletedDocumentDeleteView(ConfirmView): class DocumentTrashView(MultipleObjectConfirmActionView):
extra_context = { model = Document
'title': _('Delete the selected document?') object_permission = permission_document_trash
pk_url_kwarg = 'pk'
success_message_singular = _(
'%(count)d document moved to the trash.'
)
success_message_plural = _(
'%(count)d documents moved to the trash.'
)
def get_extra_context(self):
queryset = self.get_object_list()
result = {
'title': ungettext(
single='Move the selected document to the trash?',
plural='Move the selected documents to the trash?',
number=queryset.count()
)
} }
def object_action(self, instance): return result
source_document = get_object_or_404(
klass=Document.passthrough, pk=instance.pk
)
AccessControlList.objects.check_access( def object_action(self, form, instance):
permissions=permission_document_delete, user=self.request.user, instance.delete()
obj=source_document
)
task_delete_document.apply_async(
kwargs={'deleted_document_id': instance.pk} class EmptyTrashCanView(ConfirmView):
extra_context = {
'title': _('Empty trash?')
}
view_permission = permission_empty_trash
action_cancel_redirect = post_action_redirect = reverse_lazy(
'documents:document_list_deleted'
) )
def view_action(self): def view_action(self):
instance = get_object_or_404( for deleted_document in DeletedDocument.objects.all():
klass=DeletedDocument, pk=self.kwargs['pk'] task_delete_document.apply_async(
) kwargs={'trashed_document_id': deleted_document.pk}
self.object_action(instance=instance)
messages.success(
self.request, _('Document: %(document)s deleted.') % {
'document': instance
}
) )
messages.success(self.request, _('Trash emptied successfully'))
class DeletedDocumentDeleteManyView(MultipleInstanceActionMixin, DeletedDocumentDeleteView):
extra_context = { class TrashedDocumentDeleteView(MultipleObjectConfirmActionView):
'title': _('Delete the selected documents?')
}
model = DeletedDocument model = DeletedDocument
success_message = '%(count)d document deleted.' object_permission = permission_document_delete
success_message_plural = '%(count)d documents deleted.' pk_url_kwarg = 'pk'
success_message_singular = _(
'%(count)d trashed document deleted.'
)
success_message_plural = _(
'%(count)d trashed documents deleted.'
)
def get_extra_context(self):
queryset = self.get_object_list()
result = {
'title': ungettext(
single='Delete the selected trashed document?',
plural='Delete the selected trashed documents?',
number=queryset.count()
)
}
return result
def object_action(self, form, instance):
task_delete_document.apply_async(
kwargs={'trashed_document_id': instance.pk}
)
class DeletedDocumentListView(DocumentListView): class TrashedDocumentListView(DocumentListView):
object_permission = None object_permission = None
def get_document_queryset(self): def get_document_queryset(self):
return AccessControlList.objects.filter_by_access( return AccessControlList.objects.filter_by_access(
permission_document_view, self.request.user, permission=permission_document_view,
queryset=DeletedDocument.trash.all() queryset=DeletedDocument.trash.all(),
user=self.request.user
) )
def get_extra_context(self): def get_extra_context(self):
context = super(DeletedDocumentListView, self).get_extra_context() context = super(TrashedDocumentListView, self).get_extra_context()
context.update( context.update(
{ {
'hide_link': True, 'hide_link': True,
@@ -99,103 +135,29 @@ class DeletedDocumentListView(DocumentListView):
return context return context
class DocumentRestoreView(ConfirmView): class TrashedDocumentRestoreView(MultipleObjectConfirmActionView):
extra_context = {
'title': _('Restore the selected document?')
}
def object_action(self, instance):
source_document = get_object_or_404(
klass=Document.passthrough, pk=instance.pk
)
AccessControlList.objects.check_access(
permissions=permission_document_restore, user=self.request.user,
obj=source_document
)
instance.restore()
def view_action(self):
instance = get_object_or_404(
klass=DeletedDocument, pk=self.kwargs['pk']
)
self.object_action(instance=instance)
messages.success(
self.request, _('Document: %(document)s restored.') % {
'document': instance
}
)
class DocumentRestoreManyView(MultipleInstanceActionMixin, DocumentRestoreView):
extra_context = {
'title': _('Restore the selected documents?')
}
model = DeletedDocument model = DeletedDocument
success_message = '%(count)d document restored.' object_permission = permission_document_restore
success_message_plural = '%(count)d documents restored.' pk_url_kwarg = 'pk'
success_message_singular = _(
'%(count)d trashed document restored.'
class DocumentTrashView(ConfirmView):
def get_extra_context(self):
return {
'object': self.get_object(),
'title': _('Move "%s" to the trash?') % self.get_object()
}
def get_object(self):
return get_object_or_404(klass=Document, pk=self.kwargs['pk'])
def get_post_action_redirect(self):
return reverse('documents:document_list_recent_access')
def object_action(self, instance):
AccessControlList.objects.check_access(
permissions=permission_document_trash, user=self.request.user,
obj=instance
) )
success_message_plural = _(
instance.delete() '%(count)d trashed documents restored.'
def view_action(self):
instance = self.get_object()
self.object_action(instance=instance)
messages.success(
self.request, _('Document: %(document)s moved to trash successfully.') % {
'document': instance
}
) )
class DocumentTrashManyView(MultipleInstanceActionMixin, DocumentTrashView):
model = Document
success_message = '%(count)d document moved to the trash.'
success_message_plural = '%(count)d documents moved to the trash.'
def get_extra_context(self): def get_extra_context(self):
return { queryset = self.get_object_list()
'title': _('Move the selected documents to the trash?')
result = {
'title': ungettext(
single='Restore the selected trashed document?',
plural='Restore the selected trashed documents?',
number=queryset.count()
)
} }
return result
class EmptyTrashCanView(ConfirmView): def object_action(self, form, instance):
extra_context = { instance.restore()
'title': _('Empty trash?')
}
view_permission = permission_empty_trash
action_cancel_redirect = post_action_redirect = reverse_lazy(
'documents:document_list_deleted'
)
def view_action(self):
for deleted_document in DeletedDocument.objects.all():
task_delete_document.apply_async(
kwargs={'deleted_document_id': deleted_document.pk}
)
messages.success(self.request, _('Trash emptied successfully'))

View File

@@ -42,8 +42,8 @@ class APIObjectEventListView(generics.ListAPIView):
obj = self.get_object() obj = self.get_object()
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_events_view, user=self.request.user, obj=obj, permissions=(permission_events_view,),
obj=obj user=self.request.user
) )
return any_stream(obj) return any_stream(obj)

View File

@@ -113,8 +113,9 @@ class EventType(object):
if result.target: if result.target:
try: try:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_events_view, obj=result.target,
user=user, obj=result.target permissions=(permission_events_view,),
user=user
) )
except PermissionDenied: except PermissionDenied:
pass pass
@@ -139,8 +140,9 @@ class EventType(object):
if relationship.exists(): if relationship.exists():
try: try:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_events_view, obj=result.target,
user=user, obj=result.target permissions=(permission_events_view,),
user=user
) )
except PermissionDenied: except PermissionDenied:
pass pass
@@ -161,8 +163,9 @@ class EventType(object):
if relationship.exists(): if relationship.exists():
try: try:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_events_view, obj=result.action_object,
user=user, obj=result.action_object permissions=(permission_events_view,),
user=user
) )
except PermissionDenied: except PermissionDenied:
pass pass
@@ -191,7 +194,6 @@ class ModelEventType(object):
Class to allow matching a model to a specific set of events. Class to allow matching a model to a specific set of events.
""" """
_inheritances = {} _inheritances = {}
_proxies = {}
_registry = {} _registry = {}
@classmethod @classmethod
@@ -211,11 +213,6 @@ class ModelEventType(object):
if class_events: if class_events:
events.extend(class_events) events.extend(class_events)
proxy = cls._proxies.get(type(instance))
if proxy:
events.extend(cls._registry.get(proxy))
pks = [ pks = [
event.id for event in set(events) event.id for event in set(events)
] ]
@@ -237,7 +234,3 @@ class ModelEventType(object):
@classmethod @classmethod
def register_inheritance(cls, model, related): def register_inheritance(cls, model, related):
cls._inheritances[model] = related cls._inheritances[model] = related
@classmethod
def register_proxy(cls, source, model):
cls._proxies[model] = source

View File

@@ -234,7 +234,7 @@ class ObjectEventTypeSubscriptionListView(FormView):
raise Http404 raise Http404
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=content_object, permissions=permission_events_view, obj=content_object, permissions=(permission_events_view,),
user=self.request.user user=self.request.user
) )

View File

@@ -35,7 +35,7 @@ class APIResolvedSmartLinkDocumentListView(generics.ListAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_view, obj=document, permissions=(permission_document_view,),
user=self.request.user user=self.request.user
) )
@@ -48,7 +48,7 @@ class APIResolvedSmartLinkDocumentListView(generics.ListAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=smart_link, permissions=permission_smart_link_view, obj=smart_link, permissions=(permission_smart_link_view,),
user=self.request.user user=self.request.user
) )
@@ -91,7 +91,7 @@ class APIResolvedSmartLinkView(generics.RetrieveAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_view, obj=document, permissions=(permission_document_view,),
user=self.request.user user=self.request.user
) )
@@ -128,7 +128,7 @@ class APIResolvedSmartLinkListView(generics.ListAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=document, permissions=permission_document_view, obj=document, permissions=(permission_document_view,),
user=self.request.user user=self.request.user
) )
@@ -187,7 +187,7 @@ class APISmartLinkConditionListView(generics.ListCreateAPIView):
smart_link = get_object_or_404(klass=SmartLink, pk=self.kwargs['pk']) smart_link = get_object_or_404(klass=SmartLink, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=smart_link, permissions=permission_required, obj=smart_link, permissions=(permission_required,),
user=self.request.user user=self.request.user
) )
@@ -230,7 +230,7 @@ class APISmartLinkConditionView(generics.RetrieveUpdateDestroyAPIView):
smart_link = get_object_or_404(klass=SmartLink, pk=self.kwargs['pk']) smart_link = get_object_or_404(klass=SmartLink, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=smart_link, permissions=permission_required, obj=smart_link, permissions=(permission_required,),
user=self.request.user user=self.request.user
) )

View File

@@ -88,12 +88,12 @@ class ResolvedSmartLinkView(DocumentListView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_view, obj=self.document, permissions=(permission_document_view,),
user=request.user user=request.user
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.smart_link, permissions=permission_smart_link_view, obj=self.smart_link, permissions=(permission_smart_link_view,),
user=request.user user=request.user
) )
@@ -109,8 +109,9 @@ class ResolvedSmartLinkView(DocumentListView):
try: try:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.smart_link, permissions=permission_smart_link_edit, obj=self.smart_link,
user=self.request.user, permissions=(permission_smart_link_edit,),
user=self.request.user
) )
except PermissionDenied: except PermissionDenied:
pass pass
@@ -205,7 +206,7 @@ class DocumentSmartLinkListView(SmartLinkListView):
self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_view, obj=self.document, permissions=(permission_document_view,),
user=request.user user=request.user
) )
@@ -319,7 +320,8 @@ class SmartLinkConditionCreateView(SingleObjectCreateView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_smart_link(), permissions=permission_smart_link_edit, obj=self.get_smart_link(),
permissions=(permission_smart_link_edit,),
user=request.user user=request.user
) )
@@ -359,7 +361,7 @@ class SmartLinkConditionEditView(SingleObjectEditView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_object().smart_link, obj=self.get_object().smart_link,
permissions=permission_smart_link_edit, user=request.user permissions=(permission_smart_link_edit,), user=request.user
) )
return super( return super(
@@ -388,7 +390,7 @@ class SmartLinkConditionDeleteView(SingleObjectDeleteView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.get_object().smart_link, obj=self.get_object().smart_link,
permissions=permission_smart_link_edit, user=request.user permissions=(permission_smart_link_edit,), user=request.user
) )
return super( return super(

View File

@@ -85,8 +85,8 @@ class MailDocumentView(MultipleObjectFormActionView):
def object_action(self, form, instance): def object_action(self, form, instance):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_user_mailer_use, user=self.request.user, obj=form.cleaned_data['user_mailer'],
obj=form.cleaned_data['user_mailer'] permissions=(permission_user_mailer_use,), user=self.request.user
) )
task_send_document.apply_async( task_send_document.apply_async(
@@ -261,8 +261,8 @@ class UserMailerTestView(FormView):
def get_object(self): def get_object(self):
user_mailer = get_object_or_404(klass=UserMailer, pk=self.kwargs['pk']) user_mailer = get_object_or_404(klass=UserMailer, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_user_mailer_use, user=self.request.user, obj=user_mailer, permissions=(permission_user_mailer_use,),
obj=user_mailer user=self.request.user
) )
return user_mailer return user_mailer

View File

@@ -43,8 +43,8 @@ class APIDocumentMetadataListView(generics.ListCreateAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=document, permissions=(permission_required,),
obj=document user=self.request.user
) )
return document return document
@@ -103,8 +103,8 @@ class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=document, permissions=(permission_required,),
obj=document user=self.request.user
) )
return document return document
@@ -175,8 +175,8 @@ class APIDocumentTypeMetadataTypeListView(generics.ListCreateAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=document_type, permissions=(permission_required,),
obj=document_type user=self.request.user
) )
return document_type return document_type
@@ -232,8 +232,8 @@ class APIDocumentTypeMetadataTypeView(generics.RetrieveUpdateDestroyAPIView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user, obj=document_type, permissions=(permission_required,),
obj=document_type user=self.request.user
) )
return document_type return document_type

View File

@@ -397,8 +397,9 @@ class DocumentMetadataEditView(MultipleObjectFormActionView):
class DocumentMetadataListView(SingleObjectListView): class DocumentMetadataListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_metadata_document_view, obj=self.get_document(),
user=self.request.user, obj=self.get_document() permissions=(permission_metadata_document_view,),
user=self.request.user
) )
return super(DocumentMetadataListView, self).dispatch( return super(DocumentMetadataListView, self).dispatch(
@@ -725,8 +726,8 @@ class SetupDocumentTypeMetadataTypes(FormView):
obj = get_object_or_404(klass=self.model, pk=self.kwargs['pk']) obj = get_object_or_404(klass=self.model, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission_metadata_type_edit,), obj=obj, permissions=(permission_metadata_type_edit,),
user=self.request.user, obj=obj user=self.request.user
) )
return obj return obj

View File

@@ -48,7 +48,7 @@ class Link(object):
conditional_disable=None, description=None, html_data=None, conditional_disable=None, description=None, html_data=None,
html_extra_classes=None, icon_class=None, icon_class_path=None, html_extra_classes=None, icon_class=None, icon_class_path=None,
keep_query=False, kwargs=None, name=None, permissions=None, keep_query=False, kwargs=None, name=None, permissions=None,
permissions_related=None, remove_from_query=None, tags=None, url=None remove_from_query=None, tags=None, url=None
): ):
self.args = args or [] self.args = args or []
self.badge_text = badge_text self.badge_text = badge_text
@@ -62,7 +62,6 @@ class Link(object):
self.kwargs = kwargs or {} self.kwargs = kwargs or {}
self.name = name self.name = name
self.permissions = permissions or [] self.permissions = permissions or []
self.permissions_related = permissions_related
self.remove_from_query = remove_from_query or [] self.remove_from_query = remove_from_query or []
self.tags = tags self.tags = tags
self.text = text self.text = text
@@ -117,13 +116,13 @@ class Link(object):
try: try:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=resolved_object, permissions=self.permissions, obj=resolved_object, permissions=self.permissions,
related=self.permissions_related, user=request.user user=request.user
) )
except PermissionDenied: except PermissionDenied:
return None return None
else: else:
try: try:
Permission.check_permissions( Permission.check_user_permissions(
permissions=self.permissions, user=request.user permissions=self.permissions, user=request.user
) )
except PermissionDenied: except PermissionDenied:
@@ -567,7 +566,7 @@ class SourceColumn(object):
except KeyError: except KeyError:
try: try:
# Might be a subclass, try its root class # Might be a subclass, try its root class
result.extend(cls._registry[source.__class__.__mro__[-2]]) result = cls._registry[source.__class__.__mro__[-2]]
except KeyError: except KeyError:
try: try:
# Might be an inherited class insance, try its source class # Might be an inherited class insance, try its source class

View File

@@ -27,7 +27,7 @@ def get_cascade_condition(app_label, model_name, object_permission, view_permiss
if view_permission: if view_permission:
try: try:
Permission.check_permissions( Permission.check_user_permissions(
permissions=(view_permission,), user=context.request.user permissions=(view_permission,), user=context.request.user
) )
except PermissionDenied: except PermissionDenied:

View File

@@ -73,7 +73,8 @@ class Permission(object):
) )
@classmethod @classmethod
def check_permissions(cls, permissions, user): def check_user_permissions(cls, permissions, user):
# TODO: Remove list check. Add permissions arguments will be lists.
try: try:
for permission in permissions: for permission in permissions:
if permission.stored_permission.user_has_this(user=user): if permission.stored_permission.user_has_this(user=user):

View File

@@ -26,7 +26,7 @@ class PermissionTestCase(GroupTestMixin, PermissionTestMixin, RoleTestMixin, Use
def test_no_permissions(self): def test_no_permissions(self):
with self.assertRaises(PermissionDenied): with self.assertRaises(PermissionDenied):
Permission.check_permissions( Permission.check_user_permissions(
permissions=(self.test_permission,), user=self.test_user permissions=(self.test_permission,), user=self.test_user
) )
@@ -36,7 +36,7 @@ class PermissionTestCase(GroupTestMixin, PermissionTestMixin, RoleTestMixin, Use
self.test_role.groups.add(self.test_group) self.test_role.groups.add(self.test_group)
try: try:
Permission.check_permissions( Permission.check_user_permissions(
permissions=(self.test_permission,), user=self.test_user permissions=(self.test_permission,), user=self.test_user
) )
except PermissionDenied: except PermissionDenied:

View File

@@ -12,14 +12,14 @@ from mayan.apps.permissions import Permission
class MayanPermission(BasePermission): class MayanPermission(BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
required_permission = getattr( required_permissions = getattr(
view, 'mayan_view_permissions', {} view, 'mayan_view_permissions', {}
).get(request.method, None) ).get(request.method, None)
if required_permission: if required_permissions:
try: try:
Permission.check_permissions( Permission.check_user_permissions(
permissions=required_permission, user=request.user permissions=required_permissions, user=request.user
) )
except PermissionDenied: except PermissionDenied:
return False return False
@@ -29,22 +29,15 @@ class MayanPermission(BasePermission):
return True return True
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
required_permission = getattr( required_permissions = getattr(
view, 'mayan_object_permissions', {} view, 'mayan_object_permissions', {}
).get(request.method, None) ).get(request.method, None)
if required_permission: if required_permissions:
try: try:
if hasattr(view, 'mayan_permission_attribute_check'):
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=required_permission, obj=obj, permissions=required_permissions,
user=request.user, obj=obj, user=request.user
related=view.mayan_permission_attribute_check
)
else:
AccessControlList.objects.check_access(
permissions=required_permission, user=request.user,
obj=obj
) )
except PermissionDenied: except PermissionDenied:
return False return False

View File

@@ -248,7 +248,7 @@ class StagingFolderViewTestCase(GenericViewTestCase):
} }
) )
def test_staging_folder_delete_no_permission(self): def test_staging_file_delete_no_permission(self):
staging_folder = StagingFolderSource.objects.create( staging_folder = StagingFolderSource.objects.create(
label=TEST_SOURCE_LABEL, label=TEST_SOURCE_LABEL,
folder_path=self.temporary_directory, folder_path=self.temporary_directory,
@@ -263,11 +263,11 @@ class StagingFolderViewTestCase(GenericViewTestCase):
response = self._request_staging_file_delete_view( response = self._request_staging_file_delete_view(
staging_folder=staging_folder, staging_file=staging_file staging_folder=staging_folder, staging_file=staging_file
) )
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 404)
self.assertEqual(len(list(staging_folder.get_files())), 1) self.assertEqual(len(list(staging_folder.get_files())), 1)
def test_staging_folder_delete_with_permission(self): def test_staging_file_delete_with_permission(self):
self.grant_permission(permission=permission_staging_file_delete) self.grant_permission(permission=permission_staging_file_delete)
staging_folder = StagingFolderSource.objects.create( staging_folder = StagingFolderSource.objects.create(

View File

@@ -14,12 +14,13 @@ from django.utils.translation import ugettext_lazy as _
from mayan.apps.acls.models import AccessControlList from mayan.apps.acls.models import AccessControlList
from mayan.apps.checkouts.models import NewVersionBlock from mayan.apps.checkouts.models import NewVersionBlock
from mayan.apps.common.menus import menu_facet
from mayan.apps.common.models import SharedUploadedFile
from mayan.apps.common.generics import ( from mayan.apps.common.generics import (
ConfirmView, MultiFormView, SingleObjectCreateView, ConfirmView, MultiFormView, SingleObjectCreateView,
SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView
) )
from mayan.apps.common.menus import menu_facet
from mayan.apps.common.mixins import ExternalObjectMixin
from mayan.apps.common.models import SharedUploadedFile
from mayan.apps.documents.models import DocumentType, Document from mayan.apps.documents.models import DocumentType, Document
from mayan.apps.documents.permissions import ( from mayan.apps.documents.permissions import (
permission_document_create, permission_document_new_version permission_document_create, permission_document_new_version
@@ -205,7 +206,7 @@ class UploadInteractiveView(UploadBaseView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.document_type, permissions=permission_document_create, obj=self.document_type, permissions=(permission_document_create,),
user=request.user user=request.user
) )
@@ -386,7 +387,7 @@ class UploadInteractiveVersionView(UploadBaseView):
) )
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_new_version, obj=self.document, permissions=(permission_document_new_version,),
user=self.request.user user=self.request.user
) )
@@ -474,28 +475,22 @@ class UploadInteractiveVersionView(UploadBaseView):
return context return context
class StagingFileDeleteView(SingleObjectDeleteView): class StagingFileDeleteView(ExternalObjectMixin, SingleObjectDeleteView):
object_permission = permission_staging_file_delete external_object_class = StagingFolderSource
object_permission_related = 'staging_folder' external_object_permission = permission_staging_file_delete
def get_extra_context(self): def get_extra_context(self):
return { return {
'object': self.get_object(), 'object': self.object,
'object_name': _('Staging file'), 'object_name': _('Staging file'),
'source': self.get_source(), 'title': _('Delete staging file "%s"?') % self.object,
} }
def get_object(self): def get_object(self):
source = self.get_source() return self.external_object.get_file(
return source.get_file(
encoded_filename=self.kwargs['encoded_filename'] encoded_filename=self.kwargs['encoded_filename']
) )
def get_source(self):
return get_object_or_404(
klass=StagingFolderSource, pk=self.kwargs['pk']
)
# Setup views # Setup views
class SetupSourceCheckView(ConfirmView): class SetupSourceCheckView(ConfirmView):

View File

@@ -89,7 +89,7 @@ class APITagDocumentListView(generics.ListAPIView):
tag = get_object_or_404(klass=Tag, pk=self.kwargs['pk']) tag = get_object_or_404(klass=Tag, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_tag_view, user=self.request.user, obj=tag obj=tag, permissions=(permission_tag_view,), user=self.request.user
) )
return tag.documents.all() return tag.documents.all()
@@ -113,8 +113,8 @@ class APIDocumentTagListView(generics.ListCreateAPIView):
document = self.get_document() document = self.get_document()
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user, obj=document, permissions=(permission_document_view,),
obj=document user=self.request.user
) )
return document.attached_tags().all() return document.attached_tags().all()
@@ -165,8 +165,8 @@ class APIDocumentTagView(generics.RetrieveDestroyAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['document_pk']) document = get_object_or_404(klass=Document, pk=self.kwargs['document_pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user, obj=document, permissions=(permission_document_view,),
obj=document user=self.request.user
) )
return document return document

View File

@@ -112,7 +112,7 @@ class NewDocumentTagSerializer(serializers.Serializer):
try: try:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=tag, permissions=permission_tag_attach, obj=tag, permissions=(permission_tag_attach,),
user=self.context['request'].user user=self.context['request'].user
) )
except PermissionDenied: except PermissionDenied:

View File

@@ -100,7 +100,7 @@ class TagAttachActionView(MultipleObjectFormActionView):
for tag in form.cleaned_data['tags']: for tag in form.cleaned_data['tags']:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=tag, permissions=permission_tag_attach, obj=tag, permissions=(permission_tag_attach,),
user=self.request.user user=self.request.user
) )
@@ -251,7 +251,7 @@ class DocumentTagListView(TagListView):
self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk']) self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_view, obj=self.document, permissions=(permission_document_view,),
user=request.user, user=request.user,
) )
@@ -348,7 +348,7 @@ class TagRemoveActionView(MultipleObjectFormActionView):
for tag in form.cleaned_data['tags']: for tag in form.cleaned_data['tags']:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
obj=tag, permissions=permission_tag_remove, obj=tag, permissions=(permission_tag_remove,),
user=self.request.user user=self.request.user
) )

View File

@@ -149,8 +149,7 @@ class APIUserGroupList(generics.ListCreateAPIView):
user = get_object_or_404(klass=get_user_model(), pk=self.kwargs['pk']) user = get_object_or_404(klass=get_user_model(), pk=self.kwargs['pk'])
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
permissions=(permission,), user=self.request.user, obj=user, permissions=(permission,), user=self.request.user
obj=user
) )
return user return user