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