From 51464c910baff4c0e7eb4c27b73ad81e5f154464 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sat, 31 Dec 2011 03:26:25 -0400 Subject: [PATCH] Move more signature functionality to the document signature app --- apps/document_signatures/__init__.py | 46 +++++---- apps/document_signatures/models.py | 129 +++++++++++++++++------- apps/document_signatures/permissions.py | 15 +++ apps/document_signatures/views.py | 14 +-- apps/documents/models.py | 2 - 5 files changed, 141 insertions(+), 65 deletions(-) create mode 100644 apps/document_signatures/permissions.py diff --git a/apps/document_signatures/__init__.py b/apps/document_signatures/__init__.py index 11edeb1d07..1422b3e4e6 100644 --- a/apps/document_signatures/__init__.py +++ b/apps/document_signatures/__init__.py @@ -1,37 +1,49 @@ from __future__ import absolute_import +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + from django.utils.translation import ugettext_lazy as _ -from documents.models import Document +from documents.models import Document, DocumentVersion from navigation.api import register_links, register_top_menu, \ register_model_list_columns, register_multi_item_links, \ register_sidebar_template -from main.api import register_diagnostic, register_maintenance_links -from permissions.api import register_permission, set_namespace_title -#from project_setup.api import register_setup +from django_gpg.runtime import gpg +from django_gpg.exceptions import GPGDecryptionError +#from main.api import register_diagnostic, register_maintenance_links -#from django_gpg.api import Key +from .models import DocumentVersionSignature +from .permissions import ( + PERMISSION_DOCUMENT_VERIFY, + PERMISSION_SIGNATURE_UPLOAD, + PERMISSION_SIGNATURE_DOWNLOAD + ) -PERMISSION_DOCUMENT_VERIFY = {'namespace': 'document_signatures', 'name': 'document_verify', 'label': _(u'Verify document signatures')} -PERMISSION_SIGNATURE_UPLOAD = {'namespace': 'document_signatures', 'name': 'signature_upload', 'label': _(u'Upload detached signatures')} -PERMISSION_SIGNATURE_DOWNLOAD = {'namespace': 'document_signatures', 'name': 'key_receive', 'label': _(u'Download detached signatures')} - -# Permission setup -set_namespace_title('document_signatures', _(u'Document signatures')) -register_permission(PERMISSION_DOCUMENT_VERIFY) -register_permission(PERMISSION_SIGNATURE_UPLOAD) -register_permission(PERMISSION_SIGNATURE_DOWNLOAD) def has_embedded_signature(context): - return context['object'].signature_state + return DocumentVersionSignature.objects.has_embedded_signature(context['object']) def doesnt_have_detached_signature(context): - return context['object'].has_detached_signature() == False + return DocumentVersionSignature.objects.has_detached_signature(context['object']) == False + +def document_pre_open_hook(descriptor): + try: + result = gpg.decrypt_file(descriptor) + # gpg return a string, turn it into a file like object + return StringIO(result.data) + except GPGDecryptionError: + # At least return the original raw content + return descriptor document_signature_upload = {'text': _(u'upload signature'), 'view': 'document_signature_upload', 'args': 'object.pk', 'famfam': 'pencil_add', 'permissions': [PERMISSION_SIGNATURE_UPLOAD], 'conditional_disable': has_embedded_signature} document_signature_download = {'text': _(u'download signature'), 'view': 'document_signature_download', 'args': 'object.pk', 'famfam': 'disk', 'permissions': [PERMISSION_SIGNATURE_DOWNLOAD], 'conditional_disable': doesnt_have_detached_signature} document_verify = {'text': _(u'signatures'), 'view': 'document_verify', 'args': 'object.pk', 'famfam': 'text_signature', 'permissions': [PERMISSION_DOCUMENT_VERIFY]} register_links(Document, [document_verify], menu_name='form_header') - register_links(['document_verify', 'document_signature_upload', 'document_signature_download'], [document_signature_upload, document_signature_download], menu_name='sidebar') + + +DocumentVersion.register_pre_open_hook(1, document_pre_open_hook) diff --git a/apps/document_signatures/models.py b/apps/document_signatures/models.py index 9086d3d7ce..645dc04270 100644 --- a/apps/document_signatures/models.py +++ b/apps/document_signatures/models.py @@ -1,9 +1,98 @@ +import logging + from django.db import models from django.utils.translation import ugettext_lazy as _ from documents.models import DocumentVersion, get_filename_from_uuid from documents.conf.settings import STORAGE_BACKEND +from django_gpg.runtime import gpg +from django_gpg.exceptions import GPGVerificationError, GPGDecryptionError +logger = logging.getLogger(__name__) + + +class DocumentVersionSignatureManager(models.Manager): + #def update_signed_state(self, document): + # document_signature, created = self.model.get_or_create( + # document_version=document.latest_version, + # ) + # if document.exists(): + # descriptor = document.open() + # try: + # document_signature.signature_state = gpg.verify_file(descriptor).status + # # TODO: give use choice for auto public key fetch? + # # OR maybe new config option + # except GPGVerificationError: + # document_signature.signature_state = None + # finally: + # document_signature.save() + + def add_detached_signature(self, document, detached_signature): + document_signature, created = self.model.objects.get_or_create( + document_version=document.latest_version, + ) + if not self.signature_state(document): + document_signature.signature_file = detached_signature + document_signature.save() + else: + raise Exception('document already has an embedded signature') + + def has_detached_signature(self, document): + document_signature, created = self.model.objects.get_or_create( + document_version=document.latest_version, + ) + if document_signature.signature_file: + return True + else: + return False + + def has_embedded_signature(self, document): + logger.debug('document: %s' % document) + + if self.signature_state(document): + return True + else: + return False + + def signature_state(self, document): + document_signature, created = self.model.objects.get_or_create( + document_version=document.latest_version, + ) + logger.debug('created: %s' % created) + if created and document.exists(): + descriptor = document.open(raw=True) + try: + document_signature.signature_state = gpg.verify_file(descriptor).status + # TODO: give use choice for auto public key fetch? + # OR maybe new config option + except GPGVerificationError: + document_signature.signature_state = None + finally: + document_signature.save() + + #document_signature.signature_state = self.verify_signature(document).status + #document_signature.save() + + return document_signature.signature_state + + def detached_signature(self, document): + document_signature, created = self.model.objects.get_or_create( + document_version=document.latest_version, + ) + return document_signature.signature_file.storage.open(document_signature.signature_file.path) + + def verify_signature(self, document): + if self.has_detached_signature(document): + logger.debug('has detached signature') + args = (document.open(), self.detached_signature(document)) + else: + args = (document.open(raw=True),) + + try: + return gpg.verify_w_retry(*args) + except GPGVerificationError: + return None + class DocumentVersionSignature(models.Model): ''' @@ -13,46 +102,8 @@ class DocumentVersionSignature(models.Model): signature_state = models.CharField(blank=True, null=True, max_length=16, verbose_name=_(u'signature state'), editable=False) signature_file = models.FileField(blank=True, null=True, upload_to=get_filename_from_uuid, storage=STORAGE_BACKEND(), verbose_name=_(u'signature file'), editable=False) - def update_signed_state(self, save=True): - if self.exists(): - try: - self.signature_state = gpg.verify_file(self.open()).status - # TODO: give use choice for auto public key fetch? - # OR maybe new config option - except GPGVerificationError: - self.signature_state = None - - if save: - self.save() + objects = DocumentVersionSignatureManager() - def add_detached_signature(self, detached_signature): - if not self.signature_state: - self.signature_file = detached_signature - self.save() - else: - raise Exception('document already has an embedded signature') - - def has_detached_signature(self): - if self.signature_file: - return self.signature_file.storage.exists(self.signature_file.path) - else: - return False - - def detached_signature(self): - return self.signature_file.storage.open(self.signature_file.path) - - def verify_signature(self): - try: - if self.has_detached_signature(): - logger.debug('has detached signature') - signature = gpg.verify_w_retry(self.open(), self.detached_signature()) - else: - signature = gpg.verify_w_retry(self.open(raw=True)) - except GPGVerificationError: - signature = None - - return signature - class Meta: verbose_name = _(u'document version signature') verbose_name_plural = _(u'document version signatures') diff --git a/apps/document_signatures/permissions.py b/apps/document_signatures/permissions.py new file mode 100644 index 0000000000..9bbfdfece8 --- /dev/null +++ b/apps/document_signatures/permissions.py @@ -0,0 +1,15 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.api import register_permission, set_namespace_title + +PERMISSION_DOCUMENT_VERIFY = {'namespace': 'document_signatures', 'name': 'document_verify', 'label': _(u'Verify document signatures')} +PERMISSION_SIGNATURE_UPLOAD = {'namespace': 'document_signatures', 'name': 'signature_upload', 'label': _(u'Upload detached signatures')} +PERMISSION_SIGNATURE_DOWNLOAD = {'namespace': 'document_signatures', 'name': 'key_receive', 'label': _(u'Download detached signatures')} + +# Permission setup +set_namespace_title('document_signatures', _(u'Document signatures')) +register_permission(PERMISSION_DOCUMENT_VERIFY) +register_permission(PERMISSION_SIGNATURE_UPLOAD) +register_permission(PERMISSION_SIGNATURE_DOWNLOAD) diff --git a/apps/document_signatures/views.py b/apps/document_signatures/views.py index 02b33d8847..dd83df19be 100644 --- a/apps/document_signatures/views.py +++ b/apps/document_signatures/views.py @@ -38,8 +38,8 @@ def document_verify(request, document_pk): RecentDocument.objects.add_document_for_user(request.user, document) - signature = document.verify_signature() - + signature = DocumentVersionSignature.objects.verify_signature(document) + signature_state = SIGNATURE_STATES.get(getattr(signature, 'status', None)) widget = (u'' % (settings.STATIC_URL, signature_state['icon'])) @@ -50,7 +50,7 @@ def document_verify(request, document_pk): }, ] - if document.signature_state: + if DocumentVersionSignature.objects.has_embedded_signature(document): signature_type = _(u'embedded') else: signature_type = _(u'detached') @@ -88,7 +88,7 @@ def document_signature_upload(request, document_pk): form = DetachedSignatureForm(request.POST, request.FILES) if form.is_valid(): try: - document.add_detached_signature(request.FILES['file']) + DocumentVersionSignature.objects.add_detached_signature(document, request.FILES['file']) messages.success(request, _(u'Detached signature uploaded successfully.')) return HttpResponseRedirect(next) except Exception, msg: @@ -110,10 +110,10 @@ def document_signature_upload(request, document_pk): def document_signature_download(request, document_pk): check_permissions(request.user, [PERMISSION_SIGNATURE_DOWNLOAD]) document = get_object_or_404(Document, pk=document_pk) - + try: - if document.has_detached_signature(): - signature = document.detached_signature() + if DocumentVersionSignature.objects.has_detached_signature(document): + signature = DocumentVersionSignature.objects.detached_signature(document) return serve_file( request, signature, diff --git a/apps/documents/models.py b/apps/documents/models.py index d2edaf0e91..2255d487fe 100644 --- a/apps/documents/models.py +++ b/apps/documents/models.py @@ -26,8 +26,6 @@ from mimetype.api import (get_mimetype, get_icon_file_path, get_error_icon_file_path) from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION, DEFAULT_PAGE_NUMBER) -from django_gpg.runtime import gpg -from django_gpg.exceptions import GPGVerificationError, GPGDecryptionError from documents.conf.settings import CHECKSUM_FUNCTION from documents.conf.settings import UUID_FUNCTION