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.
* Remove the licenses.py module and replace
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)
===================

View File

@@ -245,6 +245,16 @@ Other changes
* Add new app to handle all dependencies.
* Remove the licenses.py module and replace
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
--------

View File

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

View File

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

View File

@@ -8,8 +8,8 @@ logger = logging.getLogger(__name__)
class ModelPermission(object):
_functions = {}
_inheritances = {}
_proxies = {}
_registry = {}
@classmethod
@@ -63,24 +63,23 @@ class ModelPermission(object):
if class_permissions:
permissions.extend(class_permissions)
proxy = cls._proxies.get(type(instance))
if proxy:
permissions.extend(cls._registry.get(proxy))
pks = [
permission.stored_permission.pk for permission in set(permissions)
]
return StoredPermission.objects.filter(pk__in=pks)
@classmethod
def register_proxy(cls, source, model):
cls._proxies[model] = source
@classmethod
def register_inheritance(cls, model, related):
cls._inheritances[model] = related
def get_function(cls, model):
return cls._functions[model]
@classmethod
def get_inheritance(cls, 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
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(
icon_class=icon_acl_new, kwargs=get_kwargs_factory('resolved_object'),
permissions=(permission_acl_edit,), text=_('New ACL'),
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(
args='resolved_object.pk', icon_class=icon_acl_permissions,
permissions=(permission_acl_edit,),
permissions_related='content_object', text=_('Permissions'),
view='acls:acl_permissions',
text=_('Permissions'), view='acls:acl_permissions'
)

View File

@@ -1,17 +1,23 @@
from __future__ import absolute_import, unicode_literals
from functools import reduce
import logging
import operator
import warnings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
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.translation import ugettext
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.models import StoredPermission
@@ -26,170 +32,235 @@ class AccessControlListManager(models.Manager):
Implement a 3 tier permission system, involving a permissions, an actor
and an object
"""
def check_access(self, permissions, user, obj, related=None):
if user.is_superuser or user.is_staff:
logger.debug(
'Permissions "%s" on "%s" granted to user "%s" as superuser '
'or staff', permissions, obj, user
def _get_acl_filters(self, queryset, stored_permission, user, related_field_name=None):
"""
This method does the bulk of the work. It generates filters for the
AccessControlList model to determine if there are ACL entries for the
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:
return Permission.check_permissions(
permissions=permissions, user=user
if isinstance(related_field, GenericForeignKey):
# Case 3: Generic Foreign Key, multiple ContentTypes + object
# 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:
try:
stored_permissions = [
permission.stored_permission for permission in permissions
]
except TypeError:
# Not a list of permissions, just one
stored_permissions = (permissions.stored_permission,)
).values('ct_fk_combination')
if related:
obj = return_attrib(obj, related)
try:
parent_accessor = ModelPermission.get_inheritance(
model=obj._meta.model
acl_filter = self.annotate(
ct_fk_combination=Concat(
'content_type', V('-'), 'object_id', output_field=CharField()
)
except AttributeError:
# AttributeError means non model objects: ie Statistics
# These can't have ACLs so we raise PermissionDenied
).filter(
permissions=stored_permission, role__groups__user=user,
ct_fk_combination__in=content_type_object_id_queryset
).values('object_id')
# Force object to text to avoid UnicodeDecodeError
raise PermissionDenied(
ugettext('Insufficient access for: %s') % force_text(obj)
field_lookup = 'object_id__in'
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:
pass
else:
try:
return self.check_access(
obj=getattr(obj, parent_accessor),
permissions=permissions, user=user
relation_result = []
for related_field_model_related_field_name in related_field_model_related_fields:
related_field_name = '{}__{}'.format(related_field_name, related_field_model_related_field_name)
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:
# Has no such attribute, try it as a related field
if related_field_inherited_acl_queries:
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:
return self.check_access(
obj=return_related(
instance=obj, related_field=parent_accessor
), permissions=permissions, user=user
related_fields = (
ModelPermission.get_inheritance(
model=queryset.model
),
)
except PermissionDenied:
pass
except PermissionDenied:
except KeyError:
pass
else:
relation_result = []
user_roles = []
for group in user.groups.all():
for role in group.roles.all():
if set(stored_permissions).intersection(set(self.get_inherited_permissions(role=role, obj=obj))):
logger.debug(
'Permissions "%s" on "%s" granted to user "%s" through role "%s" via inherited ACL',
permissions, obj, user, role
for related_field_name in related_fields:
inherited_acl_queries = self._get_acl_filters(
queryset=queryset, stored_permission=stored_permission,
related_field_name=related_field_name, user=user
)
return True
if inherited_acl_queries:
relation_result.append(reduce(operator.and_, inherited_acl_queries))
user_roles.append(role)
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
if relation_result:
result.append(reduce(operator.or_, relation_result))
# Case 7: Has a function
try:
Permission.check_permissions(
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(
field_query_function = ModelPermission.get_function(
model=queryset.model
)
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
else:
result.append(entry.pk)
function_results = field_query_function()
return queryset.filter(pk__in=result)
else:
parent_acl_query = Q()
# Filter by the model's content type
content_type = ContentType.objects.get_for_model(
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
content_type = ContentType.objects.get_for_model(queryset.model)
acl_query = Q(pk__in=self.filter(
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
if 'acl_values' in function_results:
acl_queryset = acl_queryset.values(
*function_results['acl_values']
)
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:
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
def get_inherited_permissions(self, obj, role):

View File

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

View File

@@ -89,7 +89,7 @@ class GrantAccessAction(WorkflowAction):
try:
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:
raise ValidationError(exception)

View File

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

View File

@@ -73,8 +73,16 @@ class CabinetsApp(MayanAppConfig):
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(

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ class NewDocumentCheckoutSerializer(serializers.ModelSerializer):
document = Document.objects.get(pk=validated_data.pop('document_pk'))
AccessControlList.objects.check_access(
obj=document, permissions=permission_document_check_out,
obj=document, permissions=(permission_document_check_out,),
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'])
AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_check_out,
obj=self.document, permissions=(permission_document_check_out,),
user=request.user
)
@@ -168,13 +168,13 @@ class DocumentCheckinView(ConfirmView):
if document.get_check_out_info().user == self.request.user:
AccessControlList.objects.check_access(
obj=document, permissions=permission_document_check_in,
obj=document, permissions=(permission_document_check_in,),
user=self.request.user
)
else:
AccessControlList.objects.check_access(
obj=document,
permissions=permission_document_check_in_override,
permissions=(permission_document_check_in_override,),
user=self.request.user
)

View File

@@ -286,8 +286,8 @@ class ObjectListPermissionFilterMixin(object):
def dispatch(self, request, *args, **kwargs):
if self.access_object_retrieve_method and self.object_permission:
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)
@@ -296,7 +296,8 @@ class ObjectListPermissionFilterMixin(object):
if not self.access_object_retrieve_method and self.object_permission:
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:
return queryset
@@ -327,9 +328,10 @@ class ObjectPermissionCheckMixin(object):
def dispatch(self, request, *args, **kwargs):
if self.object_permission:
AccessControlList.objects.check_access(
permissions=self.object_permission, user=request.user,
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(
@@ -427,7 +429,7 @@ class ViewPermissionCheckMixin(object):
def dispatch(self, request, *args, **kwargs):
if self.view_permission:
Permission.check_permissions(
Permission.check_user_permissions(
permissions=(self.view_permission,), user=self.request.user
)

View File

@@ -33,6 +33,26 @@ def encapsulate(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):
"""
Resolve the attribute of model. Supports nested reference using dotted

View File

@@ -145,7 +145,7 @@ class ObjectErrorLogEntryListClearView(ConfirmView):
class ObjectErrorLogEntryListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs):
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
)

View File

@@ -5,3 +5,9 @@ class DatabaseWarning(UserWarning):
"""
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(
obj=self.content_object,
permissions=permission_transformation_create, user=request.user
permissions=(permission_transformation_create,), user=request.user
)
return super(TransformationCreateView, self).dispatch(
@@ -96,7 +96,7 @@ class TransformationDeleteView(SingleObjectDeleteView):
AccessControlList.objects.check_access(
obj=self.transformation.content_object,
permissions=permission_transformation_delete, user=request.user
permissions=(permission_transformation_delete,), user=request.user
)
return super(TransformationDeleteView, self).dispatch(
@@ -145,7 +145,7 @@ class TransformationEditView(SingleObjectEditView):
AccessControlList.objects.check_access(
obj=self.transformation.content_object,
permissions=permission_transformation_edit, user=request.user
permissions=(permission_transformation_edit,), user=request.user
)
return super(TransformationEditView, self).dispatch(
@@ -202,7 +202,7 @@ class TransformationListView(SingleObjectListView):
AccessControlList.objects.check_access(
obj=self.content_object,
permissions=permission_transformation_view, user=request.user
permissions=(permission_transformation_view,), user=request.user
)
return super(TransformationListView, self).dispatch(

View File

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

View File

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

View File

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

View File

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

View File

@@ -65,6 +65,7 @@ class DocumentSignaturesApp(MayanAppConfig):
app_label='django_gpg', model_name='Key'
)
DetachedSignature = self.get_model(model_name='DetachedSignature')
EmbeddedSignature = self.get_model(model_name='EmbeddedSignature')
SignatureBaseModel = self.get_model(model_name='SignatureBaseModel')
@@ -86,6 +87,12 @@ class DocumentSignaturesApp(MayanAppConfig):
permission_document_version_signature_upload,
)
)
ModelPermission.register_inheritance(
model=SignatureBaseModel, related='document_version'
)
ModelPermission.register_inheritance(
model=DetachedSignature, related='document_version'
)
SourceColumn(
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,
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_delete',
permissions=(permission_document_version_signature_delete,),
permissions_related='document_version.document', tags='dangerous',
text=_('Delete'), view='signatures:document_version_signature_delete',
tags='dangerous', text=_('Delete'),
view='signatures:document_version_signature_delete',
)
link_document_version_signature_details = Link(
args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_details',
permissions=(permission_document_version_signature_view,),
permissions_related='document_version.document', text=_('Details'),
view='signatures:document_version_signature_details',
text=_('Details'), view='signatures:document_version_signature_details',
)
link_document_version_signature_list = Link(
args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_list',
permissions=(permission_document_version_signature_view,),
permissions_related='document', text=_('Signatures'),
view='signatures:document_version_signature_list',
text=_('Signatures'), view='signatures:document_version_signature_list'
)
link_document_version_signature_download = Link(
args='resolved_object.pk', condition=is_detached_signature,
permissions=(permission_document_version_signature_download,),
permissions_related='document_version.document', text=_('Download'),
view='signatures:document_version_signature_download',
text=_('Download'), view='signatures:document_version_signature_download'
)
link_document_version_signature_upload = Link(
args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_upload',
permissions=(permission_document_version_signature_upload,),
permissions_related='document', text=_('Upload signature'),
view='signatures:document_version_signature_upload',
text=_('Upload signature'),
view='signatures:document_version_signature_upload'
)
link_document_version_signature_detached_create = Link(
args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_detached_create',
permissions=(permission_document_version_sign_detached,),
permissions_related='document', text=_('Sign detached'),
view='signatures:document_version_signature_detached_create',
text=_('Sign detached'),
view='signatures:document_version_signature_detached_create'
)
link_document_version_signature_embedded_create = Link(
args='resolved_object.pk',
icon_class_path='mayan.apps.document_signatures.icons.icon_document_version_signature_embedded_create',
permissions=(permission_document_version_sign_embedded,),
permissions_related='document', text=_('Sign embedded'),
view='signatures:document_version_signature_embedded_create',
text=_('Sign embedded'),
view='signatures:document_version_signature_embedded_create'
)

View File

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

View File

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

View File

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

View File

@@ -23,8 +23,8 @@ __all__ = (
class DocumentWorkflowInstanceListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=request.user,
obj=self.get_document()
obj=self.get_document(), permissions=(permission_workflow_view,),
user=request.user
)
return super(
@@ -58,8 +58,8 @@ class DocumentWorkflowInstanceListView(SingleObjectListView):
class WorkflowInstanceDetailView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs):
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(

View File

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

View File

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

View File

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

View File

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

View File

@@ -53,13 +53,13 @@ def task_clear_image_cache():
@app.task(ignore_result=True)
def task_delete_document(deleted_document_id):
def task_delete_document(trashed_document_id):
DeletedDocument = apps.get_model(
app_label='documents', model_name='DeletedDocument'
)
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()
logger.debug(msg='Finshed')

View File

@@ -9,7 +9,7 @@ from ..permissions import (
from .base import GenericDocumentViewTestCase
class DeletedDocumentTestCase(GenericDocumentViewTestCase):
class TrashedDocumentTestCase(GenericDocumentViewTestCase):
def _request_document_restore_view(self):
return self.post(
viewname='documents:document_restore', kwargs={
@@ -22,7 +22,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(Document.objects.count(), 0)
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(Document.objects.count(), 0)
@@ -50,7 +50,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
def test_document_trash_no_permissions(self):
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(Document.objects.count(), 1)
@@ -79,7 +79,7 @@ class DeletedDocumentTestCase(GenericDocumentViewTestCase):
self.assertEqual(DeletedDocument.objects.count(), 1)
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(DeletedDocument.objects.count(), 1)

View File

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

View File

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

View File

@@ -138,8 +138,9 @@ class DocumentTypeFilenameCreateView(SingleObjectCreateView):
def dispatch(self, request, *args, **kwargs):
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(

View File

@@ -32,8 +32,9 @@ logger = logging.getLogger(__name__)
class DocumentVersionListView(SingleObjectListView):
def dispatch(self, request, *args, **kwargs):
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)

View File

@@ -318,8 +318,8 @@ class DocumentDownloadView(SingleObjectDownloadView):
class DocumentDuplicatesListView(DocumentListView):
def dispatch(self, request, *args, **kwargs):
AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user,
obj=self.get_document()
obj=self.get_document(), permissions=(permission_document_view,),
user=self.request.user
)
return super(
@@ -582,8 +582,8 @@ class DocumentTransformationsCloneView(FormView):
instance = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_transformation_edit,
user=self.request.user, obj=instance
obj=instance, permissions=(permission_transformation_edit,),
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):
instance = self.get_object()
AccessControlList.objects.check_access(
permissions=permission_document_print, user=self.request.user,
obj=instance
obj=instance, permissions=(permission_document_print,),
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
from django.contrib import messages
from django.shortcuts import get_object_or_404
from django.urls import reverse, reverse_lazy
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _, ungettext
from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.generics import ConfirmView
from mayan.apps.common.mixins import MultipleInstanceActionMixin
from mayan.apps.common.generics import (
ConfirmView, MultipleObjectConfirmActionView
)
from ..icons import icon_document_list_deleted
from ..models import DeletedDocument, Document
@@ -23,64 +23,100 @@ from ..tasks import task_delete_document
from .document_views import DocumentListView
__all__ = (
'DeletedDocumentDeleteView', 'DeletedDocumentDeleteManyView',
'DeletedDocumentListView', 'DocumentRestoreView', 'DocumentRestoreManyView',
'DocumentTrashView', 'DocumentTrashManyView', 'EmptyTrashCanView'
'DocumentTrashView', 'EmptyTrashCanView', 'TrashedDocumentDeleteView',
'TrashedDocumentListView', 'TrashedDocumentRestoreView'
)
logger = logging.getLogger(__name__)
class DeletedDocumentDeleteView(ConfirmView):
extra_context = {
'title': _('Delete the selected document?')
class DocumentTrashView(MultipleObjectConfirmActionView):
model = 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):
source_document = get_object_or_404(
klass=Document.passthrough, pk=instance.pk
)
return result
AccessControlList.objects.check_access(
permissions=permission_document_delete, user=self.request.user,
obj=source_document
)
def object_action(self, form, instance):
instance.delete()
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):
instance = get_object_or_404(
klass=DeletedDocument, pk=self.kwargs['pk']
)
self.object_action(instance=instance)
messages.success(
self.request, _('Document: %(document)s deleted.') % {
'document': instance
}
for deleted_document in DeletedDocument.objects.all():
task_delete_document.apply_async(
kwargs={'trashed_document_id': deleted_document.pk}
)
messages.success(self.request, _('Trash emptied successfully'))
class DeletedDocumentDeleteManyView(MultipleInstanceActionMixin, DeletedDocumentDeleteView):
extra_context = {
'title': _('Delete the selected documents?')
}
class TrashedDocumentDeleteView(MultipleObjectConfirmActionView):
model = DeletedDocument
success_message = '%(count)d document deleted.'
success_message_plural = '%(count)d documents deleted.'
object_permission = permission_document_delete
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
def get_document_queryset(self):
return AccessControlList.objects.filter_by_access(
permission_document_view, self.request.user,
queryset=DeletedDocument.trash.all()
permission=permission_document_view,
queryset=DeletedDocument.trash.all(),
user=self.request.user
)
def get_extra_context(self):
context = super(DeletedDocumentListView, self).get_extra_context()
context = super(TrashedDocumentListView, self).get_extra_context()
context.update(
{
'hide_link': True,
@@ -99,103 +135,29 @@ class DeletedDocumentListView(DocumentListView):
return context
class DocumentRestoreView(ConfirmView):
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?')
}
class TrashedDocumentRestoreView(MultipleObjectConfirmActionView):
model = DeletedDocument
success_message = '%(count)d document restored.'
success_message_plural = '%(count)d documents 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
object_permission = permission_document_restore
pk_url_kwarg = 'pk'
success_message_singular = _(
'%(count)d trashed document restored.'
)
instance.delete()
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
}
success_message_plural = _(
'%(count)d trashed documents restored.'
)
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):
return {
'title': _('Move the selected documents to the trash?')
queryset = self.get_object_list()
result = {
'title': ungettext(
single='Restore the selected trashed document?',
plural='Restore the selected trashed documents?',
number=queryset.count()
)
}
return result
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):
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'))
def object_action(self, form, instance):
instance.restore()

View File

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

View File

@@ -113,8 +113,9 @@ class EventType(object):
if result.target:
try:
AccessControlList.objects.check_access(
permissions=permission_events_view,
user=user, obj=result.target
obj=result.target,
permissions=(permission_events_view,),
user=user
)
except PermissionDenied:
pass
@@ -139,8 +140,9 @@ class EventType(object):
if relationship.exists():
try:
AccessControlList.objects.check_access(
permissions=permission_events_view,
user=user, obj=result.target
obj=result.target,
permissions=(permission_events_view,),
user=user
)
except PermissionDenied:
pass
@@ -161,8 +163,9 @@ class EventType(object):
if relationship.exists():
try:
AccessControlList.objects.check_access(
permissions=permission_events_view,
user=user, obj=result.action_object
obj=result.action_object,
permissions=(permission_events_view,),
user=user
)
except PermissionDenied:
pass
@@ -191,7 +194,6 @@ class ModelEventType(object):
Class to allow matching a model to a specific set of events.
"""
_inheritances = {}
_proxies = {}
_registry = {}
@classmethod
@@ -211,11 +213,6 @@ class ModelEventType(object):
if class_events:
events.extend(class_events)
proxy = cls._proxies.get(type(instance))
if proxy:
events.extend(cls._registry.get(proxy))
pks = [
event.id for event in set(events)
]
@@ -237,7 +234,3 @@ class ModelEventType(object):
@classmethod
def register_inheritance(cls, 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
AccessControlList.objects.check_access(
obj=content_object, permissions=permission_events_view,
obj=content_object, permissions=(permission_events_view,),
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'])
AccessControlList.objects.check_access(
obj=document, permissions=permission_document_view,
obj=document, permissions=(permission_document_view,),
user=self.request.user
)
@@ -48,7 +48,7 @@ class APIResolvedSmartLinkDocumentListView(generics.ListAPIView):
)
AccessControlList.objects.check_access(
obj=smart_link, permissions=permission_smart_link_view,
obj=smart_link, permissions=(permission_smart_link_view,),
user=self.request.user
)
@@ -91,7 +91,7 @@ class APIResolvedSmartLinkView(generics.RetrieveAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=document, permissions=permission_document_view,
obj=document, permissions=(permission_document_view,),
user=self.request.user
)
@@ -128,7 +128,7 @@ class APIResolvedSmartLinkListView(generics.ListAPIView):
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=document, permissions=permission_document_view,
obj=document, permissions=(permission_document_view,),
user=self.request.user
)
@@ -187,7 +187,7 @@ class APISmartLinkConditionListView(generics.ListCreateAPIView):
smart_link = get_object_or_404(klass=SmartLink, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=smart_link, permissions=permission_required,
obj=smart_link, permissions=(permission_required,),
user=self.request.user
)
@@ -230,7 +230,7 @@ class APISmartLinkConditionView(generics.RetrieveUpdateDestroyAPIView):
smart_link = get_object_or_404(klass=SmartLink, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
obj=smart_link, permissions=permission_required,
obj=smart_link, permissions=(permission_required,),
user=self.request.user
)

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,7 +48,7 @@ class Link(object):
conditional_disable=None, description=None, html_data=None,
html_extra_classes=None, icon_class=None, icon_class_path=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.badge_text = badge_text
@@ -62,7 +62,6 @@ class Link(object):
self.kwargs = kwargs or {}
self.name = name
self.permissions = permissions or []
self.permissions_related = permissions_related
self.remove_from_query = remove_from_query or []
self.tags = tags
self.text = text
@@ -117,13 +116,13 @@ class Link(object):
try:
AccessControlList.objects.check_access(
obj=resolved_object, permissions=self.permissions,
related=self.permissions_related, user=request.user
user=request.user
)
except PermissionDenied:
return None
else:
try:
Permission.check_permissions(
Permission.check_user_permissions(
permissions=self.permissions, user=request.user
)
except PermissionDenied:
@@ -567,7 +566,7 @@ class SourceColumn(object):
except KeyError:
try:
# 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:
try:
# 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:
try:
Permission.check_permissions(
Permission.check_user_permissions(
permissions=(view_permission,), user=context.request.user
)
except PermissionDenied:

View File

@@ -73,7 +73,8 @@ class Permission(object):
)
@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:
for permission in permissions:
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):
with self.assertRaises(PermissionDenied):
Permission.check_permissions(
Permission.check_user_permissions(
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)
try:
Permission.check_permissions(
Permission.check_user_permissions(
permissions=(self.test_permission,), user=self.test_user
)
except PermissionDenied:

View File

@@ -12,14 +12,14 @@ from mayan.apps.permissions import Permission
class MayanPermission(BasePermission):
def has_permission(self, request, view):
required_permission = getattr(
required_permissions = getattr(
view, 'mayan_view_permissions', {}
).get(request.method, None)
if required_permission:
if required_permissions:
try:
Permission.check_permissions(
permissions=required_permission, user=request.user
Permission.check_user_permissions(
permissions=required_permissions, user=request.user
)
except PermissionDenied:
return False
@@ -29,22 +29,15 @@ class MayanPermission(BasePermission):
return True
def has_object_permission(self, request, view, obj):
required_permission = getattr(
required_permissions = getattr(
view, 'mayan_object_permissions', {}
).get(request.method, None)
if required_permission:
if required_permissions:
try:
if hasattr(view, 'mayan_permission_attribute_check'):
AccessControlList.objects.check_access(
permissions=required_permission,
user=request.user, obj=obj,
related=view.mayan_permission_attribute_check
)
else:
AccessControlList.objects.check_access(
permissions=required_permission, user=request.user,
obj=obj
obj=obj, permissions=required_permissions,
user=request.user
)
except PermissionDenied:
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(
label=TEST_SOURCE_LABEL,
folder_path=self.temporary_directory,
@@ -263,11 +263,11 @@ class StagingFolderViewTestCase(GenericViewTestCase):
response = self._request_staging_file_delete_view(
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)
def test_staging_folder_delete_with_permission(self):
def test_staging_file_delete_with_permission(self):
self.grant_permission(permission=permission_staging_file_delete)
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.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 (
ConfirmView, MultiFormView, SingleObjectCreateView,
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.permissions import (
permission_document_create, permission_document_new_version
@@ -205,7 +206,7 @@ class UploadInteractiveView(UploadBaseView):
)
AccessControlList.objects.check_access(
obj=self.document_type, permissions=permission_document_create,
obj=self.document_type, permissions=(permission_document_create,),
user=request.user
)
@@ -386,7 +387,7 @@ class UploadInteractiveVersionView(UploadBaseView):
)
AccessControlList.objects.check_access(
obj=self.document, permissions=permission_document_new_version,
obj=self.document, permissions=(permission_document_new_version,),
user=self.request.user
)
@@ -474,28 +475,22 @@ class UploadInteractiveVersionView(UploadBaseView):
return context
class StagingFileDeleteView(SingleObjectDeleteView):
object_permission = permission_staging_file_delete
object_permission_related = 'staging_folder'
class StagingFileDeleteView(ExternalObjectMixin, SingleObjectDeleteView):
external_object_class = StagingFolderSource
external_object_permission = permission_staging_file_delete
def get_extra_context(self):
return {
'object': self.get_object(),
'object': self.object,
'object_name': _('Staging file'),
'source': self.get_source(),
'title': _('Delete staging file "%s"?') % self.object,
}
def get_object(self):
source = self.get_source()
return source.get_file(
return self.external_object.get_file(
encoded_filename=self.kwargs['encoded_filename']
)
def get_source(self):
return get_object_or_404(
klass=StagingFolderSource, pk=self.kwargs['pk']
)
# Setup views
class SetupSourceCheckView(ConfirmView):

View File

@@ -89,7 +89,7 @@ class APITagDocumentListView(generics.ListAPIView):
tag = get_object_or_404(klass=Tag, pk=self.kwargs['pk'])
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()
@@ -113,8 +113,8 @@ class APIDocumentTagListView(generics.ListCreateAPIView):
document = self.get_document()
AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user,
obj=document
obj=document, permissions=(permission_document_view,),
user=self.request.user
)
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'])
AccessControlList.objects.check_access(
permissions=permission_document_view, user=self.request.user,
obj=document
obj=document, permissions=(permission_document_view,),
user=self.request.user
)
return document

View File

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

View File

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