diff options
| author | J08nY | 2017-03-13 02:14:26 +0100 |
|---|---|---|
| committer | J08nY | 2017-07-24 18:44:19 +0200 |
| commit | ad463598885ecc91bee9fbca5aea9fcb4e9f627e (patch) | |
| tree | 9363afa79afaf75c32742c57b6e1cee8754d7a32 /src/mailman/testing | |
| parent | 02826321d0430d7ffc1f674eeff4221941689ef7 (diff) | |
| download | mailman-mta-smtps-starttls.tar.gz mailman-mta-smtps-starttls.tar.zst mailman-mta-smtps-starttls.zip | |
Diffstat (limited to 'src/mailman/testing')
| -rw-r--r-- | src/mailman/testing/layers.py | 118 | ||||
| -rw-r--r-- | src/mailman/testing/mta.py | 53 | ||||
| -rw-r--r-- | src/mailman/testing/ssl_test_cert.crt | 21 | ||||
| -rw-r--r-- | src/mailman/testing/ssl_test_key.key | 28 |
4 files changed, 213 insertions, 7 deletions
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py index dbed1fccf..54ba4f5a1 100644 --- a/src/mailman/testing/layers.py +++ b/src/mailman/testing/layers.py @@ -25,6 +25,7 @@ # get rid of the layers and use something like testresources or some such. import os +import ssl import sys import shutil import logging @@ -40,14 +41,15 @@ from mailman.database.transaction import transaction from mailman.interfaces.domain import IDomainManager from mailman.testing.helpers import ( TestableMaster, get_lmtp_client, reset_the_world, wait_for_webservice) -from mailman.testing.mta import ConnectionCountingController +from mailman.testing.mta import ( + ConnectionCountingController, ConnectionCountingSSLController, + ConnectionCountingSTARTLSController) from mailman.utilities.string import expand -from pkg_resources import resource_string as resource_bytes +from pkg_resources import resource_filename, resource_string as resource_bytes from public import public from textwrap import dedent from zope.component import getUtility - TEST_TIMEOUT = datetime.timedelta(seconds=5) NL = '\n' @@ -259,6 +261,116 @@ class SMTPLayer(ConfigLayer): @public +class SMTPSLayer(ConfigLayer): + """Layer for starting, stopping, and accessing a test SMTPS server.""" + + smtpd = None + + @classmethod + def setUp(cls): + assert cls.smtpd is None, 'Layer already set up' + # Use a different port than the SMTP layer, since that one might + # still be in use. + config.push('smtps', """ + [mta] + smtp_port: 9026 + smtp_protocol: smtps + """) + test_cert_path = resource_filename('mailman.testing', + 'ssl_test_cert.crt') + test_key_path = resource_filename('mailman.testing', + 'ssl_test_key.key') + + client_context = ssl.create_default_context() + client_context.load_verify_locations(cafile=test_cert_path) + + server_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + server_context.load_cert_chain(test_cert_path, test_key_path) + + host = config.mta.smtp_host + port = int(config.mta.smtp_port) + + cls.smtpd = ConnectionCountingSSLController( + host, port, + client_context=client_context, + server_context=server_context) + cls.smtpd.start() + + @classmethod + def testSetUp(cls): + # Make sure we don't call our superclass's testSetUp(), otherwise the + # example.com domain will get added twice. + pass + + @classmethod + def testTearDown(cls): + cls.smtpd.reset() + cls.smtpd.clear() + + @classmethod + def tearDown(cls): + assert cls.smtpd is not None, 'Layer not set up' + cls.smtpd.clear() + cls.smtpd.stop() + config.pop('smtps') + + +@public +class STARTTLSLayer(ConfigLayer): + """Layer for starting and stopping a test SMTP server with STARTTLS.""" + + smtpd = None + + @classmethod + def setUp(cls): + assert cls.smtpd is None, 'Layer already set up' + # Use a different port than the SMTP and SMTPS layers, since that one + # might still be in use. + config.push('starttls', """ + [mta] + smtp_port: 9027 + smtp_protocol: starttls + """) + test_cert_path = resource_filename('mailman.testing', + 'ssl_test_cert.crt') + test_key_path = resource_filename('mailman.testing', + 'ssl_test_key.key') + + client_context = ssl.create_default_context() + client_context.load_verify_locations(cafile=test_cert_path) + + server_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + server_context.load_cert_chain(test_cert_path, test_key_path) + + host = config.mta.smtp_host + port = int(config.mta.smtp_port) + + cls.smtpd = ConnectionCountingSTARTLSController( + host, port, + client_context=client_context, + server_context=server_context) + cls.smtpd.start() + + @classmethod + def testSetUp(cls): + # Make sure we don't call our superclass's testSetUp(), otherwise the + # example.com domain will get added twice. + pass + + @classmethod + def testTearDown(cls): + cls.smtpd.reset() + cls.smtpd.clear() + + @classmethod + def tearDown(cls): + assert cls.smtpd is not None, 'Layer not set up' + cls.smtpd.clear() + cls.smtpd.stop() + config.pop('starttls') + + +@public class LMTPLayer(ConfigLayer): """Layer for starting, stopping, and accessing a test LMTP server.""" diff --git a/src/mailman/testing/mta.py b/src/mailman/testing/mta.py index ef4d39233..02a3b83d2 100644 --- a/src/mailman/testing/mta.py +++ b/src/mailman/testing/mta.py @@ -166,12 +166,14 @@ class ConnectionCountingSMTP(SMTP): class ConnectionCountingController(Controller): """Count the number of SMTP connections opened.""" - def __init__(self, host, port): + def __init__(self, host, port, ssl_context=None): self._msg_queue = Queue() self._oob_queue = Queue() self.err_queue = Queue() + handler = ConnectionCountingHandler(self._msg_queue) - super().__init__(handler, hostname=host, port=port) + super().__init__(handler, hostname=host, port=port, + ssl_context=ssl_context) def factory(self): return ConnectionCountingSMTP( @@ -184,8 +186,7 @@ class ConnectionCountingController(Controller): self.reset() def _connect(self): - client = smtplib.SMTP() - client.connect(self.hostname, self.port) + client = smtplib.SMTP(self.hostname, self.port) return client def get_connection_count(self): @@ -223,3 +224,47 @@ class ConnectionCountingController(Controller): def reset(self): client = self._connect() client.docmd('RSET') + + +class ConnectionCountingSSLController(ConnectionCountingController): + """Count the number of SMTPS connections opened.""" + + def __init__(self, host, port, client_context=None, server_context=None): + super().__init__(host, port, ssl_context=server_context) + self._client_context = client_context + + def _connect(self): + client = smtplib.SMTP_SSL(self.hostname, self.port, + context=self._client_context) + return client + + +class ConnectionCountingSTARTLSController(ConnectionCountingController): + """Count the number of SMTP connections with STARTTLS opened.""" + + def __init__(self, host, port, client_context=None, server_context=None): + super().__init__(host, port) + self._client_context = client_context + self._server_context = server_context + + def factory(self): + return ConnectionCountingSMTP( + self.handler, self._oob_queue, + self.err_queue, tls_context=self._server_context, + require_starttls=True) + + def _connect(self): + client = smtplib.SMTP(self.hostname, self.port) + client.starttls(context=self._client_context) + return client + + def get_connection_count(self): + count = super().get_connection_count() + # This is a hack, since when using STARTTLS for every connection the + # SMTP.connection_made(transport) method is called twice. + # The -1 is there for the last pair of connections made when checking + # the connection count, since one of them is already subtracted in + # ConnectionCountingSMTP.handle_STAT. + # The //2 is there for the rest of the connections which are counted + # twice. + return (count - 1) // 2 diff --git a/src/mailman/testing/ssl_test_cert.crt b/src/mailman/testing/ssl_test_cert.crt new file mode 100644 index 000000000..06ae8ac9d --- /dev/null +++ b/src/mailman/testing/ssl_test_cert.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDeDCCAmCgAwIBAgIJANA3MRKsRL/OMA0GCSqGSIb3DQEBCwUAMFExCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJNQTEhMB8GA1UECgwYTWFpbG1hbiB0ZXN0IGNlcnRp +ZmljYXRlMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTcwNTI3MTYxNzIzWhcNMjcw +NTI1MTYxNzIzWjBRMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTUExITAfBgNVBAoM +GE1haWxtYW4gdGVzdCBjZXJ0aWZpY2F0ZTESMBAGA1UEAwwJbG9jYWxob3N0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycCl/y9Cpm0jW921eIraIieh +cxXPrYed66SxZwoZNt8StaFfNOeqykIiFpUR/5KYc1Sk0yF2tGfFv6l17xG0fuCI +OkH1EynhXZiPbdcvmqF2W6Fa1g5kZJ//KbhK4rRsUEVp/2pHVjMG6toenM+S/1LE +35XiNyIHF6ZeENAb/zR3ikjYH0MSEq2doBmKd9/m48+z5Vb+MbyIG7jbvN3MQ9f/ +NXAIwcGf37RO5+yNIcvqTV9SMBAZb1l+rMrsks6Ghr3NoBP1kuqJF5vFropKBx7R +1MKvYm2ISL5UaWbFPYs9A4VhtzhMWBLZGHQPFHnR7dub7et8eiC7fbsdx4uyGwID +AQABo1MwUTAdBgNVHQ4EFgQUe1Q+FbXTQjLoeTuSlxQQCeE+jpQwHwYDVR0jBBgw +FoAUe1Q+FbXTQjLoeTuSlxQQCeE+jpQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQsFAAOCAQEANbliLrarZfinHQ+/TQIOdqolsscw7RtyFtB7wA2uqWNsH/77 +EYDiv8AhpAQB3dzKwGdapgYgsWUe2awSD67TAJg5XS/h9F1PbbjDYGC9v6T7HDPd +SfGV34LbWmH60rE6B1lwyCACnOXhByFGrdicCm2Z073kTp5L7WuAqpwC1f3aMC0u +9goUWHSfqSIPPIv8yiV0vgKk4kP4KV/gi20x74c31pujExCiVGA4GGTXV9hqmAm7 +tZuPTYFN5n60U1tzbFXOuwK6CvCDzWeq27mYwNHTXSJw1GyG4XXM5t2wfSlu/4e4 +xDuZwvP0P7hTME5wC7KGeS7daYSWNCDiHa7g6A== +-----END CERTIFICATE----- diff --git a/src/mailman/testing/ssl_test_key.key b/src/mailman/testing/ssl_test_key.key new file mode 100644 index 000000000..619556893 --- /dev/null +++ b/src/mailman/testing/ssl_test_key.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJwKX/L0KmbSNb +3bV4itoiJ6FzFc+th53rpLFnChk23xK1oV8056rKQiIWlRH/kphzVKTTIXa0Z8W/ +qXXvEbR+4Ig6QfUTKeFdmI9t1y+aoXZboVrWDmRkn/8puEritGxQRWn/akdWMwbq +2h6cz5L/UsTfleI3IgcXpl4Q0Bv/NHeKSNgfQxISrZ2gGYp33+bjz7PlVv4xvIgb +uNu83cxD1/81cAjBwZ/ftE7n7I0hy+pNX1IwEBlvWX6syuySzoaGvc2gE/WS6okX +m8WuikoHHtHUwq9ibYhIvlRpZsU9iz0DhWG3OExYEtkYdA8UedHt25vt63x6ILt9 +ux3Hi7IbAgMBAAECggEATRjFVmLlAVwrauuqcUn+WZbzZ1sqZZGxk174O/vr7sAI +Ekh8bWcqKOhkxmRo4FVQ1KG/6r6a8g3Fz5weaSFG7EU5Sany0UPrzyyBguP8WQbi +h9l9MNeHHbzWcUbvtvpjeblM7EHcyN/vAMghcqMP9WnXuek47QCf3TXCNIKScE8a +sU01o7Lw4CkZOLP9UyLmoKiQpurj7jWOMq10QOG4693dnfcgz7voPZPrt8OnAo7x +ap4ojK9nWfjFHFxpsddBhXBxzXw/U8FxtzYtCbzxL7iYpxpq0GEc/5eF8O+RE4MX +D1uqrkLfVM4HMOgQoFLrSGcPahYSVRrQ3X+lYV+OQQKBgQDotmodstq4T3D+7buY +J3zi+saEDvcPxgy31934jm465WfNiYSrnq5PqJCkzVpYMPuj0O8za75OoyRK2viT +glBKjP0vxNXh91tTi4eU7LCFPNwOwnt+e3tu+eA4klFpQIEUI8PHlEe4VBGlvpsD +DoPHuIHCouAEXhpu/ck58+QxKQKBgQDd8Rshc7h8PY26Rxrzgjob9PvDHxQODodU +iRUAELscSkr4gwYTgmDL4vL+lOog7IAYGmSOsHLJ19wTbLsaPMPfhUh+zXuvwlA5 +/01uYFp0UM4ax4DdTGPA5qOAIkLY3rL1x6U0eBf6XJhdG8mTW9SjbD1aCbDGdZk6 +9KJVUlDdowKBgD4NHeCLZ1zL+gJP27ynktpnKfXek6xGD/AZhFuZhvT3ZKVerNyi +NDKTbPY0t4lajk7REGcyrI0FXVEEcFHM5qHqVDyfjLRzI4v0YZOpRSxR3Q+mdg10 +2aXuxQXwpfqds41uN+8Ir9MLv6TlXSoEfckMfrUqfvdLLFs6GqT0Tn15AoGBANG0 +05HUKekasCPms8yKrCVmYcyIPQbbK3vw2uro5CNi/1u5UbB1bMi5dCigxGi/jnk3 +1vQMPSoC0Gt6PYAZEmrNISbPOaNk0zE5zgwQ9ucYwuYCw/xWBZtrUensdYU9R5N8 +RNlC8EUb4Mt5Sgn2pwCTcZT1uxaKX3KZXBmKYeZJAoGAAtWlzPxgwDglNhhuLpgW +vSOH0lpHlR//Ne7kMPCfG4lrAaPo5VSfQrjlzoF9tlhu0IuRKnwOg4HmFD9COaLA +RWTNDLMxBct5lZNAjgIzI5nZm6VLioCQkAdThzSBTxpGrIR0iDR99RWf/M4p7cK6 +tiqX285r1nogUpROl0uraV4= +-----END PRIVATE KEY----- |
