Require view permissions for tag and document

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
Roberto Rosario
2019-05-08 16:55:54 -04:00
parent d533e48f46
commit 0b68463ed5
8 changed files with 642 additions and 267 deletions

View File

@@ -247,6 +247,8 @@
* Move the user set password views to the authentication app. * Move the user set password views to the authentication app.
* All views redirect to common's home view instead of the * All views redirect to common's home view instead of the
REDIRECT_URL setting. REDIRECT_URL setting.
* Update tag document list and the document tag list
views to require the view permissions for both objects.
3.1.11 (2019-04-XX) 3.1.11 (2019-04-XX)
=================== ===================

File diff suppressed because it is too large Load Diff

View File

@@ -68,9 +68,7 @@ class TagsApp(MayanAppConfig):
DocumentTag = self.get_model(model_name='DocumentTag') DocumentTag = self.get_model(model_name='DocumentTag')
Tag = self.get_model(model_name='Tag') Tag = self.get_model(model_name='Tag')
Document.add_to_class( Document.add_to_class(name='get_tags', value=method_document_get_tags)
name='attached_tags', value=method_document_get_tags
)
ModelEventType.register( ModelEventType.register(
model=Tag, event_types=( model=Tag, event_types=(

View File

@@ -4,12 +4,19 @@ from django.apps import apps
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
def method_document_get_tags(self): def method_document_get_tags(self, permission, user):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
)
DocumentTag = apps.get_model(app_label='tags', model_name='DocumentTag') DocumentTag = apps.get_model(app_label='tags', model_name='DocumentTag')
return DocumentTag.objects.filter(documents=self) return AccessControlList.objects.restrict_queryset(
permission=permission,
queryset=DocumentTag.objects.filter(documents=self), user=user
)
method_document_get_tags.help_text = _( method_document_get_tags.help_text = _(
'Return a the tags attached to the document.' 'Return a the tags attached to the document.'
) )
method_document_get_tags.short_description = _('get_tags()')

View File

@@ -64,11 +64,21 @@ class Tag(models.Model):
The count if filtered by access. The count if filtered by access.
""" """
queryset = AccessControlList.objects.restrict_queryset( queryset = AccessControlList.objects.restrict_queryset(
permission_document_view, user, queryset=self.documents permission=permission_document_view, queryset=self.documents,
user=user
) )
return queryset.count() return queryset.count()
def get_documents(self, user):
"""
Return a filtered queryset documents that have this tag attached.
"""
return AccessControlList.objects.restrict_queryset(
permission=permission_document_view, queryset=self.documents.all(),
user=user
)
def get_preview_widget(self): def get_preview_widget(self):
return widget_single_tag(tag=self) return widget_single_tag(tag=self)
get_preview_widget.short_description = _('Preview') get_preview_widget.short_description = _('Preview')

View File

@@ -110,8 +110,13 @@ class TagViewTestMixin(object):
} }
) )
def _request_test_tag_list_view(self):
return self.get(viewname='tags:tag_list')
def _request_test_tag_document_list_view(self): def _request_test_tag_document_list_view(self):
return self.get(viewname='documents:document_list') return self.get(
viewname='tags:tag_document_list', kwargs={'pk': self.test_tag.pk}
)
def _request_test_document_tag_attach_view(self): def _request_test_document_tag_attach_view(self):
return self.post( return self.post(
@@ -146,3 +151,10 @@ class TagViewTestMixin(object):
'tags': self.test_tag.pk, 'tags': self.test_tag.pk,
} }
) )
def _request_test_document_tag_list_view(self):
return self.get(
viewname='tags:document_tag_list', kwargs={
'pk': self.test_document.pk
}
)

View File

@@ -1,5 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.encoding import force_text
from mayan.apps.common.tests import GenericViewTestCase from mayan.apps.common.tests import GenericViewTestCase
from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.permissions import permission_document_view
from mayan.apps.documents.tests import GenericDocumentViewTestCase from mayan.apps.documents.tests import GenericDocumentViewTestCase
@@ -100,8 +102,27 @@ class TagViewTestCase(TagTestMixin, TagViewTestMixin, GenericViewTestCase):
self.test_tag.refresh_from_db() self.test_tag.refresh_from_db()
self.assertNotEqual(self.test_tag.label, tag_label) self.assertNotEqual(self.test_tag.label, tag_label)
def test_tag_list_view_with_no_permission(self):
self._create_test_tag()
response = self._request_test_tag_list_view()
self.assertNotContains(
response=response, text=self.test_tag.label, status_code=200
)
def test_tag_list_view_with_access(self):
self._create_test_tag()
self.grant_access(obj=self.test_tag, permission=permission_tag_view)
response = self._request_test_tag_list_view()
self.assertContains(
response=response, text=self.test_tag.label, status_code=200
)
class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentViewTestCase): class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentViewTestCase):
"""
def test_document_tags_widget_no_permissions(self): def test_document_tags_widget_no_permissions(self):
self._create_test_tag() self._create_test_tag()
@@ -126,6 +147,58 @@ class TagDocumentViewTestCase(TagTestMixin, TagViewTestMixin, GenericDocumentVie
self.assertContains( self.assertContains(
response=response, text=self.test_tag.label, status_code=200 response=response, text=self.test_tag.label, status_code=200
) )
"""
def test_document_tags_list_no_permissions(self):
self._create_test_tag()
self.test_tag.documents.add(self.test_document)
response = self._request_test_document_tag_list_view()
self.assertNotContains(
response=response, text=force_text(self.test_tag), status_code=404
)
def test_document_tags_list_with_document_access(self):
self._create_test_tag()
self.test_tag.documents.add(self.test_document)
self.grant_access(
obj=self.test_document, permission=permission_tag_view
)
response = self._request_test_document_tag_list_view()
self.assertNotContains(
response=response, text=force_text(self.test_tag), status_code=200
)
def test_document_tags_list_with_tag_access(self):
self._create_test_tag()
self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.test_tag, permission=permission_tag_view)
response = self._request_test_document_tag_list_view()
self.assertNotContains(
response=response, text=force_text(self.test_tag), status_code=404
)
def test_document_tags_list_with_full_access(self):
self._create_test_tag()
self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.test_tag, permission=permission_tag_view)
self.grant_access(
obj=self.test_document, permission=permission_tag_view
)
response = self._request_test_document_tag_list_view()
self.assertContains(
response=response, text=force_text(self.test_tag), status_code=200
)
def test_document_attach_tag_view_no_permission(self): def test_document_attach_tag_view_no_permission(self):
self._create_test_tag() self._create_test_tag()

