From 938cada00298243f0cf51c9bfd460ecb16938b57 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 24 Mar 2015 15:07:51 -0300 Subject: [bug] correctly return async bouncer deferred --- changes/bug_correctly-return-async-bouncer-deferred | 1 + src/leap/mx/mail_receiver.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changes/bug_correctly-return-async-bouncer-deferred diff --git a/changes/bug_correctly-return-async-bouncer-deferred b/changes/bug_correctly-return-async-bouncer-deferred new file mode 100644 index 0000000..15d3691 --- /dev/null +++ b/changes/bug_correctly-return-async-bouncer-deferred @@ -0,0 +1 @@ + o Correctly return async bouncer deferred. diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index 630c982..7856594 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -96,6 +96,10 @@ class BouncerSubprocessProtocol(protocol.ProcessProtocol): self._errBuffer = "" self._d = None + @property + def deferred(self): + return self._d + def connectionMade(self): self._d = defer.Deferred() @@ -130,7 +134,7 @@ def async_check_output(args, msg): """ pprotocol = BouncerSubprocessProtocol(msg) reactor.spawnProcess(pprotocol, args[0], args) - return pprotocol.d + return pprotocol.deferred class MailReceiver(Service): -- cgit v1.2.3 From 5a45acd3486f4e7f830647953731353cda916d51 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 24 Mar 2015 15:09:18 -0300 Subject: [feat] reject incoming mail if no pgp key found Implement a PGP key lookup in the postfix smtp recipient restriction and virtual alias mapping levels. If no PGP key is found, then the address is rejected with a temporary error. Closes: #6795 --- changes/bug_6795_reject-mail-if-no-pgp-key-found | 1 + src/leap/mx/alias_resolver.py | 45 +++++++++++------------- src/leap/mx/check_recipient_access.py | 5 ++- 3 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 changes/bug_6795_reject-mail-if-no-pgp-key-found diff --git a/changes/bug_6795_reject-mail-if-no-pgp-key-found b/changes/bug_6795_reject-mail-if-no-pgp-key-found new file mode 100644 index 0000000..7b9ef1f --- /dev/null +++ b/changes/bug_6795_reject-mail-if-no-pgp-key-found @@ -0,0 +1 @@ + o Reject mail if no PGP key was found for a user. Closes #6795. diff --git a/src/leap/mx/alias_resolver.py b/src/leap/mx/alias_resolver.py index 45a3ed2..4247b57 100644 --- a/src/leap/mx/alias_resolver.py +++ b/src/leap/mx/alias_resolver.py @@ -28,10 +28,11 @@ TODO: try: # TODO: we should probably use the system alias somehow - # from twisted.mail import alias + # from twisted.mail import alias from twisted.protocols import postfix from twisted.python import log from twisted.internet import defer + from twisted.internet.protocol import ServerFactory except ImportError: print "This software requires Twisted. Please see the README file" print "for instructions on getting required dependencies." @@ -39,19 +40,20 @@ except ImportError: class LEAPPostFixTCPMapserver(postfix.PostfixTCPMapServer): def _cbGot(self, value): - if value is None: + uuid, pubkey = value + if uuid is None: self.sendCode(500, postfix.quote("NOT FOUND SRY")) + elif pubkey is None: + self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: self.sendCode(200, postfix.quote(value)) -class AliasResolverFactory(postfix.PostfixTCPMapDeferringDictServerFactory): +class AliasResolverFactory(ServerFactory): protocol = LEAPPostFixTCPMapserver - def __init__(self, couchdb, *args, **kwargs): - postfix.PostfixTCPMapDeferringDictServerFactory.__init__( - self, *args, **kwargs) + def __init__(self, couchdb): self._cdb = couchdb def _to_str(self, result): @@ -64,14 +66,14 @@ class AliasResolverFactory(postfix.PostfixTCPMapDeferringDictServerFactory): log.msg("Result not found") return result - def spit_result(self, result): - """ - Formats the return codes in a postfix friendly format. - """ - if result is None: - return None - else: - return defer.succeed(result) + def _getPubKey(self, uuid): + if uuid is None: + return defer.succeed([None, None]) + d = defer.gatherResults([ + self._to_str(uuid), + self._cdb.getPubKey(uuid), + ]) + return d def get(self, key): """ @@ -79,13 +81,8 @@ class AliasResolverFactory(postfix.PostfixTCPMapDeferringDictServerFactory): At some point we will have to consider the domain part too. """ - try: - log.msg("Query key: %s" % (key,)) - d = self._cdb.queryByAddress(key) - - d.addCallback(self._to_str) - d.addCallback(self.spit_result) - d.addErrback(log.err) - return d - except Exception as e: - log.err('exception in get: %r' % e) + log.msg("Query key: %s" % (key,)) + d = self._cdb.queryByAddress(key) + d.addCallback(self._getPubKey) + d.addErrback(log.err) + return d diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index b80ccfd..d4ae339 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -32,8 +32,11 @@ class LEAPPostFixTCPMapserverAccess(postfix.PostfixTCPMapServer): # For more info, see: # http://www.postfix.org/tcp_table.5.html # http://www.postfix.org/access.5.html - if value is None: + uuid, pubkey = value + if uuid is None: self.sendCode(500, postfix.quote("REJECT")) + elif pubkey is None: + self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: self.sendCode(200, postfix.quote("OK")) -- cgit v1.2.3 From b5309dc5910f35f5320c649be2ce2c6147030b39 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 25 Mar 2015 15:47:28 -0300 Subject: [refactor] separate tcp map server code Separate the common tcp map server code, used for both alias resolver and recipient access checker, to its own file. --- src/leap/mx/alias_resolver.py | 84 ++++++++++++++--------------------- src/leap/mx/check_recipient_access.py | 40 ++++++++++++----- src/leap/mx/tcp_map.py | 76 +++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 60 deletions(-) create mode 100644 src/leap/mx/tcp_map.py diff --git a/src/leap/mx/alias_resolver.py b/src/leap/mx/alias_resolver.py index 4247b57..9206ffb 100644 --- a/src/leap/mx/alias_resolver.py +++ b/src/leap/mx/alias_resolver.py @@ -24,65 +24,49 @@ Test this with postmap -v -q "foo" tcp:localhost:4242 TODO: o Look into using twisted.protocols.postfix.policies classes for controlling concurrent connections and throttling resource consumption. + o We should probably use twisted.mail.alias somehow. """ -try: - # TODO: we should probably use the system alias somehow - # from twisted.mail import alias - from twisted.protocols import postfix - from twisted.python import log - from twisted.internet import defer - from twisted.internet.protocol import ServerFactory -except ImportError: - print "This software requires Twisted. Please see the README file" - print "for instructions on getting required dependencies." +from twisted.protocols import postfix -class LEAPPostFixTCPMapserver(postfix.PostfixTCPMapServer): - def _cbGot(self, value): - uuid, pubkey = value - if uuid is None: - self.sendCode(500, postfix.quote("NOT FOUND SRY")) - elif pubkey is None: - self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) - else: - self.sendCode(200, postfix.quote(value)) - +from leap.mx.tcp_map import LEAPostfixTCPMapServerFactory +from leap.mx.tcp_map import TCP_MAP_CODE_SUCCESS +from leap.mx.tcp_map import TCP_MAP_CODE_TEMPORARY_FAILURE +from leap.mx.tcp_map import TCP_MAP_CODE_PERMANENT_FAILURE -class AliasResolverFactory(ServerFactory): - protocol = LEAPPostFixTCPMapserver +class LEAPPostfixTCPMapAliasServer(postfix.PostfixTCPMapServer): + """ + A postfix tcp map alias resolver server. + """ - def __init__(self, couchdb): - self._cdb = couchdb - - def _to_str(self, result): - """ - Properly encodes the result string if any. + def _cbGot(self, value): """ - if isinstance(result, unicode): - result = result.encode("utf8") - if result is None: - log.msg("Result not found") - return result + Return a code and message depending on the result of the factory's + get(). - def _getPubKey(self, uuid): + :param value: The uuid and public key. + :type value: list + """ + uuid, pubkey = value if uuid is None: - return defer.succeed([None, None]) - d = defer.gatherResults([ - self._to_str(uuid), - self._cdb.getPubKey(uuid), - ]) - return d + self.sendCode( + TCP_MAP_CODE_PERMANENT_FAILURE, + postfix.quote("NOT FOUND SRY")) + elif pubkey is None: + self.sendCode( + TCP_MAP_CODE_TEMPORARY_FAILURE, + postfix.quote("4.7.13 USER ACCOUNT DISABLED")) + else: + self.sendCode( + TCP_MAP_CODE_SUCCESS, + postfix.quote(uuid)) - def get(self, key): - """ - Looks up the passed key, but only up to the username id of the key. - At some point we will have to consider the domain part too. - """ - log.msg("Query key: %s" % (key,)) - d = self._cdb.queryByAddress(key) - d.addCallback(self._getPubKey) - d.addErrback(log.err) - return d +class AliasResolverFactory(LEAPostfixTCPMapServerFactory): + """ + A factory for postfix tcp map alias resolver servers. + """ + + protocol = LEAPPostfixTCPMapAliasServer diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index d4ae339..cf172c7 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -24,22 +24,42 @@ Test this with postmap -v -q "foo" tcp:localhost:2244 from twisted.protocols import postfix -from leap.mx.alias_resolver import AliasResolverFactory +from leap.mx.tcp_map import LEAPPostfixTCPMapServerFactory +from leap.mx.tcp_map import TCP_MAP_CODE_SUCCESS +from leap.mx.tcp_map import TCP_MAP_CODE_TEMPORARY_FAILURE +from leap.mx.tcp_map import TCP_MAP_CODE_PERMANENT_FAILURE -class LEAPPostFixTCPMapserverAccess(postfix.PostfixTCPMapServer): +class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): + """ + A postfix tcp map recipient access checker server. + """ + def _cbGot(self, value): - # For more info, see: - # http://www.postfix.org/tcp_table.5.html - # http://www.postfix.org/access.5.html + """ + Return a code and message depending on the result of the factory's + get(). + + For more info, see: http://www.postfix.org/access.5.html + + :param value: The uuid and public key. + :type value: list + """ uuid, pubkey = value if uuid is None: - self.sendCode(500, postfix.quote("REJECT")) + self.sendCode( + TCP_MAP_CODE_PERMANENT_FAILURE, + postfix.quote("REJECT")) elif pubkey is None: - self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) + self.sendCode( + TCP_MAP_CODE_TEMPORARY_FAILURE, + postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: - self.sendCode(200, postfix.quote("OK")) + self.sendCode( + TCP_MAP_CODE_SUCCESS, + postfix.quote("OK")) + +class CheckRecipientAccessFactory(LEAPPostfixTCPMapServerFactory): -class CheckRecipientAccessFactory(AliasResolverFactory): - protocol = LEAPPostFixTCPMapserverAccess + protocol = LEAPPostFixTCPMapAccessServer diff --git a/src/leap/mx/tcp_map.py b/src/leap/mx/tcp_map.py new file mode 100644 index 0000000..b7066ff --- /dev/null +++ b/src/leap/mx/tcp_map.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# tcpmap.py +# Copyright (C) 2015 LEAP +# +# This program 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. +# +# This program 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 this program. If not, see . + + +from twisted.python import log +from twisted.internet import defer +from twisted.internet.protocol import ServerFactory + + +# For info on codes, see: http://www.postfix.org/tcp_table.5.html +TCP_MAP_CODE_SUCCESS = 200 +TCP_MAP_CODE_TEMPORARY_FAILURE = 400 +TCP_MAP_CODE_PERMANENT_FAILURE = 500 + + +class LEAPPostfixTCPMapServerFactory(ServerFactory): + """ + A factory for postfix tcp map servers. + """ + + def __init__(self, couchdb): + """ + Initialize the factory. + + :param couchdb: A CouchDB client. + :type couchdb: leap.mx.couchdbhelper.ConnectedCouchDB + """ + self._cdb = couchdb + + def _getPubKey(self, uuid): + """ + Look up PGP public key based on user uid. + + :param uuid: The user uid. + :type uuid: str + + :return: A deferred that is fired with the uuid and the public key, if + available. + :rtype: DeferredList + """ + if uuid is None: + return defer.succeed([None, None]) + return defer.gatherResults([ + defer.succeed(uuid), + self._cdb.getPubKey(uuid), + ]) + + def get(self, key): + """ + Look up uuid based on key, only up to the username id of the key. + + At some point we will have to consider the domain part too. + + :param key: The lookup key. + :type key: str + """ + log.msg("Query key: %s" % (key,)) + d = self._cdb.queryByAddress(key) + d.addCallback(self._getPubKey) + d.addErrback(log.err) + return d -- cgit v1.2.3 From e8fd9feb5891b6cd0840afdcae996314ea3849a9 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 26 Mar 2015 10:44:18 -0400 Subject: [docs] add leap standard commit template to repo Because in CDO we trust. --- doc/leap-commit-template | 7 ++++++ doc/leap-commit-template.README | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 doc/leap-commit-template create mode 100644 doc/leap-commit-template.README diff --git a/doc/leap-commit-template b/doc/leap-commit-template new file mode 100644 index 0000000..8a5c7cd --- /dev/null +++ b/doc/leap-commit-template @@ -0,0 +1,7 @@ +[bug|feat|docs|style|refactor|test|pkg|i18n] ... +... + +- Resolves: #XYZ +- Related: #XYZ +- Documentation: #XYZ +- Releases: XYZ diff --git a/doc/leap-commit-template.README b/doc/leap-commit-template.README new file mode 100644 index 0000000..ce8809e --- /dev/null +++ b/doc/leap-commit-template.README @@ -0,0 +1,47 @@ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +HOW TO USE THIS TEMPLATE: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Run `git config commit.template docs/leap-commit-template` or +edit the .git/config for this project and add +`template = docs/leap-commit-template` +under the [commit] block + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +COMMIT TEMPLATE FORMAT EXPLAINED +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[type] + + +