summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2013-10-04 14:58:26 +0200
committerJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2013-10-04 14:58:26 +0200
commite4dd9536cb391c04698184e196700133f5b98d3a (patch)
treef6b34a88bd699a62003b2c75f0bc63600b15d3b3
parentcd2b45bf4ef9a7b08fc48b2bd82fac0bc7907836 (diff)
parentb5d02f2d5938e6b9793519989d221ed1a0f8512b (diff)
downloadpykolab-e4dd9536cb391c04698184e196700133f5b98d3a.tar.gz
Merge branch 'master' of ssh://git.kolabsys.com/git/pykolab
Conflicts: configure.ac
-rwxr-xr-xbin/kolab_smtp_access_policy.py132
-rw-r--r--configure.ac4
-rw-r--r--kolabd/__init__.py2
-rw-r--r--pykolab/auth/__init__.py49
-rw-r--r--pykolab/auth/ldap/__init__.py54
-rw-r--r--pykolab/auth/ldap/auth_cache.py13
-rw-r--r--pykolab/base.py3
-rw-r--r--pykolab/imap/__init__.py2
-rw-r--r--pykolab/setup/setup_ldap.py2
9 files changed, 176 insertions, 85 deletions
diff --git a/bin/kolab_smtp_access_policy.py b/bin/kolab_smtp_access_policy.py
index c13bfaf..9db2c2a 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,16 @@ log = pykolab.getLogger('pykolab.smtp_access_policy')
conf = pykolab.getConf()
+auth = None
+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 +770,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 +778,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)
@@ -782,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 not self.auth.domains == None and 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") % (
@@ -819,9 +836,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 +857,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 +892,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 +901,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 +934,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 +963,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:
@@ -980,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)
@@ -1123,6 +1170,7 @@ def cache_init():
Session = sessionmaker(bind=engine)
session = Session()
+ cache_cleanup()
return cache
@@ -1138,8 +1186,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
@@ -1175,10 +1223,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(
@@ -1226,9 +1272,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
@@ -1278,19 +1323,16 @@ def expand_mydomains():
Return a list of my domains.
"""
- auth = Auth()
- auth.connect()
+ global auth,mydomains
- mydomains = []
+ if not mydomains == None:
+ return mydomains
- _mydomains = auth.list_domains()
+ auth.connect()
- for primary, secondaries in _mydomains:
- mydomains.append(primary)
- for secondary in secondaries:
- mydomains.append(secondary)
+ mydomains = auth.list_domains()
- return mydomains
+ return mydomains.keys()
def normalize_address(email_address):
"""
@@ -1348,20 +1390,20 @@ def verify_domain(domain):
Verify whether the domain is internal (mine) or external.
"""
- auth = Auth()
+ global auth,mydomains
+
+ if not mydomains == None:
+ return domain in mydomains.keys()
+
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
@@ -1397,6 +1439,8 @@ if __name__ == "__main__":
conf.finalize_conf()
+ auth = Auth()
+
cache = cache_init()
policy_requests = {}
diff --git a/configure.ac b/configure.ac
index 84d7820..130b9d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
-AC_INIT([pykolab], 0.6.5)
-AC_SUBST([RELEASE], 2)
+AC_INIT([pykolab], 0.6.7)
+AC_SUBST([RELEASE], 1)
AC_CONFIG_SRCDIR(pykolab/constants.py.in)
diff --git a/kolabd/__init__.py b/kolabd/__init__.py
index 5624280..ce552d8 100644
--- a/kolabd/__init__.py
+++ b/kolabd/__init__.py
@@ -232,7 +232,7 @@ class KolabDaemon(object):
# domains now is a list of tuples, we want the primary_domains
primary_domains = []
- for primary_domain, secondary_domains in domains:
+ for primary_domain in list(set(domains.values())):
primary_domains.append(primary_domain)
# Now we can check if any changes happened.
diff --git a/pykolab/auth/__init__.py b/pykolab/auth/__init__.py
index fd02083..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),
@@ -180,7 +180,10 @@ class Auth(pykolab.base.Base):
"""
Find one or more entries corresponding to the recipient address.
"""
- if not domain == None:
+ 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)
@@ -204,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 (...).
@@ -222,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)
diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index 4f9ad64..06fb00d 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -169,7 +169,11 @@ class LDAP(pykolab.base.Base):
else:
base_dn = config_base_dn
- auth_cache.set_entry(self.domain, base_dn)
+ try:
+ auth_cache.set_entry(self.domain, base_dn)
+ except Exception, errmsg:
+ log.error(_("Authentication cache failed: %r") % (errmsg))
+ pass
user_filter = self.config_get_raw('user_filter') % ({'base_dn':base_dn})
@@ -216,7 +220,11 @@ class LDAP(pykolab.base.Base):
# to True!!
self.ldap.simple_bind_s(entry_dn, login[1])
retval = True
- auth_cache.set_entry(_filter, entry_dn)
+ try:
+ auth_cache.set_entry(_filter, entry_dn)
+ except Exception, errmsg:
+ log.error(_("Authentication cache failed: %r") % (errmsg))
+ pass
except:
try:
log.debug(
@@ -393,7 +401,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 = "(|"
@@ -523,7 +536,10 @@ class LDAP(pykolab.base.Base):
"""
List alias domain name spaces for the current domain name space.
"""
- return [s for s, p in self.secondary_domains.iteritems() if p == self.domain]
+ if not self.domains == None:
+ return [s for s in self.domains.keys() if not s in self.domains.values()]
+ else:
+ return []
def recipient_policy(self, entry):
"""
@@ -1284,14 +1300,14 @@ class LDAP(pykolab.base.Base):
self.imap.connect(domain=self.domain)
- if not self.imap.user_mailbox_exists(entry[result_attribute]):
+ if not self.imap.user_mailbox_exists(entry[result_attribute].lower()):
folder = self.imap.user_mailbox_create(
entry[result_attribute],
entry[mailserver_attribute]
)
else:
- folder = "user%s%s" % (self.imap.separator,entry[result_attribute])
+ folder = "user%s%s" % (self.imap.separator,entry[result_attribute].lower())
server = self.imap.user_mailbox_server(folder)
@@ -1933,6 +1949,12 @@ class LDAP(pykolab.base.Base):
return _user_dn
def _kolab_domain_root_dn(self, domain):
+ if not hasattr(self, 'domain_rootdns'):
+ self.domain_rootdns = {}
+
+ if self.domain_rootdns.has_key(domain):
+ return self.domain_rootdns[domain]
+
self._bind()
log.debug(_("Finding domain root dn for domain %s") % (domain), level=8)
@@ -1963,16 +1985,19 @@ class LDAP(pykolab.base.Base):
)
_domain_attrs = utils.normalize(_domain_attrs)
if _domain_attrs.has_key(domain_rootdn_attribute):
+ self.domain_rootdns[domain] = _domain_attrs[domain_rootdn_attribute]
return _domain_attrs[domain_rootdn_attribute]
else:
if isinstance(_domain_attrs[domain_name_attribute], list):
domain = _domain_attrs[domain_name_attribute][0]
-
+ else:
+ domain = _domain_attrs[domain_name_attribute]
else:
if conf.has_option('ldap', 'base_dn'):
return conf.get('ldap', 'base_dn')
- return utils.standard_root_dn(domain)
+ self.domain_rootdns[domain] = utils.standard_root_dn(domain)
+ return self.domain_rootdns[domain]
def _kolab_filter(self):
"""
@@ -1991,7 +2016,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.
@@ -2019,6 +2044,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 []
@@ -2493,6 +2521,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(
diff --git a/pykolab/auth/ldap/auth_cache.py b/pykolab/auth/ldap/auth_cache.py
index 12f362c..ba14262 100644
--- a/pykolab/auth/ldap/auth_cache.py
+++ b/pykolab/auth/ldap/auth_cache.py
@@ -71,7 +71,7 @@ entry_table = Table(
'entries', metadata,
Column('id', Integer, primary_key=True),
Column('domain', String(128), index=True, nullable=True),
- Column('key', String(128), index=True, nullable=False),
+ Column('key', String(512), index=True, nullable=False),
Column('value', String(128), nullable=False),
Column('last_change', DateTime, nullable=False, default=datetime.datetime.now())
)
@@ -114,9 +114,8 @@ def set_entry(key, value):
db.commit()
-#def purge_entries():
- #db = init_db()
- #db.query(Entry).filter(Entry.last_change <= datetime.datetime.now()).delete()
+def purge_entries(db):
+ db.query(Entry).filter(Entry.last_change <= (datetime.datetime.now() - datetime.timedelta(1))).delete()
def init_db():
"""
@@ -127,12 +126,16 @@ def init_db():
if not db == None:
return db
- db_uri = 'sqlite:///%s/auth_cache.db' % (KOLAB_LIB_PATH)
+ db_uri = conf.get('ldap', 'auth_cache_uri')
+ if db_uri == None:
+ db_uri = 'sqlite:///%s/auth_cache.db' % (KOLAB_LIB_PATH)
+
echo = conf.debuglevel > 8
engine = create_engine(db_uri, echo=echo)
metadata.create_all(engine)
Session = sessionmaker(bind=engine)
db = Session()
+ purge_entries(db)
return db
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:
diff --git a/pykolab/imap/__init__.py b/pykolab/imap/__init__.py
index b203db6..52dfdc1 100644
--- a/pykolab/imap/__init__.py
+++ b/pykolab/imap/__init__.py
@@ -638,7 +638,7 @@ class IMAP(object):
log.warning(_("Moving INBOX folder %s won't succeed as target folder %s already exists") % (old_name,new_name))
def user_mailbox_server(self, mailbox):
- return self.imap.find_mailfolder_server(mailbox)
+ return self.imap.find_mailfolder_server(mailbox.lower())
def has_folder(self, folder):
"""
diff --git a/pykolab/setup/setup_ldap.py b/pykolab/setup/setup_ldap.py
index f6576f1..b0f40c0 100644
--- a/pykolab/setup/setup_ldap.py
+++ b/pykolab/setup/setup_ldap.py
@@ -365,7 +365,7 @@ ServerAdminPwd = %(admin_pass)s
fp.close()
fp = open('/var/log/kolab/setup.out.log', 'w')
- fp.write(stderrdata)
+ fp.write(stdoutdata)
fp.close()
log.debug(_("Setup DS stdout:"), level=8)