6 commits - bin/kolab_smtp_access_policy.py pykolab/auth pykolab/base.py

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Fri Sep 20 16:15:30 CEST 2013


 bin/kolab_smtp_access_policy.py |  132 ++++++++++++++++++++++++++--------------
 pykolab/auth/__init__.py        |   47 ++++++++------
 pykolab/auth/ldap/__init__.py   |   20 +++++-
 pykolab/base.py                 |    3 
 4 files changed, 135 insertions(+), 67 deletions(-)

New commits:
commit 9576dc98ad17e2eeb3c3c30ffc4fb5d2efe2c2c3
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Sep 20 15:15:06 2013 +0100

    Do not assume that because there is an sasl_username and such, the sender user can actually be found.

diff --git a/bin/kolab_smtp_access_policy.py b/bin/kolab_smtp_access_policy.py
index 63795ae..473c0a0 100755
--- a/bin/kolab_smtp_access_policy.py
+++ b/bin/kolab_smtp_access_policy.py
@@ -1026,9 +1026,10 @@ class PolicyRequest(object):
 
                 return True
 
-        self.verify_authenticity()
-        self.sasl_user_uses_alias = self.verify_alias()
+        if self.verify_authenticity() == False:
+            reject(_("Unverifiable sender."))
 
+        self.sasl_user_uses_alias = self.verify_alias()
 
         if not self.sasl_user_uses_alias:
             log.debug(_("Sender is not using an alias"), level=8)


commit 50d860036472d2a0bbd3ccd8d69f649d3b39983a
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Sep 20 15:09:05 2013 +0100

    Make the Kolab SMTP Access Policy smarter

diff --git a/bin/kolab_smtp_access_policy.py b/bin/kolab_smtp_access_policy.py
index c375e39..63795ae 100755
--- a/bin/kolab_smtp_access_policy.py
+++ b/bin/kolab_smtp_access_policy.py
@@ -66,6 +66,7 @@ log = pykolab.getLogger('pykolab.smtp_access_policy')
 
 conf = pykolab.getConf()
 
+auth = None
 mydomains = None
 
 #
@@ -798,16 +799,16 @@ class PolicyRequest(object):
             self.auth = Auth(sasl_domain)
 
         if verify_domain(sasl_domain):
-            if self.auth.secondary_domains.has_key(sasl_domain):
+            if self.auth.domains.has_key(sasl_domain):
                 log.debug(
                         _("Using authentication domain %s instead of %s") % (
-                                self.auth.secondary_domains[sasl_domain],
+                                self.auth.domains[sasl_domain],
                                 sasl_domain
                             ),
                         level=8
                     )
 
