diff --git a/mayan/apps/document_signatures/apps.py b/mayan/apps/document_signatures/apps.py index e8b2468e04..6be04810e7 100644 --- a/mayan/apps/document_signatures/apps.py +++ b/mayan/apps/document_signatures/apps.py @@ -12,7 +12,6 @@ from acls import ModelPermission from common import ( MayanAppConfig, menu_object, menu_sidebar ) -from common.widgets import two_state_template from mayan.celery import app from navigation import SourceColumn @@ -56,8 +55,6 @@ class DocumentSignaturesApp(MayanAppConfig): app_label='django_gpg', model_name='Key' ) - DetachedSignature = self.get_model('DetachedSignature') - EmbeddedSignature = self.get_model('EmbeddedSignature') SignatureBaseModel = self.get_model('SignatureBaseModel') diff --git a/mayan/apps/document_signatures/forms.py b/mayan/apps/document_signatures/forms.py index b323b1abf6..09139f78cd 100644 --- a/mayan/apps/document_signatures/forms.py +++ b/mayan/apps/document_signatures/forms.py @@ -9,12 +9,6 @@ from django_gpg.models import Key from .models import SignatureBaseModel -class DetachedSignatureForm(forms.Form): - file = forms.FileField( - label=_('Signature file'), - ) - - class DocumentVersionSignatureDetailForm(DetailForm): def __init__(self, *args, **kwargs): extra_fields = ( @@ -70,7 +64,9 @@ class DocumentVersionSignatureDetailForm(DetailForm): ) kwargs['extra_fields'] = extra_fields - super(DocumentVersionSignatureDetailForm, self).__init__(*args, **kwargs) + super( + DocumentVersionSignatureDetailForm, self + ).__init__(*args, **kwargs) class Meta: fields = () diff --git a/mayan/apps/document_signatures/models.py b/mayan/apps/document_signatures/models.py index 197ab83103..2a7b3bff95 100644 --- a/mayan/apps/document_signatures/models.py +++ b/mayan/apps/document_signatures/models.py @@ -98,7 +98,9 @@ class EmbeddedSignature(SignatureBaseModel): with self.document_version.open(raw=raw) as file_object: try: - verify_result = Key.objects.verify_file(file_object=file_object) + verify_result = Key.objects.verify_file( + file_object=file_object + ) except VerificationError as exception: # Not signed logger.debug( @@ -113,6 +115,7 @@ class EmbeddedSignature(SignatureBaseModel): super(EmbeddedSignature, self).save(*args, **kwargs) +@python_2_unicode_compatible class DetachedSignature(SignatureBaseModel): signature_file = models.FileField( blank=True, null=True, storage=storage_backend, upload_to=upload_to, @@ -123,6 +126,9 @@ class DetachedSignature(SignatureBaseModel): verbose_name = _('Document version detached signature') verbose_name_plural = _('Document version detached signatures') + def __str__(self): + return '{}-{}'.format(self.document_version, _('signature')) + def delete(self, *args, **kwargs): self.signature_file.storage.delete(self.signature_file.name) super(DetachedSignature, self).delete(*args, **kwargs) diff --git a/mayan/apps/document_signatures/tests/test_views.py b/mayan/apps/document_signatures/tests/test_views.py index 85088784ba..eaa1f82ed2 100644 --- a/mayan/apps/document_signatures/tests/test_views.py +++ b/mayan/apps/document_signatures/tests/test_views.py @@ -2,8 +2,9 @@ from __future__ import absolute_import, unicode_literals from django.core.files import File +from django_downloadview.test import assert_download_response + from django_gpg.models import Key -from documents.permissions import permission_document_view from documents.tests.literals import TEST_DOCUMENT_PATH from documents.tests.test_views import GenericDocumentViewTestCase from user_management.tests import ( @@ -168,3 +169,51 @@ class SignaturesViewTestCase(GenericDocumentViewTestCase): self.assertEqual(response.status_code, 302) self.assertEqual(DetachedSignature.objects.count(), 1) + + def test_signature_download_view_no_permission(self): + with open(TEST_DOCUMENT_PATH) as file_object: + document = self.document_type.new_document( + file_object=file_object + ) + + with open(TEST_SIGNATURE_FILE_PATH) as file_object: + signature = DetachedSignature.objects.create( + document_version=document.latest_version, + signature_file=File(file_object) + ) + + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + response = self.get( + 'signatures:document_version_signature_download', + args=(signature.pk,), + ) + + self.assertEqual(response.status_code, 403) + + def test_signature_download_view_with_permission(self): + with open(TEST_DOCUMENT_PATH) as file_object: + document = self.document_type.new_document( + file_object=file_object + ) + + with open(TEST_SIGNATURE_FILE_PATH) as file_object: + signature = DetachedSignature.objects.create( + document_version=document.latest_version, + signature_file=File(file_object) + ) + + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + self.role.permissions.add( + permission_document_version_signature_download.stored_permission + ) + + response = self.get( + 'signatures:document_version_signature_download', + args=(signature.pk,), + ) + + assert_download_response( + self, response=response, content=signature.signature_file.read(), + ) diff --git a/mayan/apps/document_signatures/urls.py b/mayan/apps/document_signatures/urls.py index 1695d51de3..971b615877 100644 --- a/mayan/apps/document_signatures/urls.py +++ b/mayan/apps/document_signatures/urls.py @@ -4,11 +4,12 @@ from django.conf.urls import patterns, url from .views import ( DocumentVersionSignatureDeleteView, DocumentVersionSignatureDetailView, - DocumentVersionSignatureListView, DocumentVersionSignatureUploadView + DocumentSignatureDownloadView, DocumentVersionSignatureListView, + DocumentVersionSignatureUploadView ) urlpatterns = patterns( - 'document_signatures.views', + '', url( r'^(?P\d+)/details/$', DocumentVersionSignatureDetailView.as_view(), @@ -16,7 +17,7 @@ urlpatterns = patterns( ), url( r'^signature/(?P\d+)/download/$', - 'document_signature_download', + DocumentSignatureDownloadView.as_view(), name='document_version_signature_download' ), url( diff --git a/mayan/apps/document_signatures/views.py b/mayan/apps/document_signatures/views.py index e1703ce687..f05b483983 100644 --- a/mayan/apps/document_signatures/views.py +++ b/mayan/apps/document_signatures/views.py @@ -2,25 +2,20 @@ from __future__ import absolute_import, unicode_literals import logging -from django.conf import settings -from django.contrib import messages from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse -from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext +from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ from acls.models import AccessControlList from common.generics import ( SingleObjectCreateView, SingleObjectDeleteView, SingleObjectDetailView, - SingleObjectListView + SingleObjectDownloadView, SingleObjectListView ) from documents.models import DocumentVersion -from filetransfers.api import serve_file from permissions import Permission -from .forms import DetachedSignatureForm, DocumentVersionSignatureDetailForm +from .forms import DocumentVersionSignatureDetailForm from .models import DetachedSignature, SignatureBaseModel from .permissions import ( permission_document_version_signature_view, @@ -34,12 +29,16 @@ logger = logging.getLogger(__name__) class DocumentVersionSignatureDeleteView(SingleObjectDeleteView): model = DetachedSignature + object_permission = permission_document_version_signature_delete + object_permission_related = 'document_version.document' def get_extra_context(self): return { 'document': self.get_object().document_version.document, 'document_version': self.get_object().document_version, - 'navigation_object_list': ('document', 'document_version', 'signature'), + 'navigation_object_list': ( + 'document', 'document_version', 'signature' + ), 'signature': self.get_object(), 'title': _('Delete detached signature: %s') % self.get_object() } @@ -61,7 +60,9 @@ class DocumentVersionSignatureDetailView(SingleObjectDetailView): 'document': self.get_object().document_version.document, 'document_version': self.get_object().document_version, 'signature': self.get_object(), - 'navigation_object_list': ('document', 'document_version', 'signature'), + 'navigation_object_list': ( + 'document', 'document_version', 'signature' + ), 'hide_object': True, 'title': _( 'Details for signature: %s' @@ -72,6 +73,19 @@ class DocumentVersionSignatureDetailView(SingleObjectDetailView): return SignatureBaseModel.objects.select_subclasses() +class DocumentSignatureDownloadView(SingleObjectDownloadView): + model = DetachedSignature + object_permission = permission_document_version_signature_download + object_permission_related = 'document_version.document' + + def get_file(self): + signature = self.get_object() + + return DocumentSignatureDownloadView.VirtualFile( + signature.signature_file, name=unicode(signature) + ) + + class DocumentVersionSignatureListView(SingleObjectListView): object_permission = permission_document_version_signature_view object_permission_related = 'document_version.document' @@ -95,11 +109,14 @@ class DocumentVersionSignatureListView(SingleObjectListView): try: Permission.check_permissions( - self.request.user, (permission_document_version_signature_view,) + self.request.user, ( + permission_document_version_signature_view, + ) ) except PermissionDenied: return AccessControlList.objects.filter_by_access( - permission_document_version_signature_view, self.request.user, queryset + permission_document_version_signature_view, self.request.user, + queryset ) else: return queryset @@ -145,24 +162,3 @@ class DocumentVersionSignatureUploadView(SingleObjectCreateView): 'signatures:document_version_signature_list', args=(self.get_document_version().pk,) ) - - -def document_signature_download(request, pk): - signature = get_object_or_404(DetachedSignature, pk=pk) - - try: - Permission.check_permissions( - request.user, (permission_document_version_signature_download,) - ) - except PermissionDenied: - AccessControlList.objects.check_access( - permission_document_version_signature_download, request.user, - signature.document_version.signature - ) - - return serve_file( - request, - signature, - save_as='"%s.sig"' % signature.document_version.document, - content_type='application/octet-stream' - )