From 302a90ff9da824155f7e7f6f51cad0736e50507a Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 15 Nov 2019 19:02:01 -0400 Subject: [PATCH] Add workflow action to sign documents Signed-off-by: Roberto Rosario --- HISTORY.rst | 5 ++ .../tests/test_workflow_actions.py | 41 ++++++++++ .../document_signatures/workflow_actions.py | 75 +++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 mayan/apps/document_signatures/tests/test_workflow_actions.py create mode 100644 mayan/apps/document_signatures/workflow_actions.py diff --git a/HISTORY.rst b/HISTORY.rst index c5499da4b8..21d6dde6d0 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -140,6 +140,11 @@ setting is not specified. - Refactor the initial setting bootstrap code. - Use timezone aware date for document statistics +- Show placeholder label on invalid action classes + Instead of throwing an error a sample label of + "Unknown action type" will be used and allow users to + delete the unknown state action. +- Add workflow action to sign documents. 3.2.10 (2019-XX-XX) =================== diff --git a/mayan/apps/document_signatures/tests/test_workflow_actions.py b/mayan/apps/document_signatures/tests/test_workflow_actions.py new file mode 100644 index 0000000000..686a05730c --- /dev/null +++ b/mayan/apps/document_signatures/tests/test_workflow_actions.py @@ -0,0 +1,41 @@ +from __future__ import unicode_literals + +from mayan.apps.django_gpg.tests.literals import TEST_KEY_PRIVATE_PASSPHRASE +from mayan.apps.django_gpg.tests.mixins import KeyTestMixin +from mayan.apps.document_states.tests.mixins import WorkflowTestMixin +from mayan.apps.documents.tests.base import GenericDocumentViewTestCase + +from ..models import DetachedSignature, EmbeddedSignature +from ..workflow_actions import ( + DocumentSignatureDetachedAction, DocumentSignatureEmbeddedAction +) + + +class DocumentSignatureWorkflowActionTestCase( + GenericDocumentViewTestCase, KeyTestMixin, WorkflowTestMixin, +): + def test_document_signature_detached_action(self): + self._create_test_key_private() + signature_count = DetachedSignature.objects.count() + + action = DocumentSignatureDetachedAction( + form_data={ + 'key': self.test_key_private, + 'passphrase': TEST_KEY_PRIVATE_PASSPHRASE + } + ) + action.execute(context={'document': self.test_document}) + self.assertNotEqual(signature_count, DetachedSignature.objects.count()) + + def test_document_signature_embedded_action(self): + self._create_test_key_private() + signature_count = EmbeddedSignature.objects.count() + + action = DocumentSignatureEmbeddedAction( + form_data={ + 'key': self.test_key_private, + 'passphrase': TEST_KEY_PRIVATE_PASSPHRASE + } + ) + action.execute(context={'document': self.test_document}) + self.assertNotEqual(signature_count, EmbeddedSignature.objects.count()) diff --git a/mayan/apps/document_signatures/workflow_actions.py b/mayan/apps/document_signatures/workflow_actions.py new file mode 100644 index 0000000000..c2e77b8173 --- /dev/null +++ b/mayan/apps/document_signatures/workflow_actions.py @@ -0,0 +1,75 @@ +from __future__ import absolute_import, unicode_literals + +import logging + +from django.utils.translation import ugettext_lazy as _ + +from mayan.apps.acls.models import AccessControlList +from mayan.apps.django_gpg.models import Key +from mayan.apps.django_gpg.permissions import permission_key_sign +from mayan.apps.document_states.classes import WorkflowAction + +from .models import DetachedSignature, EmbeddedSignature + +logger = logging.getLogger(__name__) + + +class DocumentSignatureDetachedAction(WorkflowAction): + fields = { + 'key': { + 'label': _('Key'), + 'class': 'django.forms.ModelChoiceField', 'kwargs': { + 'help_text': _( + 'Private key that will be used to sign the document ' + 'version.' + ), 'queryset': Key.objects.none(), + }, + }, 'passphrase': { + 'label': _('Passphrase'), + 'class': 'django.forms.CharField', 'kwargs': { + 'help_text': _( + 'The passphrase to unlock the key and allow it to be ' + 'used to sign the document version.' + ), 'required': False + }, + }, + } + field_order = ('key', 'passphrase') + label = _('Sign document (detached)') + widgets = { + 'passphrase': { + 'class': 'django.forms.widgets.PasswordInput', + } + } + + def get_form_schema(self, request): + user = request.user + logger.debug('user: %s', user) + + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_key_sign, queryset=Key.objects.all(), + user=user + ) + + self.fields['key']['kwargs']['queryset'] = queryset + return super(DocumentSignatureDetachedAction, self).get_form_schema( + request=request + ) + + def execute(self, context): + DetachedSignature.objects.sign_document_version( + document_version=context['document'].latest_version, + key=self.form_data['key'], + passphrase=self.form_data.get('passphrase'), + ) + + +class DocumentSignatureEmbeddedAction(DocumentSignatureDetachedAction): + label = _('Sign document (embedded)') + + def execute(self, context): + EmbeddedSignature.objects.sign_document_version( + document_version=context['document'].latest_version, + key=self.form_data['key'], + passphrase=self.form_data.get('passphrase'), + )