-                sasl_domain = self.auth.secondary_domains[sasl_domain]
+                sasl_domain = self.auth.domains[sasl_domain]
             else:
                 log.debug(
                         _("Domain %s is a primary domain") % (
@@ -1221,10 +1222,8 @@ def cache_insert(
             level=8
         )
 
-    cache_cleanup()
-
-    if not recipient == '':
-        recipients.append(recipient)
+    if not recipient == '' and recipients == []:
+        recipients = [recipient]
 
     for recipient in recipients:
         session.add(
@@ -1272,9 +1271,8 @@ def cache_update(
         if record.value == (int)(result):
             records.append(record)
 
-    if not recipient == '':
-        recipients.append(recipient)
-        recipient = ''
+    if not recipient == '' and recipients == []:
+        recipients = [recipient]
 
     for recipient in recipients:
         recipient_found = False
@@ -1324,24 +1322,16 @@ def expand_mydomains():
         Return a list of my domains.
     """
 
-    global mydomains
+    global auth,mydomains
 
     if not mydomains == None:
         return mydomains
 
-    auth = Auth()
     auth.connect()
 
-    mydomains = []
+    mydomains = auth.list_domains()
 
-    _mydomains = auth.list_domains()
-
-    for primary, secondaries in _mydomains:
-        mydomains.append(primary)
-        for secondary in secondaries:
-            mydomains.append(secondary)
-
-    return mydomains
+    return mydomains.keys()
 
 def normalize_address(email_address):
     """
@@ -1399,25 +1389,20 @@ def verify_domain(domain):
         Verify whether the domain is internal (mine) or external.
     """
 
-    global mydomains
+    global auth,mydomains
 
     if not mydomains == None:
-        return domain in mydomains
+        return domain in mydomains.keys()
 
-    auth = Auth()
     auth.connect()
 
     domain_verified = False
 
-    _mydomains = auth.list_domains()
-
-    for primary, secondaries in _mydomains:
-        if primary == domain:
-            domain_verified = True
-        elif domain in secondaries:
-            domain_verified = True
+    mydomains = auth.list_domains()
 
-    if domain_verified == None:
+    if not mydomains == None and mydomains.has_key(domain):
+        domain_verified = True
+    else:
         domain_verified = False
 
     return domain_verified
@@ -1453,6 +1438,8 @@ if __name__ == "__main__":
 
     conf.finalize_conf()
 
+    auth = Auth()
+
     cache = cache_init()
 
     policy_requests = {}


commit 55d43ba9bf0364dade6e9510c7eaea33695b92b2
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Sep 20 15:08:51 2013 +0100

    Adapt to the new logic of storing domain parameters

diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py
index ea8cea6..5c6d51c 100644
--- a/pykolab/auth/__init__.py
+++ b/pykolab/auth/__init__.py
@@ -99,7 +99,7 @@ class Auth(pykolab.base.Base):
                 section = 'kolab'
                 domain = conf.get('kolab', 'primary_domain')
         else:
-            self.list_domains()
+            self.list_domains(domain)
             section = domain
 
         log.debug(
@@ -107,9 +107,9 @@ class Auth(pykolab.base.Base):
                 level=9
             )
 
-        if self.secondary_domains.has_key(domain):
-            section = self.secondary_domains[domain]
-            domain = self.secondary_domains[domain]
+        if not self.domains == None and self.domains.has_key(domain):
+            section = self.domains[domain]
+            domain = self.domains[domain]
 
         log.debug(
                 _("Using section %s and domain %s") % (section,domain),
@@ -207,7 +207,7 @@ class Auth(pykolab.base.Base):
     def find_user(self, attr, value, **kw):
         return self._auth._find_user(attr, value, **kw)
 
-    def list_domains(self):
+    def list_domains(self, domain=None):
         """
             List the domains using the auth_mechanism setting in the kolab
             section of the configuration file, either ldap or sql or (...).
@@ -225,23 +225,27 @@ class Auth(pykolab.base.Base):
         # Find the domains in the authentication backend.
         kolab_primary_domain = conf.get('kolab', 'primary_domain')
 
-        try:
-            domains = self._auth._list_domains()
-        except:
-            if not self.domain == kolab_primary_domain:
-                return [(self.domain, [])]
-            else:
-                domains = []
+        if self.domains == None:
 
-        # If no domains are found, the primary domain is used.
-        if len(domains) < 1:
-            domains = [(kolab_primary_domain, [])]
-        else:
-            for primary, secondaries in domains:
-                for secondary in secondaries:
-                    self.secondary_domains[secondary] = primary
+            try:
+                domains = self._auth._list_domains(domain)
+            except:
+                if not self.domain == kolab_primary_domain:
+                    return { self.domain: [] }
+                else:
+                    domains = []
+
+            # If no domains are found, the primary domain is used.
+            if len(domains) < 1:
+                self.domains = { kolab_primary_domain: [] }
+            else:
+                self.domains = {}
+                for primary, secondaries in domains:
+                    self.domains[primary] = primary
+                    for secondary in secondaries:
+                        self.domains[secondary] = primary
 
-        return domains
+        return self.domains
 
     def synchronize(self, mode=0):
         self._auth.synchronize(mode=mode)


commit 91d7125c2e2b64634dfc376e7b9856fda6bddfa7
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Sep 20 15:03:08 2013 +0100

    Increase the efficiency of three frequently used queries:
    
      - The search for recipients does not have to include the nsuniqueid attribute,
      - Not all domains list have to include all domains,
      - There's no need to search for supported controls if we already know what controls are supported.

diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index 78a505f..042592b 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -393,7 +393,12 @@ class LDAP(pykolab.base.Base):
 
         kolab_filter = self._kolab_filter()
         recipient_address_attrs = self.config_get_list("mail_attributes")
-        result_attributes = recipient_address_attrs
+
+        result_attributes = []
+
+        for recipient_address_attr in recipient_address_attrs:
+            result_attributes.append(recipient_address_attr)
+
         result_attributes.append(self.config_get('unique_attribute'))
 
         _filter = "(|"
@@ -2000,7 +2005,7 @@ class LDAP(pykolab.base.Base):
 
         return _filter
 
-    def _list_domains(self):
+    def _list_domains(self, domain=None):
         """
             Find the domains related to this Kolab setup, and return a list of
             DNS domain names.
@@ -2028,6 +2033,9 @@ class LDAP(pykolab.base.Base):
         # If we haven't returned already, let's continue searching
         domain_filter = conf.get('ldap', 'domain_filter')
 
+        if not domain == None:
+            domain_filter = domain_filter.replace('*', domain)
+
         if domain_base_dn == None or domain_filter == None:
             return []
 
@@ -2502,6 +2510,14 @@ class LDAP(pykolab.base.Base):
             the first one supported.
         """
 
+        supported_controls = conf.get_list('ldap', 'supported_controls')
+
+        if not supported_controls == None and not len(supported_controls) < 1:
+            for control_num in [(int)(x) for x in supported_controls]:
+                self.ldap.supported_controls.append(
+                        SUPPORTED_LDAP_CONTROLS[control_num]['func']
+                    )
+
         if len(self.ldap.supported_controls) < 1:
             for control_num in SUPPORTED_LDAP_CONTROLS.keys():
                 log.debug(


commit 25a56ceb45e3195835995803f9893d539df2ad93
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Sep 20 15:01:11 2013 +0100

    Replace the use of secondary domains with just domains - all keys are all domains we have, and their parent (including the parent)

diff --git a/pykolab/base.py b/pykolab/base.py
index 207783c..9b4465b 100644
--- a/pykolab/base.py
+++ b/pykolab/base.py
@@ -33,9 +33,10 @@ class Base(object):
 
         # Placeholder primary_domain => [secondary_domains]. Should be updated
         # on auth backend _connect().
-        self.secondary_domains = {}
+        self.domains = None
 
         self.imap = IMAP()
+        self.domain_rootdns = {}
 
     def config_get(self, key1, key2=None):
         if not key2 == None:


commit 2cca00f83476179b987deb06bb3cd65b9cfdff68
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Fri Sep 20 13:53:36 2013 +0100

    Make sure the recipient checks are cached as well

diff --git a/bin/kolab_smtp_access_policy.py b/bin/kolab_smtp_access_policy.py
index c13bfaf..c375e39 100755
--- a/bin/kolab_smtp_access_policy.py
+++ b/bin/kolab_smtp_access_policy.py
@@ -48,7 +48,7 @@ except:
 from sqlalchemy.schema import Index
 from sqlalchemy.schema import UniqueConstraint
 
-sys.path = ['..'] + sys.path
+sys.path = ['..','.'] + sys.path
 
 import pykolab
 
@@ -66,13 +66,15 @@ log = pykolab.getLogger('pykolab.smtp_access_policy')
 
 conf = pykolab.getConf()
 
+mydomains = None
+
 #
 # Caching routines using SQLAlchemy.
 #
 # If creating the cache fails, we continue without any caching, significantly
 # increasing the load on LDAP.
 #
-cache_expire = 3600
+cache_expire = 86400
 try:
     metadata = MetaData()
 except:
@@ -767,7 +769,7 @@ class PolicyRequest(object):
                             )
                     )
 
-                return record[0].value
+                return records[0].value
 
         # TODO: Under some conditions, the recipient may not be fully qualified.
         # We'll cross that bridge when we get there, though.
@@ -775,6 +777,20 @@ class PolicyRequest(object):
             sasl_domain = recipient.split('@')[1]
         else:
             sasl_domain = conf.get('kolab', 'primary_domain')
+            recipient = "%s@%s" % (recipient,sasl_domain)
+
+        if not verify_domain(sasl_domain):
+            if not cache == False:
+                cache_update(
+                        function='verify_recipient',
+                        sender=self.sender,
+                        recipient=recipient,
+                        result=(int)(True),
+                        sasl_username=self.sasl_username,
+                        sasl_sender=self.sasl_sender
+                    )
+
+            return True
 
         if self.auth == None:
             self.auth = Auth(sasl_domain)
@@ -819,9 +835,19 @@ class PolicyRequest(object):
                 log.info(
                         _("This recipient address is related to multiple object entries and the SMTP Access Policy can therefore not restrict message flow")
                     )
+
+                cache_update(
+                        function='verify_recipient',
+                        sender=self.sender,
+                        recipient=normalize_address(recipient),
+                        result=(int)(True),
+                        sasl_username=self.sasl_username,
+                        sasl_sender=self.sasl_sender
+                    )
+
                 return True
             elif len(recipients) == 1:
-                recipient = { 'dn': recipients[0] }
+                _recipient = { 'dn': recipients[0] }
             else:
                 log.debug(
                         _("Recipient address %r not found. Allowing since the MTA was configured to accept the recipient.") % (
@@ -830,22 +856,31 @@ class PolicyRequest(object):
                         level=3
                     )
 
+                cache_update(
+                        function='verify_recipient',
+                        sender=self.sender,
+                        recipient=normalize_address(recipient),
+                        result=(int)(True),
+                        sasl_username=self.sasl_username,
+                        sasl_sender=self.sasl_sender
+                    )
+
                 return True
 
         elif isinstance(recipients, basestring):
-            recipient = {
+            _recipient = {
                     'dn': recipients
                 }
 
         # We have gotten an invalid recipient. We need to catch this case,
         # because testing can input invalid recipients, and so can faulty
         # applications, or misconfigured servers.
-        if not recipient['dn']:
+        if not _recipient['dn']:
             if not conf.allow_unauthenticated:
                 cache_update(
                         function='verify_recipient',
                         sender=self.sender,
-                        recipient=recipient,
+                        recipient=normalize_address(recipient),
                         result=(int)(False),
                         sasl_username=self.sasl_username,
                         sasl_sender=self.sasl_sender
@@ -856,7 +891,7 @@ class PolicyRequest(object):
                 cache_update(
                         function='verify_recipient',
                         sender=self.sender,
-                        recipient=recipient,
+                        recipient=normalize_address(recipient),
                         result=(int)(True),
                         sasl_username=self.sasl_username,
                         sasl_sender=self.sasl_sender
@@ -865,15 +900,24 @@ class PolicyRequest(object):
                 log.debug(_("Could not find this user, accepting"), level=8)
                 return True
 
-        if not recipient['dn'] == False:
+        if not _recipient['dn'] == False:
             recipient_policy = self.auth.get_entry_attribute(
                     sasl_domain,
-                    recipient,
+                    _recipient['dn'],
                     'kolabAllowSMTPSender'
                 )
 
         # If no such attribute has been specified, allow
         if recipient_policy == None:
+            cache_update(
+                    function='verify_recipient',
+                    sender=self.sender,
+                    recipient=normalize_address(recipient),
+                    result=(int)(True),
+                    sasl_username=self.sasl_username,
+                    sasl_sender=self.sasl_sender
+                )
+
             recipient_verified = True
 
         # Otherwise, parse the policy obtained with the subject of the policy
@@ -889,7 +933,7 @@ class PolicyRequest(object):
             cache_update(
                     function='verify_recipient',
                     sender=self.sender,
-                    recipient=recipient,
+                    recipient=normalize_address(recipient),
                     result=(int)(recipient_verified),
                     sasl_username=self.sasl_username,
                     sasl_sender=self.sasl_sender
@@ -918,10 +962,11 @@ class PolicyRequest(object):
                 )
 
             if not records == None and len(records) == len(self.recipients):
+                log.debug("Euh, what am I doing here?")
                 for record in records:
                     recipient_found = False
                     for recipient in self.recipients:
-                        if recipient == record['recipient']:
+                        if recipient == record.recipient:
                             recipient_found = True
 
                     if not recipient_found:
@@ -1123,6 +1168,7 @@ def cache_init():
 
     Session = sessionmaker(bind=engine)
     session = Session()
+    cache_cleanup()
 
     return cache
 
@@ -1138,8 +1184,8 @@ def cache_select(
     if not cache == True:
         return None
 
-    if not recipient == '':
-        recipients.append(recipient)
+    if not recipient == '' and recipients == []:
+        recipients = [recipient]
 
     return session.query(
             PolicyResult
@@ -1278,6 +1324,11 @@ def expand_mydomains():
         Return a list of my domains.
     """
 
+    global mydomains
+
+    if not mydomains == None:
+        return mydomains
+
     auth = Auth()
     auth.connect()
 
@@ -1348,6 +1399,11 @@ def verify_domain(domain):
         Verify whether the domain is internal (mine) or external.
     """
 
+    global mydomains
+
+    if not mydomains == None:
+        return domain in mydomains
+
     auth = Auth()
     auth.connect()
 
diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py
index f07d383..ea8cea6 100644
--- a/pykolab/auth/__init__.py
+++ b/pykolab/auth/__init__.py
@@ -183,6 +183,9 @@ class Auth(pykolab.base.Base):
         if not domain == None and not self.domain == domain:
             self.connect(domain=domain)
 
+        if not self._auth or self._auth == None:
+            self.connect(domain=domain)
+
         result = self._auth.find_recipient(address)
 
         if isinstance(result, list) and len(result) == 1:




More information about the commits mailing list