Clean up the code after refactor

- Clean up PGP/MIME flow by using API instead of explicit/manual generation of
  headers.
- Fix E2E test configuration for PGP/MIME case.
- Add first lacre.core unit tests.
- Add another Contract Test.
This commit is contained in:
Piotr F. Mieszkowski 2023-02-19 11:22:37 +01:00
parent 27b07e672d
commit 56101b86c0
5 changed files with 120 additions and 78 deletions

View File

@ -321,42 +321,38 @@ def _encrypt_all_payloads_mime(message: email.message.Message, gpg_to_cmdline):
encrypted_part.set_payload(copy_encrypted.get_payload())
_set_content_type(message, boundary)
_set_type_and_boundary_2(message, boundary)
return [pgp_ver_part, encrypted_part]
else:
processed_payloads = _generate_message_from_payloads(message)
encrypted_part.set_payload(processed_payloads.as_string())
_set_content_type(message, boundary)
_set_type_and_boundary_2(message, boundary)
return [pgp_ver_part, _encrypt_payload(encrypted_part, gpg_to_cmdline, False)]
def _make_boundary():
junk_msg = MIMEMultipart()
_ = junk_msg.as_string() # WTF! Without this, get_boundary() will return 'None'!
# XXX See EmailTest.test_boundary_generated_after_as_string_call.
_ = junk_msg.as_string()
return junk_msg.get_boundary()
def _set_content_type(message: email.message.Message, boundary: str):
if 'Content-Type' in message:
message.replace_header('Content-Type', _multipart_encrypted_with_boundary(boundary)+text.EOL)
else:
message['Content-Type'] = _multipart_encrypted_with_boundary(boundary)+text.EOL
def _multipart_encrypted_with_boundary(boundary):
return f"multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"{boundary}\""
def _set_type_and_boundary_2(message: email.message.Message, boundary):
message.set_type('multipart/encrypted')
message.set_param('protocol', 'application/pgp-encrypted')
message.set_param('boundary', boundary)
def _encrypt_payload(payload: email.message.Message, recipients, check_nested=True, **kwargs):
raw_payload = raw_data_manager.get_content(payload)
LOG.debug('Got raw_payload (%s; %s): %s. Message: %s',
LOG.debug('About to encrypt raw payload (%s; %s): %s',
type(raw_payload),
payload.get_content_type(),
raw_payload,
payload)
raw_payload)
LOG.debug('Encrypting message: %s', payload)
if check_nested and text.is_payload_pgp_inline(raw_payload):
LOG.debug("Message is already pgp encrypted. No nested encryption needed.")
return payload
@ -364,8 +360,7 @@ def _encrypt_payload(payload: email.message.Message, recipients, check_nested=Tr
gpg = _make_encryptor(raw_payload, recipients)
gpg.update(raw_payload)
encrypted_data, returncode = gpg.encrypt()
LOG.debug("Return code from encryption=%d (0 indicates success)." % returncode)
encrypted_data, exit_code = gpg.encrypt()
payload.set_payload(encrypted_data)
isAttachment = payload.get_param('attachment', None, 'Content-Disposition') is not None

View File

@ -37,6 +37,14 @@ e2e_log_datefmt: %Y-%m-%d %H:%M:%S
lacre_log: test/logs/gpg-mailgate.log
log_config: test/gpg-lacre-log.ini
# TEST IDENTITIES AND SETTINGS:
#
# Email Key Style
# alice@disposlab RSA 3072 PGP/Inline
# bob@disposlab ED25519 PGP/Inline
# carlos@disposlab none PGP/Inline
# evan@disposlab ED25519 PGP/MIME
[case-1]
descr: Clear text message to a user without a key
to: carlos@disposlab
@ -119,10 +127,10 @@ out: -----BEGIN PGP MESSAGE-----
descr: Clear text with UTF-8, PGP/MIME
to: evan@disposlab
in: test/msgin/utf8-plain.msg
out: -----BEGIN PGP MESSAGE-----
out: Content-Type: application/pgp-encrypted
[case-15]
descr: Clear text with UTF-8, PGP/Inline
to: carlos@disposlab
to: bob@disposlab
in: test/msgin/utf8-plain.msg
out-not: -----BEGIN PGP MESSAGE-----
out: -----BEGIN PGP MESSAGE-----

View File

@ -28,9 +28,10 @@ log_headers = yes
send_email = no
[pgp_style]
# this recipient has PGP/MIME enabled, because the default approach is to use
# PGP/Inline
evan@disposlab = mime
[enc_keymap]
alice@disposlab = 1CD245308F0963D038E88357973CF4D9387C44D7
bob@disposlab = 19CF4B47ECC9C47AFA84D4BD96F39FDA0E31BB67

View File

@ -1,20 +1,20 @@
#
# gpg-mailgate
# gpg-mailgate
#
# This file is part of the gpg-mailgate source code.
# This file is part of the gpg-mailgate source code.
#
# gpg-mailgate is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# gpg-mailgate is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gpg-mailgate source code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# gpg-mailgate source code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
# You should have received a copy of the GNU General Public License
# along with gpg-mailgate source code. If not, see <http://www.gnu.org/licenses/>.
#
"""Unit-tests as contracts for external dependencies.
@ -25,68 +25,80 @@ documentation.
"""
import email
import email.mime.multipart
import unittest
from configparser import RawConfigParser
class EmailParsingTest(unittest.TestCase):
"""This test serves as a package contract and documentation of its behaviour."""
"""This test serves as a package contract and documentation of its behaviour."""
def test_message_from_bytes_produces_message_with_str_headers(self):
rawmsg = b"From: alice@lacre.io\r\n" \
+ b"To: bob@lacre.io\r\n" \
+ b"Subject: Test message\r\n" \
+ b"\r\n" \
+ b"Test message from Alice to Bob.\r\n"
def test_message_from_bytes_produces_message_with_str_headers(self):
rawmsg = b"From: alice@lacre.io\r\n" \
+ b"To: bob@lacre.io\r\n" \
+ b"Subject: Test message\r\n" \
+ b"\r\n" \
+ b"Test message from Alice to Bob.\r\n"
parsed = email.message_from_bytes(rawmsg)
parsed = email.message_from_bytes(rawmsg)
self.assertEqual(parsed["From"], "alice@lacre.io")
self.assertEqual(parsed["To"], "bob@lacre.io")
self.assertEqual(parsed["Subject"], "Test message")
self.assertEqual(parsed["From"], "alice@lacre.io")
self.assertEqual(parsed["To"], "bob@lacre.io")
self.assertEqual(parsed["Subject"], "Test message")
def test_bytes_message_payload_decoded_produces_bytes(self):
rawmsg = b"From: alice@lacre.io\r\n" \
+ b"To: bob@lacre.io\r\n" \
+ b"Subject: Test message\r\n" \
+ b"\r\n" \
+ b"Test message from Alice to Bob.\r\n"
def test_bytes_message_payload_decoded_produces_bytes(self):
rawmsg = b"From: alice@lacre.io\r\n" \
+ b"To: bob@lacre.io\r\n" \
+ b"Subject: Test message\r\n" \
+ b"\r\n" \
+ b"Test message from Alice to Bob.\r\n"
parsed = email.message_from_bytes(rawmsg)
parsed = email.message_from_bytes(rawmsg)
self.assertEqual(parsed.get_payload(), "Test message from Alice to Bob.\r\n")
self.assertEqual(parsed.get_payload(decode=True), b"Test message from Alice to Bob.\r\n")
self.assertEqual(parsed.get_payload(), "Test message from Alice to Bob.\r\n")
self.assertEqual(parsed.get_payload(decode=True), b"Test message from Alice to Bob.\r\n")
def test_message_from_string_produces_message_with_str_headers(self):
rawmsg = "From: alice@lacre.io\r\n" \
+ "To: bob@lacre.io\r\n" \
+ "Subject: Test message\r\n" \
+ "\r\n" \
+ "Test message from Alice to Bob.\r\n"
def test_message_from_string_produces_message_with_str_headers(self):
rawmsg = "From: alice@lacre.io\r\n" \
+ "To: bob@lacre.io\r\n" \
+ "Subject: Test message\r\n" \
+ "\r\n" \
+ "Test message from Alice to Bob.\r\n"
parsed = email.message_from_string(rawmsg)
parsed = email.message_from_string(rawmsg)
self.assertEqual(parsed["From"], "alice@lacre.io")
self.assertEqual(parsed["To"], "bob@lacre.io")
self.assertEqual(parsed["Subject"], "Test message")
self.assertEqual(parsed["From"], "alice@lacre.io")
self.assertEqual(parsed["To"], "bob@lacre.io")
self.assertEqual(parsed["Subject"], "Test message")
def test_str_message_payload_decoded_produces_bytes(self):
rawmsg = "From: alice@lacre.io\r\n" \
+ "To: bob@lacre.io\r\n" \
+ "Subject: Test message\r\n" \
+ "\r\n" \
+ "Test message from Alice to Bob.\r\n"
def test_str_message_payload_decoded_produces_bytes(self):
rawmsg = "From: alice@lacre.io\r\n" \
+ "To: bob@lacre.io\r\n" \
+ "Subject: Test message\r\n" \
+ "\r\n" \
+ "Test message from Alice to Bob.\r\n"
parsed = email.message_from_string(rawmsg)
parsed = email.message_from_string(rawmsg)
self.assertEqual(parsed.get_payload(), "Test message from Alice to Bob.\r\n")
self.assertEqual(parsed.get_payload(decode=True), b"Test message from Alice to Bob.\r\n")
class EmailTest(unittest.TestCase):
def test_boundary_generated_after_as_string_call(self):
mp = email.mime.multipart.MIMEMultipart()
self.assertTrue(mp.get_boundary() is None)
_ = mp.as_string()
self.assertFalse(mp.get_boundary() is None)
self.assertEqual(parsed.get_payload(), "Test message from Alice to Bob.\r\n")
self.assertEqual(parsed.get_payload(decode=True), b"Test message from Alice to Bob.\r\n")
class RawConfigParserTest(unittest.TestCase):
def test_config_parser_returns_str(self):
cp = RawConfigParser()
cp.read("test/sample.ini")
self.assertEqual(cp.get("foo", "bar"), "quux")
self.assertEqual(cp.get("foo", "baz"), "14")
def test_config_parser_returns_str(self):
cp = RawConfigParser()
cp.read("test/sample.ini")
self.assertEqual(cp.get("foo", "bar"), "quux")
self.assertEqual(cp.get("foo", "baz"), "14")
if __name__ == '__main__':
unittest.main()
unittest.main()

View File

@ -0,0 +1,26 @@
import lacre.core
from email.message import Message
import unittest
class LacreCoreTest(unittest.TestCase):
def test_attachment_handling(self):
m = Message()
m.set_payload('This is a payload')
m.set_param('attachment', '', 'Content-Disposition')
m.set_param('filename', 'foo', 'Content-Disposition')
lacre.core._append_gpg_extension(m)
self.assertEqual(m.get_filename(), 'foo.pgp')
def test_attachment_handling_2(self):
m = Message()
m.set_payload('This is a payload')
m.set_param('attachment', '', 'Content-Disposition')
m.set_param('name', 'quux', 'Content-Type')
lacre.core._append_gpg_extension(m)
self.assertEqual(m.get_filename(), 'quux.pgp')