From 19cda9ca7aa1aca18f61ebf659fc490f8ad4c15a Mon Sep 17 00:00:00 2001 From: Duda Dornelles Date: Wed, 2 Sep 2015 18:07:51 -0300 Subject: [feature] adding security_casing to LeapMail so we can show in the UI the signature and encryption status --- .../pixelated/adapter/mailstore/leap_mailstore.py | 25 +++++++++++++-- .../extensions/incoming_decrypt_header.py | 35 --------------------- service/test/integration/test_leap_mailstore.py | 2 +- .../test/unit/adapter/mailstore/test_leap_mail.py | 23 ++++++++++++++ web-ui/app/js/mail_view/ui/mail_view.js | 36 +++++++++++++--------- 5 files changed, 68 insertions(+), 53 deletions(-) delete mode 100644 service/pixelated/extensions/incoming_decrypt_header.py diff --git a/service/pixelated/adapter/mailstore/leap_mailstore.py b/service/pixelated/adapter/mailstore/leap_mailstore.py index 993f413c..a27ce5c4 100644 --- a/service/pixelated/adapter/mailstore/leap_mailstore.py +++ b/service/pixelated/adapter/mailstore/leap_mailstore.py @@ -16,13 +16,13 @@ import base64 from email.header import decode_header import quopri -import re from uuid import uuid4 + +import re from leap.mail.adaptors.soledad import SoledadMailAdaptor, ContentDocWrapper from twisted.internet import defer from pixelated.adapter.mailstore.body_parser import BodyParser from pixelated.adapter.mailstore.mailstore import MailStore, underscore_uuid - from leap.mail.mail import Message from pixelated.adapter.model.mail import Mail, InputMail @@ -74,6 +74,26 @@ class LeapMail(Mail): def mailbox_name(self): return self._mailbox_name + @property + def security_casing(self): + casing = dict(imprints=self._signature_information(), locks=[]) + if self._encrypted() == "decrypted": + casing["locks"] = [{"state": "valid"}] + return casing + + def _encrypted(self): + return self.headers.get("X-Leap-Encryption", "false") + + def _signature_information(self): + signature = self.headers.get("X-Leap-Signature", None) + if signature is None or signature.startswith("could not verify"): + return [{"state": "no_signature_information"}] + else: + if signature.startswith("valid"): + return [{"state": "valid", "seal": {"validity": "valid"}}] + else: + return [] + @property def raw(self): result = u'' @@ -107,6 +127,7 @@ class LeapMail(Mail): 'tags': self.tags, 'status': list(self.status), 'body': self._body, + 'security_casing': self.security_casing, 'textPlainBody': self._body, 'replying': self._replying_dict(), 'mailbox': self._mailbox_name.lower(), diff --git a/service/pixelated/extensions/incoming_decrypt_header.py b/service/pixelated/extensions/incoming_decrypt_header.py deleted file mode 100644 index 2db5dd1d..00000000 --- a/service/pixelated/extensions/incoming_decrypt_header.py +++ /dev/null @@ -1,35 +0,0 @@ -import leap.mail.imap.fetch as fetch - - -def mark_as_encrypted_inline(f): - - def w(*args, **kwargs): - msg, valid_sign = f(*args) - is_encrypted = fetch.PGP_BEGIN in args[1].as_string() and fetch.PGP_END in args[1].as_string() - decrypted_successfully = fetch.PGP_BEGIN not in msg.as_string() and fetch.PGP_END not in msg.as_string() - - if not is_encrypted: - encrypted = 'false' - else: - if decrypted_successfully: - encrypted = 'true' - else: - encrypted = 'fail' - - msg.add_header('X-Pixelated-encryption-status', encrypted) - return msg, valid_sign - - return w - - -def mark_as_encrypted_multipart(f): - - def w(*args, **kwargs): - msg, valid_sign = f(*args) - msg.add_header('X-Pixelated-encryption-status', 'true') - return msg, valid_sign - return w - - -fetch.LeapIncomingMail._maybe_decrypt_inline_encrypted_msg = mark_as_encrypted_inline(fetch.LeapIncomingMail._maybe_decrypt_inline_encrypted_msg) -fetch.LeapIncomingMail._decrypt_multipart_encrypted_msg = mark_as_encrypted_multipart(fetch.LeapIncomingMail._decrypt_multipart_encrypted_msg) diff --git a/service/test/integration/test_leap_mailstore.py b/service/test/integration/test_leap_mailstore.py index abe5d584..8f401bdd 100644 --- a/service/test/integration/test_leap_mailstore.py +++ b/service/test/integration/test_leap_mailstore.py @@ -31,7 +31,7 @@ class LeapMailStoreTest(SoledadTestBase): self.maxDiff = None mail = load_mail_from_file('mbox00000000') mail_id = yield self._create_mail_in_soledad(mail) - expected_mail_dict = {'body': u'Dignissimos ducimus veritatis. Est tenetur consequatur quia occaecati. Vel sit sit voluptas.\n\nEarum distinctio eos. Accusantium qui sint ut quia assumenda. Facere dignissimos inventore autem sit amet. Pariatur voluptatem sint est.\n\nUt recusandae praesentium aspernatur. Exercitationem amet placeat deserunt quae consequatur eum. Unde doloremque suscipit quia.\n\n', 'header': {u'date': u'Tue, 21 Apr 2015 08:43:27 +0000 (UTC)', u'to': [u'carmel@murazikortiz.name'], u'x-tw-pixelated-tags': u'nite, macro, trash', u'from': u'darby.senger@zemlak.biz', u'subject': u'Itaque consequatur repellendus provident sunt quia.'}, 'ident': mail_id, 'status': [], 'tags': set([]), 'replying': {'all': {'cc-field': [], 'to-field': [u'carmel@murazikortiz.name', u'darby.senger@zemlak.biz']}, 'single': u'darby.senger@zemlak.biz'}, 'textPlainBody': u'Dignissimos ducimus veritatis. Est tenetur consequatur quia occaecati. Vel sit sit voluptas.\n\nEarum distinctio eos. Accusantium qui sint ut quia assumenda. Facere dignissimos inventore autem sit amet. Pariatur voluptatem sint est.\n\nUt recusandae praesentium aspernatur. Exercitationem amet placeat deserunt quae consequatur eum. Unde doloremque suscipit quia.\n\n', 'mailbox': u'inbox', 'attachments': []} + expected_mail_dict = {'body': u'Dignissimos ducimus veritatis. Est tenetur consequatur quia occaecati. Vel sit sit voluptas.\n\nEarum distinctio eos. Accusantium qui sint ut quia assumenda. Facere dignissimos inventore autem sit amet. Pariatur voluptatem sint est.\n\nUt recusandae praesentium aspernatur. Exercitationem amet placeat deserunt quae consequatur eum. Unde doloremque suscipit quia.\n\n', 'header': {u'date': u'Tue, 21 Apr 2015 08:43:27 +0000 (UTC)', u'to': [u'carmel@murazikortiz.name'], u'x-tw-pixelated-tags': u'nite, macro, trash', u'from': u'darby.senger@zemlak.biz', u'subject': u'Itaque consequatur repellendus provident sunt quia.'}, 'ident': mail_id, 'status': [], 'tags': set([]), 'replying': {'all': {'cc-field': [], 'to-field': [u'carmel@murazikortiz.name', u'darby.senger@zemlak.biz']}, 'single': u'darby.senger@zemlak.biz'}, 'textPlainBody': u'Dignissimos ducimus veritatis. Est tenetur consequatur quia occaecati. Vel sit sit voluptas.\n\nEarum distinctio eos. Accusantium qui sint ut quia assumenda. Facere dignissimos inventore autem sit amet. Pariatur voluptatem sint est.\n\nUt recusandae praesentium aspernatur. Exercitationem amet placeat deserunt quae consequatur eum. Unde doloremque suscipit quia.\n\n', 'mailbox': u'inbox', 'attachments': [], 'security_casing': {'imprints': [{'state': 'no_signature_information'}], 'locks': []}} result = yield self.mail_store.get_mail(mail_id, include_body=True) self.assertIsNotNone(result) diff --git a/service/test/unit/adapter/mailstore/test_leap_mail.py b/service/test/unit/adapter/mailstore/test_leap_mail.py index 9b72d8c3..571e2f60 100644 --- a/service/test/unit/adapter/mailstore/test_leap_mail.py +++ b/service/test/unit/adapter/mailstore/test_leap_mail.py @@ -62,6 +62,10 @@ class TestLeapMail(TestCase): 'status': [], 'body': None, 'textPlainBody': None, + 'security_casing': { + 'imprints': [{'state': 'no_signature_information'}], + 'locks': [] + }, 'replying': {'all': {'cc-field': [], 'to-field': ['receiver@example.test', 'test@example.test', @@ -125,3 +129,22 @@ class TestLeapMail(TestCase): self.assertEquals([], mail.headers['To']) self.assertEquals([], mail.headers['Cc']) self.assertEquals([], mail.headers['Bcc']) + + def test_security_casing(self): + # No Encryption, no Signature + mail = LeapMail('id', 'INBOX', {}) + self.assertEqual({'locks': [], 'imprints': [{'state': 'no_signature_information'}]}, mail.security_casing) + + # Encryption + mail = LeapMail('id', 'INBOX', {'X-Leap-Encryption': 'decrypted'}) + self.assertEqual([{'state': 'valid'}], mail.security_casing['locks']) + + mail = LeapMail('id', 'INBOX', {'X-Leap-Encryption': 'false'}) + self.assertEqual([], mail.security_casing['locks']) + + # Signature + mail = LeapMail('id', 'INBOX', {'X-Leap-Signature': 'valid'}) + self.assertEqual([{'seal': {'validity': 'valid'}, 'state': 'valid'}], mail.security_casing['imprints']) + + mail = LeapMail('id', 'INBOX', {'X-Leap-Signature': 'invalid'}) + self.assertEqual([], mail.security_casing['imprints']) diff --git a/web-ui/app/js/mail_view/ui/mail_view.js b/web-ui/app/js/mail_view/ui/mail_view.js index 7caf813a..fb640529 100644 --- a/web-ui/app/js/mail_view/ui/mail_view.js +++ b/web-ui/app/js/mail_view/ui/mail_view.js @@ -25,11 +25,10 @@ define( 'mixins/with_hide_and_show', 'mixins/with_mail_tagging', 'page/events', - 'views/i18n', - 'features' + 'views/i18n' ], - function (defineComponent, templates, mailActions, viewHelpers, withHideAndShow, withMailTagging, events, i18n, features) { + function (defineComponent, templates, mailActions, viewHelpers, withHideAndShow, withMailTagging, events, i18n) { return defineComponent(mailView, mailActions, withHideAndShow, withMailTagging); @@ -50,12 +49,8 @@ define( var signed, encrypted; data.mail.security_casing = data.mail.security_casing || {}; - if(features.isEnabled('signatureStatus')) { - signed = this.checkSigned(data.mail); - } - if(features.isEnabled('encryptionStatus')) { - encrypted = this.checkEncrypted(data.mail); - } + signed = this.checkSigned(data.mail); + encrypted = this.checkEncrypted(data.mail); var attachments = _.map(data.mail.attachments, function(a){ return { 'encoding': a.encoding, 'name': a.name, 'ident': a.ident }; @@ -69,7 +64,6 @@ define( tags: data.mail.tags, encryptionStatus: encrypted, signatureStatus: signed, - features: features, attachments: attachments })); @@ -104,16 +98,29 @@ define( var status = ['encrypted']; - if(_.any(mail.security_casing.locks, function (lock) { return lock.state === 'valid'; })) { status.push('encryption-valid'); } - else { status.push('encryption-error'); } + var hasAnyEncryptionInfo = _.any(mail.security_casing.locks, function (lock) { + return lock.state === 'valid'; + }); + + if(hasAnyEncryptionInfo) { + status.push('encryption-valid'); + } else { + status.push('encryption-error'); + } return status.join(' '); }; this.checkSigned = function(mail) { - if(_.isEmpty(mail.security_casing.imprints)) { return 'not-signed'; } + if(_.isEmpty(mail.security_casing.imprints)) { + return 'not-signed'; + } + + var hasNoSignatureInformation = _.any(mail.security_casing.imprints, function (imprint) { + return imprint.state === 'no_signature_information'; + }); - if(_.any(mail.security_casing.imprints, function(imprint) { return imprint.state === 'no_signature_information'; })) { + if(hasNoSignatureInformation) { return ''; } @@ -130,7 +137,6 @@ define( status.push('signature-not-trusted'); } - return status.join(' '); }; -- cgit v1.2.3