View File

@@ -13,6 +13,7 @@ from mayan.apps.common.generics import (
MultipleObjectFormActionView, MultipleObjectConfirmActionView, MultipleObjectFormActionView, MultipleObjectConfirmActionView,
SingleObjectCreateView, SingleObjectEditView, SingleObjectListView SingleObjectCreateView, SingleObjectEditView, SingleObjectListView
) )
from mayan.apps.common.mixins import ExternalObjectMixin
from mayan.apps.documents.models import Document from mayan.apps.documents.models import Document
from mayan.apps.documents.views import DocumentListView from mayan.apps.documents.views import DocumentListView
from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.permissions import permission_document_view
@@ -96,7 +97,9 @@ class TagAttachActionView(MultipleObjectFormActionView):
return super(TagAttachActionView, self).get_post_action_redirect() return super(TagAttachActionView, self).get_post_action_redirect()
def object_action(self, form, instance): def object_action(self, form, instance):
attached_tags = instance.attached_tags() attached_tags = instance.get_tags(
permission=permission_tag_attach, user=self.request.user
)
for tag in form.cleaned_data['tags']: for tag in form.cleaned_data['tags']:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(
@@ -227,12 +230,13 @@ class TagListView(SingleObjectListView):
return Tag.objects.all() return Tag.objects.all()
class TagDocumentListView(DocumentListView): class TagDocumentListView(ExternalObjectMixin, DocumentListView):
def get_tag(self): external_object_class = Tag
return get_object_or_404(klass=Tag, pk=self.kwargs['pk']) external_object_permission = permission_tag_view
external_object_pk_url_kwarg = 'pk'
def get_document_queryset(self): def get_document_queryset(self):
return self.get_tag().documents.all() return self.get_tag().get_documents(user=self.request.user).all()
def get_extra_context(self): def get_extra_context(self):
context = super(TagDocumentListView, self).get_extra_context() context = super(TagDocumentListView, self).get_extra_context()
@@ -244,39 +248,38 @@ class TagDocumentListView(DocumentListView):
) )
return context return context
def get_tag(self):
return self.get_external_object()
class DocumentTagListView(TagListView):
def dispatch(self, request, *args, **kwargs):
self.document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access( class DocumentTagListView(ExternalObjectMixin, TagListView):
obj=self.document, permissions=(permission_document_view,), external_object_class = Document
user=request.user, external_object_permission = permission_tag_view
) external_object_pk_url_kwarg = 'pk'
return super(DocumentTagListView, self).dispatch(
request, *args, **kwargs
)
def get_extra_context(self): def get_extra_context(self):
context = super(DocumentTagListView, self).get_extra_context() context = super(DocumentTagListView, self).get_extra_context()
context.update( context.update(
{ {
'hide_link': True, 'hide_link': True,
'no_results_title': _('Document has no tags attached'),
'no_results_main_link': link_document_tag_multiple_attach.resolve( 'no_results_main_link': link_document_tag_multiple_attach.resolve(
context=RequestContext( context=RequestContext(
request=self.request, dict_={'object': self.document} self.request, {'object': self.get_external_object()}
) )
), ),
'object': self.document, 'no_results_title': _('Document has no tags attached'),
'title': _('Tags for document: %s') % self.document, 'object': self.get_external_object(),
'title': _(
'Tags for document: %s'
) % self.get_external_object(),
} }
) )
return context return context
def get_tag_queryset(self): def get_source_queryset(self):
return self.document.attached_tags().all() return self.get_external_object().get_tags(
permission=permission_tag_view, user=self.request.user
).all()
class TagRemoveActionView(MultipleObjectFormActionView): class TagRemoveActionView(MultipleObjectFormActionView):
@@ -343,7 +346,9 @@ class TagRemoveActionView(MultipleObjectFormActionView):
return super(TagRemoveActionView, self).get_post_action_redirect() return super(TagRemoveActionView, self).get_post_action_redirect()
def object_action(self, form, instance): def object_action(self, form, instance):
attached_tags = instance.attached_tags() attached_tags = instance.get_tags(
permission=permission_tag_remove, user=self.request.user
)
for tag in form.cleaned_data['tags']: for tag in form.cleaned_data['tags']:
AccessControlList.objects.check_access( AccessControlList.objects.check_access(