summaryrefslogtreecommitdiffstats
path: root/pykolab
diff options
context:
space:
mode:
authorJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2011-04-11 11:51:07 +0100
committerJeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com>2011-04-11 11:51:07 +0100
commitde8818dd793a1b1caff98be0ff03fc3e032f9f92 (patch)
tree081332bb6c4a60446caa61b09d82c14f40b71283 /pykolab
parentb8a2d3b78886126ece422c3141d7bce92e67c65c (diff)
downloadpykolab-de8818dd793a1b1caff98be0ff03fc3e032f9f92.tar.gz
Make our auth.ldap module agnostic to python-ldap 2.4 changes
Diffstat (limited to 'pykolab')
-rw-r--r--pykolab/auth/ldap/__init__.py304
1 files changed, 234 insertions, 70 deletions
diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py
index c81f7dd..d1895d2 100644
--- a/pykolab/auth/ldap/__init__.py
+++ b/pykolab/auth/ldap/__init__.py
@@ -34,6 +34,42 @@ from pykolab.translate import _
log = pykolab.getLogger('pykolab.auth')
conf = pykolab.getConf()
+# Catch python-ldap-2.4 changes
+from distutils import version
+if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
+ LDAP_CONTROL_PAGED_RESULTS = ldap.CONTROL_PAGEDRESULTS
+else:
+ LDAP_CONTROL_PAGED_RESULTS = ldap.LDAP_CONTROL_PAGE_OID
+
+class SimplePagedResultsControl(ldap.controls.SimplePagedResultsControl):
+ """
+
+ Python LDAP 2.4 and later breaks the API. This is an abstraction class
+ so that we can handle either.
+ """
+
+ def __init__(self, page_size=0, cookie=''):
+ if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
+ ldap.controls.SimplePagedResultsControl.__init__(self, size=page_size, cookie=cookie)
+ else:
+ ldap.controls.SimplePagedResultsControl.__init__(
+ LDAP_CONTROL_PAGED_RESULTS,
+ critical,
+ (page_size, '')
+ )
+
+ def cookie(self):
+ if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
+ return self.cookie
+ else:
+ return self.controlValue[1]
+
+ def size(self):
+ if version.StrictVersion('2.4.0') <= version.StrictVersion(ldap.__version__):
+ return self.size
+ else:
+ return self.controlValue[0]
+
class LDAP(object):
"""
Abstraction layer for the LDAP authentication / authorization backend,
@@ -45,12 +81,17 @@ class LDAP(object):
self.bind = False
def _authenticate(self, login, domain):
- log.debug(_("Attempting to authenticate user %s in domain %s") %(login, domain), level=8)
+ log.debug(_("Attempting to authenticate user %s in domain %s")
+ %(login, domain), level=8)
+
self._connect()
user_dn = self._find_dn(login[0], domain)
try:
- log.debug(_("Binding with user_dn %s and password %s") %(user_dn, login[1]))
- # Needs to be synchronous or succeeds and continues setting retval to True!!
+ log.debug(_("Binding with user_dn %s and password %s")
+ %(user_dn, login[1]))
+
+ # Needs to be synchronous or succeeds and continues setting retval
+ # to True!!
self.ldap.simple_bind_s(user_dn, login[1])
retval = True
except:
@@ -69,7 +110,7 @@ class LDAP(object):
if domain == None:
section = 'ldap'
- elif not conf.has_option(domain, uri):
+ elif not conf.has_option(domain, 'uri'):
section = 'ldap'
log.debug(_("Connecting to LDAP..."), level=8)
@@ -138,9 +179,10 @@ class LDAP(object):
else:
section = 'ldap'
- user_base_dn = conf.get_raw(section, 'user_base_dn') %({'base_dn': domain_root_dn})
-
- #print "user_base_dn:", user_base_dn
+ user_base_dn = conf.get_raw(
+ section,
+ 'user_base_dn'
+ ) %({'base_dn': domain_root_dn})
if conf.has_option(domain_root_dn, 'kolab_user_filter'):
section = domain_root_dn
@@ -152,19 +194,33 @@ class LDAP(object):
if kolab_user_filter == "":
kolab_user_filter = conf.get('ldap', 'kolab_user_filter')
- search_filter = "(&(%s=%s)%s)" %(conf.get('cyrus-sasl', 'result_attribute'),login,kolab_user_filter)
-
- #print search_filter
+ search_filter = "(&(%s=%s)%s)" %(
+ conf.get(
+ 'cyrus-sasl',
+ 'result_attribute'
+ ),
+ login,
+ kolab_user_filter
+ )
- _results = self._search(user_base_dn, filterstr=search_filter, attrlist=['dn'])
+ _results = self._search(
+ user_base_dn,
+ filterstr=search_filter,
+ attrlist=[ 'dn' ]
+ )
if len(_results) == 1:
(_user_dn, _user_attrs) = _results[0]
else:
- # Retry to find the user_dn with just uid=%s against the root_dn, if the login is not fully qualified
+ # Retry to find the user_dn with just uid=%s against the root_dn,
+ # if the login is not fully qualified
if len(login.split('@')) < 2:
search_filter = "(uid=%s)" %(login)
- _results = self._search(domain_root_dn, filterstr=search_filter, attrlist=['dn'])
+ _results = self._search(
+ domain_root_dn,
+ filterstr=search_filter,
+ attrlist=[ 'dn' ]
+ )
if len(_results) == 1:
(_user_dn, _user_attrs) = _results[0]
else:
@@ -175,17 +231,21 @@ class LDAP(object):
return _user_dn
- def _search(self, base_dn, scope=ldap.SCOPE_SUBTREE, filterstr="(objectClass=*)", attrlist=None, attrsonly=0, timeout=-1):
+ def _search(self,
+ base_dn,
+ scope=ldap.SCOPE_SUBTREE,
+ filterstr="(objectClass=*)",
+ attrlist=None,
+ attrsonly=0,
+ timeout=-1
+ ):
+
_results = []
page_size = 500
critical = True
- server_page_control = ldap.controls.SimplePagedResultsControl(
- ldap.LDAP_CONTROL_PAGE_OID,
- critical,
- (page_size, '')
- )
+ server_page_control = SimplePagedResultsControl(page_size=page_size)
_search = self.ldap.search_ext(
base_dn,
@@ -199,9 +259,14 @@ class LDAP(object):
pages = 0
while True:
pages += 1
- #print "Getting page %d..." %(pages)
try:
- (_result_type, _result_data, _result_msgid, _result_controls) = self.ldap.result3(_search)
+ (
+ _result_type,
+ _result_data,
+ _result_msgid,
+ _result_controls
+ ) = self.ldap.result3(_search)
+
except ldap.NO_SUCH_OBJECT, e:
log.warning(_("Object %s searched no longer exists") %(base_dn))
break
@@ -209,10 +274,14 @@ class LDAP(object):
if (pages % 2) == 0:
log.debug(_("%d results...") %(len(_results)))
- pctrls = [c for c in _result_controls if c.controlType == ldap.LDAP_CONTROL_PAGE_OID]
+ pctrls = [
+ c for c in _result_controls
+ if c.controlType == LDAP_CONTROL_PAGED_RESULTS
+ ]
if pctrls:
- est, cookie = pctrls[0].controlValue
+ size = pctrls[0].size
+ cookie = pctrls[0].cookie
if cookie:
server_page_control.controlValue = (page_size, cookie)
_search = self.ldap.search_ext(
@@ -257,20 +326,52 @@ class LDAP(object):
def _get_user_attribute(self, user, attribute):
self._bind()
- result = self._search(user['dn'], ldap.SCOPE_BASE, '(objectclass=*)', [ attribute ])
+ result = self._search(
+ user['dn'],
+ ldap.SCOPE_BASE,
+ '(objectclass=*)',
+ [ attribute ]
+ )
if len(result) >= 1:
(user_dn, user_attrs) = result[0]
else:
- log.warning(_("Could not get user attribute %s for %s") %(attribute,user['dn']))
+ log.warning(_("Could not get user attribute %s for %s")
+ %(attribute,user['dn']))
+
return None
user_attrs = utils.normalize(user_attrs)
+
if not user_attrs.has_key(attribute):
user_attrs[attribute] = None
return user_attrs[attribute]
+ def _get_user_attributes(self, user, attributes):
+ self._bind()
+
+ result = self._search(
+ user['dn'],
+ ldap.SCOPE_BASE,
+ '(objectclass=*)',
+ attributes
+ )
+
+ if len(result) >= 1:
+ (user_dn, user_attrs) = result[0]
+ else:
+ log.warning(_("Could not get user attributes for %s") %(user['dn']))
+ return None
+
+ user_attrs = utils.normalize(user_attrs)
+
+ # Only return the attributes requested in the function call.
+ _user_attrs = []
+ for _attr in attributes:
+ _user_attrs[_attr] = user_attrs[_attr]
+
+ return _user_attrs
def _set_user_attribute(self, user, attribute, value):
self._bind()
@@ -282,12 +383,20 @@ class LDAP(object):
# TODO: This should be a schema check!
if attribute in [ 'mailquota', 'mailalternateaddress' ]:
if not user.has_key('objectclass'):
- user['objectclass'] = self._get_user_attribute(user,'objectclass')
+ user['objectclass'] = self._get_user_attribute(
+ user,
+ 'objectclass'
+ )
+
if user['objectclass'] == None:
return
if not 'mailrecipient' in user['objectclass']:
user['objectclass'].append('mailrecipient')
- self._set_user_attribute(user, 'objectclass', user['objectclass'])
+ self._set_user_attribute(
+ user,
+ 'objectclass',
+ user['objectclass']
+ )
try:
self.ldap.modify(user['dn'], [(ldap.MOD_REPLACE, attribute, value)])
@@ -378,7 +487,10 @@ class LDAP(object):
domains = []
for _domain in _results:
(domain_dn, _domain_attrs) = _domain
- domain_rootdn_attribute = conf.get('ldap', 'domain_rootdn_attribute')
+ domain_rootdn_attribute = conf.get(
+ 'ldap',
+ 'domain_rootdn_attribute'
+ )
_domain_attrs = utils.normalize(_domain_attrs)
if _domain_attrs.has_key(domain_rootdn_attribute):
return _domain_attrs[domain_rootdn_attribute]
@@ -387,11 +499,12 @@ class LDAP(object):
def _list_users(self, primary_domain, secondary_domains=[]):
- log.info(_("Listing users for domain %s (and %s)") %(primary_domain, ' '.join(secondary_domains)))
+ log.info(_("Listing users for domain %s (and %s)")
+ %(primary_domain, ' '.join(secondary_domains)))
self._bind()
- # TODO: Bind with read-only credentials, perhaps even domain-specific, please
+ # TODO: Bind with read-only credentials, perhaps even domain-specific
bind_dn = conf.get('ldap', 'bind_dn')
#bind_dn = conf.get('ldap', 'ro_bind_dn')
bind_pw = conf.get('ldap', 'bind_pw')
@@ -404,7 +517,10 @@ class LDAP(object):
else:
section = 'ldap'
- user_base_dn = conf.get_raw(section, 'user_base_dn') %({'base_dn': domain_root_dn})
+ user_base_dn = conf.get_raw(
+ section,
+ 'user_base_dn'
+ ) %({'base_dn': domain_root_dn})
if conf.has_option(domain_root_dn, 'kolab_user_filter'):
section = domain_root_dn
@@ -413,8 +529,12 @@ class LDAP(object):
kolab_user_filter = conf.get(section, 'kolab_user_filter', quiet=True)
- if kolab_user_filter == "":
- kolab_user_filter = conf.get('ldap', 'kolab_user_filter')
+ # TODO: Is, perhaps, a domain specific setting
+ result_attribute = conf.get(
+ 'cyrus-sasl',
+ 'result_attribute',
+ quiet=True
+ )
self.ldap.simple_bind_s(bind_dn, bind_pw)
@@ -424,13 +544,21 @@ class LDAP(object):
user_base_dn,
ldap.SCOPE_SUBTREE,
kolab_user_filter,
- attrlist=[ 'dn', 'mail', 'mailquota', 'mailalternateaddress', 'sn', 'givenname', 'cn', 'uid' ],
+ attrlist=[
+ 'dn',
+ result_attribute,
+ 'sn',
+ 'givenname',
+ 'cn',
+ 'uid'
+ ],
attrsonly=0
)
- log.debug(_("Iterating over %d users, making sure we have the necessary attributes...") %(len(_search)))
+ log.info(_("Found %d users") %(len(_search)))
- #print "SEARCH RESULTS:", _search
+ log.debug(_("Iterating over %d users, making sure we have the " + \
+ "necessary attributes...") %(len(_search)), level=6)
users = []
@@ -439,6 +567,8 @@ class LDAP(object):
for user_dn, user_attrs in _search:
num_user += 1
+
+ # Placeholder for the user attributes
user = {}
user['dn'] = user_dn
if not user.has_key('standard_domain'):
@@ -446,22 +576,36 @@ class LDAP(object):
user_attrs = utils.normalize(user_attrs)
- #print "USER_ATTRS:", user_attrs
+ _get_attrs = []
- for attribute in [ 'mail', 'mailalternateaddress', 'sn', 'givenname', 'cn', 'uid' ]:
+ for attribute in [
+ result_attribute,
+ 'sn',
+ 'givenname',
+ 'cn',
+ 'uid'
+ ]:
if not user_attrs.has_key(attribute):
- #print "doesn't have attribute"
+ _get_attrs.append(attribute)
user[attribute] = self._get_user_attribute(user, attribute)
else:
- #print "has attribute"
user[attribute] = user_attrs[attribute]
- #print "============== EXECING PLUGINS ================="
+ if len(_get_attrs) > 0:
+ _user_attrs = self._get_user_attributes(user, _get_attrs)
+ for key in _user_attrs.keys():
+ user[key] = _user_attrs[key]
+ # Check to see if we want to apply a primary mail recipient policy
if conf.has_option(domain_root_dn, 'primary_mail'):
- primary_mail = conf.plugins.exec_hook("set_primary_mail",
+ primary_mail = conf.plugins.exec_hook(
+ "set_primary_mail",
kw={
- 'primary_mail': conf.get_raw(domain_root_dn, 'primary_mail'),
+ 'primary_mail':
+ conf.get_raw(
+ domain_root_dn,
+ 'primary_mail'
+ ),
'user_attrs': user_attrs,
'primary_domain': primary_domain,
'secondary_domains': secondary_domains
@@ -474,9 +618,12 @@ class LDAP(object):
user['mail'] = primary_mail
else:
if not primary_mail == user['mail']:
+ self._set_user_attribute(user, 'mail', primary_mail)
user['old_mail'] = user['mail']
user['mail'] = primary_mail
+ # Check to see if we want to apply a secondary mail recipient
+ # policy.
section = None
if conf.has_option(domain_root_dn, 'secondary_mail'):
@@ -485,36 +632,53 @@ class LDAP(object):
section = 'kolab'
if not section == None:
- secondary_mail = conf.plugins.exec_hook("set_secondary_mail", kw={
- 'secondary_mail': conf.get_raw(domain_root_dn, 'secondary_mail'),
- 'user_attrs': user_attrs,
- 'primary_domain': primary_domain,
- 'secondary_domains': secondary_domains
- }
- )
-
- if not secondary_mail == None:
- secondary_mail = list(set(secondary_mail))
- if not user.has_key('mailalternateaddress'):
- self._set_user_attribute(user, 'mailalternateaddress', secondary_mail)
- user['mailalternateaddress'] = secondary_mail
- else:
- if not secondary_mail == user['mailalternateaddress']:
- user['mailalternateaddress'] = secondary_mail
+ # Execute the plugin hook
+ secondary_mail = conf.plugins.exec_hook(
+ "set_secondary_mail",
+ kw={
+ 'secondary_mail':
+ conf.get_raw(
+ domain_root_dn,
+ 'secondary_mail'
+ ),
+ 'user_attrs': user_attrs,
+ 'primary_domain': primary_domain,
+ 'secondary_domains': secondary_domains
+ }
+ ) # end of conf.plugins.exec_hook() call
+
+ if not secondary_mail == None:
+ secondary_mail = list(set(secondary_mail))
+ # Avoid duplicates
+ while primary_mail in secondary_mail:
+ secondary_mail.pop(secondary_mail.index(primary_mail))
+
+ if not user.has_key('mailalternateaddress'):
+ if not len(secondary_mail) == 0:
+ self._set_user_attribute(
+ user,
+ 'mailalternateaddress',
+ secondary_mail
+ )
+
+ user['mailalternateaddress'] = secondary_mail
+ else:
+ if not secondary_mail == user['mailalternateaddress']:
+ self._set_user_attribute(
+ user,
+ 'mailalternateaddress',
+ secondary_mail
+ )
+
+ user['mailalternateaddress'] = secondary_mail
users.append(user)
if (num_user % 1000) == 0:
- log.info(_("Done iterating over user %d of %d") %(num_user,num_users))
-
- #print "USERS:", users
+ log.debug(
+ _("Done iterating over user %d of %d")
+ %(num_user,num_users),
+ level=3
+ )
return users
-
-#class SyncControl(ldap.controls.LDAPControl):
- #controlType = '(1.3.6.1.4.1.4203.1.9.1.1'
- #def __init__(self, controlType, criticality, controlValue=None, encodedControlValue=None):
- #LDAPControl.__init__(self, controlType, criticality, controlValue, encodedControlValue)
-
- #def encodeControlValue(self, value):
-