From 5a05017ecf1db8e3123dbdd02af438df06607113 Mon Sep 17 00:00:00 2001 From: Ian Good Date: Sun, 16 Feb 2020 22:34:56 -0500 Subject: [PATCH] Support pysasl 0.5 --- .travis.yml | 3 +-- setup.py | 16 +++++++-------- slimta/smtp/auth.py | 30 ++++++++++++--------------- slimta/smtp/client.py | 2 +- test/test_slimta_smtp_auth.py | 36 ++++++++++++++++----------------- test/test_slimta_smtp_client.py | 4 ++-- test/test_slimta_smtp_server.py | 2 +- 7 files changed, 42 insertions(+), 51 deletions(-) diff --git a/.travis.yml b/.travis.yml index a394177c..44606459 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,8 @@ language: python python: - - "2.7" - - "3.5" - "3.6" - "3.7" + - "3.8" dist: xenial # https://github.com/travis-ci/travis-ci/issues/9069#issuecomment-425720905 install: - travis_retry pip install -r test/requirements.txt diff --git a/setup.py b/setup.py index 79a61bee..6eb74017 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ setup(name='python-slimta', - version='4.0.9', + version='5.0.0', author='Ian Good', author_email='icgood@gmail.com', description='Lightweight, asynchronous SMTP libraries.', @@ -31,20 +31,18 @@ url='http://slimta.org/', packages=find_packages(), namespace_packages=['slimta'], - install_requires=['gevent >= 1.1rc', - 'pysasl >= 0.4.0, < 0.5', - 'pycares < 3.0.0; python_version < "3.0"', - 'pycares >= 1; python_version >= "3.0"'], + install_requires=['gevent >= 1.1.0', + 'pysasl >= 0.5.0', + 'pycares >= 1'], classifiers=['Development Status :: 3 - Alpha', 'Topic :: Communications :: Email :: Mail Transport Agents', 'Intended Audience :: Developers', 'Intended Audience :: Information Technology', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6']) + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8']) # vim:et:fdm=marker:sts=4:sw=4:ts=4 diff --git a/slimta/smtp/auth.py b/slimta/smtp/auth.py index 18804390..ac54c278 100644 --- a/slimta/smtp/auth.py +++ b/slimta/smtp/auth.py @@ -24,7 +24,7 @@ import re import base64 -from pysasl import AuthenticationError, ServerChallenge, \ +from pysasl import AuthenticationError, ServerChallenge, ChallengeResponse, \ AuthenticationCredentials from . import SmtpError @@ -107,12 +107,11 @@ def _parse_arg(self, arg): @property def server_mechanisms(self): - return [mech for mech in self.auth.server_mechanisms] + return self.auth.server_mechanisms @property def client_mechanisms(self): - return [mech for mech in self.auth.client_mechanisms - if self.io.encrypted or not getattr(mech, 'insecure', False)] + return self.auth.client_mechanisms def _server_challenge(self, challenge, response=None): if not response: @@ -141,11 +140,9 @@ def server_attempt(self, arg): except AuthenticationError as exc: raise UnexpectedAuthError(exc) except ServerChallenge as chal: - resp = self._server_challenge(chal.challenge, - mechanism_arg) + resp = self._server_challenge(chal.data, mechanism_arg) mechanism_arg = None - chal.set_response(resp) - responses.append(chal) + responses.append(ChallengeResponse(chal.data, resp)) raise InvalidMechanismError() def _client_respond(self, mech, response, first=False): @@ -167,19 +164,18 @@ def _client_respond(self, mech, response, first=False): def client_attempt(self, authcid, secret, authzid, mech_name): if not mech_name: raise InvalidMechanismError() - mechanism = self.auth.get_client(mech_name) - if not mechanism: + mech = self.auth.get_client(mech_name) + if not mech: raise InvalidMechanismError() creds = AuthenticationCredentials(authcid, secret, authzid) - resp = mechanism.client_attempt(creds, []) + resp = mech.client_attempt(creds, []) chal, reply = self._client_respond( - mechanism, resp.get_response(), True) - responses = [resp] + mech, resp.response, True) + challenges = [ServerChallenge(chal)] while chal is not None: - resp.set_challenge(chal) - resp = mechanism.client_attempt(creds, responses) - responses.append(resp) - chal, reply = self._client_respond(mechanism, resp.get_response()) + resp = mech.client_attempt(creds, challenges) + chal, reply = self._client_respond(mech, resp.response) + challenges.append(ServerChallenge(chal)) return reply diff --git a/slimta/smtp/client.py b/slimta/smtp/client.py index 57721600..169af839 100644 --- a/slimta/smtp/client.py +++ b/slimta/smtp/client.py @@ -256,7 +256,7 @@ def auth(self, authcid, secret, authzid=None, mechanism=None): return unknown_command advertised = [self._encode(mech_name) for mech_name in self.extensions.getparam('AUTH').split()] - auth = AuthSession(SASLAuth(advertised), self.io) + auth = AuthSession(SASLAuth.named(advertised), self.io) if not mechanism and auth.client_mechanisms: mechanism = auth.client_mechanisms[0].name return auth.client_attempt(authcid, secret, authzid, mechanism) diff --git a/test/test_slimta_smtp_auth.py b/test/test_slimta_smtp_auth.py index b5fa0743..4f1f08f4 100644 --- a/test/test_slimta_smtp_auth.py +++ b/test/test_slimta_smtp_auth.py @@ -21,11 +21,11 @@ def setUp(self): self.make_msgid = email.utils.make_msgid = lambda: '' def test_bytes(self): - auth = AuthSession(SASLAuth(), self.io) - self.assertEqual('PLAIN LOGIN CRAM-MD5', str(auth)) + auth = AuthSession(SASLAuth.defaults(), self.io) + self.assertEqual('PLAIN LOGIN', str(auth)) def test_invalid_mechanism(self): - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) with self.assertRaises(InvalidMechanismError): auth.server_attempt(b'TEST') with self.assertRaises(InvalidMechanismError): @@ -35,7 +35,7 @@ def test_plain_noarg(self): self.sock.sendall(b'334 \r\n') self.sock.recv(IsA(int)).AndReturn(b'dGVzdHppZAB0ZXN0dXNlcgB0ZXN0cGFzc3dvcmQ=\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) result = auth.server_attempt(b'PLAIN') self.assertEqual(u'testuser', result.authcid) self.assertEqual(u'testpassword', result.secret) @@ -43,7 +43,7 @@ def test_plain_noarg(self): def test_plain(self): self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) result = auth.server_attempt(b'PLAIN dGVzdHppZAB0ZXN0dXNlcgB0ZXN0cGFzc3dvcmQ=') self.assertEqual(u'testuser', result.authcid) self.assertEqual(u'testpassword', result.secret) @@ -53,7 +53,7 @@ def test_plain_canceled(self): self.sock.sendall(b'334 \r\n') self.sock.recv(IsA(int)).AndReturn(b'*\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) with self.assertRaises(AuthenticationCanceled): auth.server_attempt(b'PLAIN') with self.assertRaises(AuthenticationCanceled): @@ -65,7 +65,7 @@ def test_login_noarg(self): self.sock.sendall(b'334 UGFzc3dvcmQ6\r\n') self.sock.recv(IsA(int)).AndReturn(b'dGVzdHBhc3N3b3Jk\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) result = auth.server_attempt(b'LOGIN') self.assertEqual(u'testuser', result.authcid) self.assertEqual(u'testpassword', result.secret) @@ -75,7 +75,7 @@ def test_login(self): self.sock.sendall(b'334 UGFzc3dvcmQ6\r\n') self.sock.recv(IsA(int)).AndReturn(b'dGVzdHBhc3N3b3Jk\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) result = auth.server_attempt(b'LOGIN dGVzdHVzZXI=') self.assertEqual(u'testuser', result.authcid) self.assertEqual(u'testpassword', result.secret) @@ -85,7 +85,7 @@ def test_crammd5(self): self.sock.sendall(b'334 PHRlc3RAZXhhbXBsZS5jb20+\r\n') self.sock.recv(IsA(int)).AndReturn(b'dGVzdHVzZXIgNDkzMzA1OGU2ZjgyOTRkZTE0NDJkMTYxOTI3ZGI5NDQ=\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.named([b'CRAM-MD5']), self.io) result = auth.server_attempt(b'CRAM-MD5') self.assertEqual(u'testuser', result.authcid) self.assertTrue(result.check_secret(u'testpassword')) @@ -96,7 +96,7 @@ def test_crammd5_malformed(self): self.sock.sendall(b'334 PHRlc3RAZXhhbXBsZS5jb20+\r\n') self.sock.recv(IsA(int)).AndReturn(b'bWFsZm9ybWVk\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.named([b'CRAM-MD5']), self.io) with self.assertRaises(ServerAuthError): auth.server_attempt(b'CRAM-MD5') @@ -104,7 +104,7 @@ def test_client_bad_mech(self): self.sock.sendall(b'AUTH LOGIN\r\n') self.sock.recv(IsA(int)).AndReturn(b'535 Nope!\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) reply = auth.client_attempt(u'test@example.com', u'asdf', None, b'LOGIN') self.assertEqual('535', reply.code) @@ -114,7 +114,7 @@ def test_client_plain(self): self.sock.sendall(b'AUTH PLAIN amtsAHRlc3RAZXhhbXBsZS5jb20AYXNkZg==\r\n') self.sock.recv(IsA(int)).AndReturn(b'235 Ok\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) reply = auth.client_attempt(u'test@example.com', u'asdf', u'jkl', b'PLAIN') self.assertEqual('235', reply.code) @@ -128,7 +128,7 @@ def test_client_login(self): self.sock.sendall(b'YXNkZg==\r\n') self.sock.recv(IsA(int)).AndReturn(b'235 Ok\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) reply = auth.client_attempt(u'test@example.com', u'asdf', None, b'LOGIN') self.assertEqual('235', reply.code) @@ -140,7 +140,7 @@ def test_client_login_bad_username(self): self.sock.sendall(b'dGVzdEBleGFtcGxlLmNvbQ==\r\n') self.sock.recv(IsA(int)).AndReturn(b'535 Nope!\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.defaults(), self.io) reply = auth.client_attempt(u'test@example.com', u'asdf', None, b'LOGIN') self.assertEqual('535', reply.code) @@ -152,7 +152,7 @@ def test_client_crammd5(self): self.sock.sendall(b'dGVzdEBleGFtcGxlLmNvbSA1Yzk1OTBjZGE3ZTgxMDY5Mzk2ZjhiYjlkMzU1MzE1Yg==\r\n') self.sock.recv(IsA(int)).AndReturn(b'235 Ok\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth(), self.io) + auth = AuthSession(SASLAuth.named([b'CRAM-MD5']), self.io) reply = auth.client_attempt(u'test@example.com', u'asdf', None, b'CRAM-MD5') self.assertEqual('235', reply.code) @@ -162,7 +162,7 @@ def test_client_xoauth2(self): self.sock.sendall(b'AUTH XOAUTH2 dXNlcj10ZXN0QGV4YW1wbGUuY29tAWF1dGg9QmVhcmVyYXNkZgEB\r\n') self.sock.recv(IsA(int)).AndReturn(b'235 Ok\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth([b'XOAUTH2']), self.io) + auth = AuthSession(SASLAuth.named([b'XOAUTH2']), self.io) reply = auth.client_attempt(u'test@example.com', u'asdf', None, b'XOAUTH2') self.assertEqual('235', reply.code) @@ -170,11 +170,9 @@ def test_client_xoauth2(self): def test_client_xoauth2_error(self): self.sock.sendall(b'AUTH XOAUTH2 dXNlcj10ZXN0QGV4YW1wbGUuY29tAWF1dGg9QmVhcmVyYXNkZgEB\r\n') - self.sock.recv(IsA(int)).AndReturn(b'334 eyJzdGF0dXMiOiI0MDEiLCJzY2hlbWVzIjoiYmVhcmVyIG1hYyIsInNjb3BlIjoiaHR0cHM6Ly9tYWlsLmdvb2dsZS5jb20vIn0K\r\n') - self.sock.sendall(b'\r\n') self.sock.recv(IsA(int)).AndReturn(b'535 Nope!\r\n') self.mox.ReplayAll() - auth = AuthSession(SASLAuth([b'XOAUTH2']), self.io) + auth = AuthSession(SASLAuth.named([b'XOAUTH2']), self.io) reply = auth.client_attempt(u'test@example.com', u'asdf', None, b'XOAUTH2') self.assertEqual('535', reply.code) diff --git a/test/test_slimta_smtp_client.py b/test/test_slimta_smtp_client.py index 2bab2a3d..84031da6 100644 --- a/test/test_slimta_smtp_client.py +++ b/test/test_slimta_smtp_client.py @@ -108,12 +108,12 @@ def test_auth(self): self.assertEqual('2.0.0 Ok', reply.message) self.assertEqual(b'AUTH', reply.command) - def test_auth_insecure(self): + def test_auth_invalid(self): self.mox.ReplayAll() client = Client(self.sock) client.extensions.add('AUTH', 'PLAIN') self.assertRaises(InvalidMechanismError, client.auth, - 'test@example.com', 'asdf') + 'test@example.com', 'asdf', mechanism='BAD') def test_auth_force_mechanism(self): self.sock.sendall(b'AUTH PLAIN AHRlc3RAZXhhbXBsZS5jb20AYXNkZg==\r\n') diff --git a/test/test_slimta_smtp_server.py b/test/test_slimta_smtp_server.py index 6c8ac865..6b0117a3 100644 --- a/test/test_slimta_smtp_server.py +++ b/test/test_slimta_smtp_server.py @@ -219,7 +219,7 @@ def test_auth(self): self.mox.ReplayAll() s = Server(self.sock, None) s.extensions.reset() - s.extensions.add('AUTH', AuthSession(SASLAuth([b'PLAIN']), s.io)) + s.extensions.add('AUTH', AuthSession(SASLAuth.named([b'PLAIN']), s.io)) s.handle() self.assertTrue(s.authed)