Do not send unencrypted admin reports
This commit is contained in:
parent
283641ee5c
commit
612334ae8d
4 changed files with 59 additions and 17 deletions
|
@ -84,11 +84,14 @@ class MultiList:
|
||||||
logging.info(f'No changes for {self._target}')
|
logging.info(f'No changes for {self._target}')
|
||||||
else:
|
else:
|
||||||
for admin in target_admins:
|
for admin in target_admins:
|
||||||
report = AdminReport(self._target, admin.email, self._mail_from,
|
try:
|
||||||
admin.key.blob if admin.key is not None else None,
|
report = AdminReport(self._target, admin.email, self._mail_from,
|
||||||
to_subscribe, to_unsubscribe, to_update, to_add, to_remove,
|
admin.key.blob if admin.key is not None else None,
|
||||||
conflicts)
|
to_subscribe, to_unsubscribe, to_update, to_add, to_remove,
|
||||||
self._reporter.add_message(report)
|
conflicts)
|
||||||
|
self._reporter.add_message(report)
|
||||||
|
except BaseException:
|
||||||
|
logging.exception(f'Encryption to {admin.email} failed, not sending report')
|
||||||
logging.info(f'Finished processing: {self._target}')
|
logging.info(f'Finished processing: {self._target}')
|
||||||
|
|
||||||
def _lists_by_name(self) -> Tuple[SchleuderList, List[SchleuderList]]:
|
def _lists_by_name(self) -> Tuple[SchleuderList, List[SchleuderList]]:
|
||||||
|
|
|
@ -27,24 +27,29 @@ class Message(abc.ABC):
|
||||||
mail_from: str,
|
mail_from: str,
|
||||||
mail_to: str,
|
mail_to: str,
|
||||||
content: str,
|
content: str,
|
||||||
encrypt_to: List[str]):
|
encrypt_to: List[str],
|
||||||
|
encrypt_may_fail: bool = False):
|
||||||
self._schleuder: str = schleuder
|
self._schleuder: str = schleuder
|
||||||
self._from: str = mail_from
|
self._from: str = mail_from
|
||||||
self._to: str = mail_to
|
self._to: str = mail_to
|
||||||
self._keys: List[str] = encrypt_to
|
self._keys: List[str] = encrypt_to
|
||||||
self._mime: email.mime.base.MIMEBase = self._make_mime(content)
|
self._mime: email.mime.base.MIMEBase = self._make_mime(content, encrypt_may_fail)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mime(self) -> email.mime.base.MIMEBase:
|
def mime(self) -> email.mime.base.MIMEBase:
|
||||||
return self._mime
|
return self._mime
|
||||||
|
|
||||||
def _make_mime(self, content: str) -> email.mime.base.MIMEBase:
|
def _make_mime(self, content: str, encrypt_may_fail: bool) -> email.mime.base.MIMEBase:
|
||||||
# Encrypt to all keys, if possible. Fall back to unencrypted otherwise
|
# Encrypt to all keys, if possible. Fall back to unencrypted otherwise
|
||||||
try:
|
try:
|
||||||
self._mime = self._encrypt_message(content)
|
self._mime = self._encrypt_message(content)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
logging.exception('Encryption failed; falling back to unencrypted message')
|
if encrypt_may_fail:
|
||||||
self._mime = email.mime.text.MIMEText(content, _subtype='plain', _charset='utf-8')
|
logging.exception('Encryption failed; falling back to unencrypted message')
|
||||||
|
self._mime = email.mime.text.MIMEText(content, _subtype='plain', _charset='utf-8')
|
||||||
|
else:
|
||||||
|
logging.exception('Encryption failed; Not sending this message')
|
||||||
|
raise e
|
||||||
# Set all the email headers
|
# Set all the email headers
|
||||||
self._mime['From'] = self._from
|
self._mime['From'] = self._from
|
||||||
self._mime['Reply-To'] = self._from
|
self._mime['Reply-To'] = self._from
|
||||||
|
@ -124,7 +129,8 @@ class KeyConflictMessage(Message):
|
||||||
mail_from=mail_from,
|
mail_from=mail_from,
|
||||||
mail_to=chosen.email,
|
mail_to=chosen.email,
|
||||||
content=content,
|
content=content,
|
||||||
encrypt_to=[s.key.blob for s in affected if s.key is not None]
|
encrypt_to=[s.key.blob for s in affected if s.key is not None],
|
||||||
|
encrypt_may_fail=True # Permit unencrypted fallback so the user gets notified of the conflict anyway
|
||||||
)
|
)
|
||||||
self.mime['Subject'] = f'MultiSchleuder {self._schleuder} - Key Conflict'
|
self.mime['Subject'] = f'MultiSchleuder {self._schleuder} - Key Conflict'
|
||||||
self.mime['X-MultiSchleuder-Digest'] = digest
|
self.mime['X-MultiSchleuder-Digest'] = digest
|
||||||
|
@ -174,7 +180,8 @@ class UserConflictMessage(Message):
|
||||||
mail_from=mail_from,
|
mail_from=mail_from,
|
||||||
mail_to=subscriber,
|
mail_to=subscriber,
|
||||||
content=content,
|
content=content,
|
||||||
encrypt_to=[chosen.key.blob]
|
encrypt_to=[chosen.key.blob],
|
||||||
|
encrypt_may_fail=True # Permit unencrypted fallback so the user gets notified of the conflict anyway
|
||||||
)
|
)
|
||||||
self.mime['Subject'] = f'MultiSchleuder {self._schleuder} - Subscriber Conflict'
|
self.mime['Subject'] = f'MultiSchleuder {self._schleuder} - Subscriber Conflict'
|
||||||
self.mime['X-MultiSchleuder-Digest'] = digest
|
self.mime['X-MultiSchleuder-Digest'] = digest
|
||||||
|
|
|
@ -9,6 +9,7 @@ from dateutil.tz import tzutc
|
||||||
|
|
||||||
from multischleuder.processor import MultiList
|
from multischleuder.processor import MultiList
|
||||||
from multischleuder.reporting import Message
|
from multischleuder.reporting import Message
|
||||||
|
from multischleuder.test.test_conflict import _PRIVKEY_1
|
||||||
from multischleuder.types import SchleuderKey, SchleuderList, SchleuderSubscriber
|
from multischleuder.types import SchleuderKey, SchleuderList, SchleuderSubscriber
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ def _list_lists():
|
||||||
|
|
||||||
def _get_key(fpr: str, schleuder: SchleuderList):
|
def _get_key(fpr: str, schleuder: SchleuderList):
|
||||||
key1 = SchleuderKey('966842467B3254143F994D5E5C408C012D216471',
|
key1 = SchleuderKey('966842467B3254143F994D5E5C408C012D216471',
|
||||||
'admin@example.org', 'BEGIN PGP 2D216471', schleuder.id)
|
'admin@example.org', str(_PRIVKEY_1.pubkey), schleuder.id)
|
||||||
key2 = SchleuderKey('6449FFB6EE68187962FA013B5CA2F4F51791BAF6',
|
key2 = SchleuderKey('6449FFB6EE68187962FA013B5CA2F4F51791BAF6',
|
||||||
'ada.lovelace@example.org', 'BEGIN PGP 1791BAF6', schleuder.id)
|
'ada.lovelace@example.org', 'BEGIN PGP 1791BAF6', schleuder.id)
|
||||||
key3 = SchleuderKey('414D3960D34730F63C74D5190EBC5A16716DEC79',
|
key3 = SchleuderKey('414D3960D34730F63C74D5190EBC5A16716DEC79',
|
||||||
|
@ -72,7 +73,7 @@ def _get_admins(schleuder: SchleuderList):
|
||||||
if schleuder.id != 2:
|
if schleuder.id != 2:
|
||||||
return []
|
return []
|
||||||
key = SchleuderKey('966842467B3254143F994D5E5C408C012D216471',
|
key = SchleuderKey('966842467B3254143F994D5E5C408C012D216471',
|
||||||
'admin@example.org', 'BEGIN PGP 2D216471', schleuder.id)
|
'admin@example.org', str(_PRIVKEY_1.pubkey), schleuder.id)
|
||||||
date = datetime(2022, 4, 15, 5, 23, 42, 0, tzinfo=tzutc())
|
date = datetime(2022, 4, 15, 5, 23, 42, 0, tzinfo=tzutc())
|
||||||
admin = SchleuderSubscriber(0, 'admin@example.org', key, schleuder.id, date)
|
admin = SchleuderSubscriber(0, 'admin@example.org', key, schleuder.id, date)
|
||||||
return [admin]
|
return [admin]
|
||||||
|
@ -80,7 +81,7 @@ def _get_admins(schleuder: SchleuderList):
|
||||||
|
|
||||||
def _get_subs(schleuder: SchleuderList):
|
def _get_subs(schleuder: SchleuderList):
|
||||||
key1 = SchleuderKey('966842467B3254143F994D5E5C408C012D216471',
|
key1 = SchleuderKey('966842467B3254143F994D5E5C408C012D216471',
|
||||||
'admin@example.org', 'BEGIN PGP 2D216471', schleuder.id)
|
'admin@example.org', str(_PRIVKEY_1.pubkey), schleuder.id)
|
||||||
key2 = SchleuderKey('6449FFB6EE68187962FA013B5CA2F4F51791BAF6',
|
key2 = SchleuderKey('6449FFB6EE68187962FA013B5CA2F4F51791BAF6',
|
||||||
'ada.lovelace@example.org', 'BEGIN PGP 1791BAF6', schleuder.id)
|
'ada.lovelace@example.org', 'BEGIN PGP 1791BAF6', schleuder.id)
|
||||||
key3 = SchleuderKey('414D3960D34730F63C74D5190EBC5A16716DEC79',
|
key3 = SchleuderKey('414D3960D34730F63C74D5190EBC5A16716DEC79',
|
||||||
|
|
|
@ -3,11 +3,27 @@ import unittest
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import pgpy.errors
|
||||||
|
|
||||||
from multischleuder.reporting import KeyConflictMessage, AdminReport, Reporter, UserConflictMessage
|
from multischleuder.reporting import KeyConflictMessage, AdminReport, Reporter, UserConflictMessage
|
||||||
from multischleuder.types import SchleuderKey, SchleuderList, SchleuderSubscriber
|
from multischleuder.types import SchleuderKey, SchleuderList, SchleuderSubscriber
|
||||||
from multischleuder.test.test_conflict import _PRIVKEY_1
|
from multischleuder.test.test_conflict import _PRIVKEY_1
|
||||||
|
|
||||||
|
|
||||||
|
BROKENKEY = '''
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mDMEYmcMbxYJKwYBBAHaRw8BAQdAKUohRdnuTSldKwawfLdwwUvOJjz/pHx3fXS2
|
||||||
|
v2dUQx+0SU11bHRpc2NobGV1ZGVyIEJyb2tlbiBBZG1pbiBLZXkgKFRFU1QgS0VZ
|
||||||
|
IERPIE5PVCBVU0UpIDxhZG1pbkBleGFtcGxlLm9yZz6IkAQTFggAOBYhBGtuFOnz
|
||||||
|
PJOCOdfv6OuAwhfh1Uj8BQJiZwxvAhsBBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
|
||||||
|
AAoJEOuAwhfh1Uj8PnkBAM6PfYUZbvvYEkSdwzmZXDwhPRsSA0bhjL5aVwIeCCdp
|
||||||
|
AQDeImNI6czSLVAuwObKv8FnpmbFi3HxTNzakp44DoD8Aw==
|
||||||
|
=JtdI
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
def one_of_each_kind():
|
def one_of_each_kind():
|
||||||
sub = SchleuderSubscriber(1, 'foo@example.org', None, 1, datetime.utcnow())
|
sub = SchleuderSubscriber(1, 'foo@example.org', None, 1, datetime.utcnow())
|
||||||
key = SchleuderKey(_PRIVKEY_1.fingerprint.replace(' ', ''), 'foo@example.org', str(_PRIVKEY_1.pubkey), 1)
|
key = SchleuderKey(_PRIVKEY_1.fingerprint.replace(' ', ''), 'foo@example.org', str(_PRIVKEY_1.pubkey), 1)
|
||||||
|
@ -24,7 +40,7 @@ def one_of_each_kind():
|
||||||
schleuder='test@example.org',
|
schleuder='test@example.org',
|
||||||
mail_to='admin@example.org',
|
mail_to='admin@example.org',
|
||||||
mail_from='test-owner@example.org',
|
mail_from='test-owner@example.org',
|
||||||
encrypt_to=None,
|
encrypt_to=str(_PRIVKEY_1.pubkey),
|
||||||
subscribed={},
|
subscribed={},
|
||||||
unsubscribed={sub},
|
unsubscribed={sub},
|
||||||
updated={},
|
updated={},
|
||||||
|
@ -94,3 +110,18 @@ class TestReporting(unittest.TestCase):
|
||||||
r.add_messages([None])
|
r.add_messages([None])
|
||||||
self.assertEqual(0, len(Reporter.get_messages()))
|
self.assertEqual(0, len(Reporter.get_messages()))
|
||||||
Reporter.clear_messages()
|
Reporter.clear_messages()
|
||||||
|
|
||||||
|
def test_admin_report_nokey(self):
|
||||||
|
sub = SchleuderSubscriber(1, 'foo@example.org', None, 1, datetime.utcnow())
|
||||||
|
with self.assertRaises(pgpy.errors.PGPError):
|
||||||
|
AdminReport(
|
||||||
|
schleuder='test@example.org',
|
||||||
|
mail_to='admin@example.org',
|
||||||
|
mail_from='test-owner@example.org',
|
||||||
|
encrypt_to=BROKENKEY,
|
||||||
|
subscribed={sub},
|
||||||
|
unsubscribed={},
|
||||||
|
updated={},
|
||||||
|
added={},
|
||||||
|
removed={},
|
||||||
|
conflicts=[])
|
||||||
|
|
Loading…
Reference in a new issue