Changeset View
Changeset View
Standalone View
Standalone View
pykolab/auth/ldap/__init__.py
Show First 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | class LDAP(pykolab.base.Base): | ||||
def __init__(self, domain=None): | def __init__(self, domain=None): | ||||
""" | """ | ||||
Initialize the LDAP object for domain. If no domain is specified, | Initialize the LDAP object for domain. If no domain is specified, | ||||
domain name space configured as 'kolab'.'primary_domain' is used. | domain name space configured as 'kolab'.'primary_domain' is used. | ||||
""" | """ | ||||
pykolab.base.Base.__init__(self, domain=domain) | pykolab.base.Base.__init__(self, domain=domain) | ||||
self.ldap = None | self.ldap = None | ||||
self.bind = False | self.bind = None | ||||
if domain == None: | if domain == None: | ||||
self.domain = conf.get('kolab', 'primary_domain') | self.domain = conf.get('kolab', 'primary_domain') | ||||
else: | else: | ||||
self.domain = domain | self.domain = domain | ||||
def authenticate(self, login, realm): | def authenticate(self, login, realm): | ||||
""" | """ | ||||
Find the entry corresponding to login, and attempt a bind. | Find the entry corresponding to login, and attempt a bind. | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | def authenticate(self, login, realm): | ||||
log.error(_("%s") % (traceback.format_exc())) | log.error(_("%s") % (traceback.format_exc())) | ||||
self._disconnect() | self._disconnect() | ||||
return False | return False | ||||
if len(_result_data) >= 1: | if len(_result_data) >= 1: | ||||
(entry_dn, entry_attrs) = _result_data[0] | (entry_dn, entry_attrs) = _result_data[0] | ||||
try: | try: | ||||
log.debug(_("Binding with user_dn %s and password %s") | |||||
% (entry_dn, '*' * len(login[1]))) | |||||
# Needs to be synchronous or succeeds and continues setting retval | # Needs to be synchronous or succeeds and continues setting retval | ||||
# to True!! | # to True!! | ||||
self.ldap.simple_bind_s(entry_dn, login[1]) | retval = self._bind(entry_dn, login[1]) | ||||
retval = True | |||||
try: | try: | ||||
auth_cache.set_entry(_filter, entry_dn) | auth_cache.set_entry(_filter, entry_dn) | ||||
except Exception, errmsg: | except Exception, errmsg: | ||||
log.error(_("Authentication cache failed: %r") % (errmsg)) | log.error(_("Authentication cache failed: %r") % (errmsg)) | ||||
pass | pass | ||||
except ldap.SERVER_DOWN, errmsg: | except ldap.SERVER_DOWN, errmsg: | ||||
log.error(_("Authentication failed, LDAP server unavailable")) | log.error(_("Authentication failed, LDAP server unavailable")) | ||||
self._disconnect() | self._disconnect() | ||||
pass | pass | ||||
except: | except: | ||||
try: | try: | ||||
log.debug( | log.debug( | ||||
_("Failed to authenticate as user %s") % (login[0]), | _("Failed to authenticate as user %s") % (login[0]), | ||||
level=8 | level=8 | ||||
) | ) | ||||
except: | except: | ||||
pass | pass | ||||
retval = False | retval = False | ||||
else: | else: | ||||
try: | try: | ||||
log.debug(_("Binding with user_dn %s and password %s") | |||||
% (entry_dn, '*' * len(login[1]))) | |||||
# Needs to be synchronous or succeeds and continues setting retval | # Needs to be synchronous or succeeds and continues setting retval | ||||
# to True!! | # to True!! | ||||
self.ldap.simple_bind_s(entry_dn, login[1]) | retval = self._bind(entry_dn, login[1]) | ||||
auth_cache.set_entry(_filter, entry_dn) | auth_cache.set_entry(_filter, entry_dn) | ||||
retval = True | |||||
except ldap.NO_SUCH_OBJECT, errmsg: | except ldap.NO_SUCH_OBJECT, errmsg: | ||||
log.debug(_("Error occured, there is no such object: %r") % (errmsg), level=8) | log.debug(_("Error occured, there is no such object: %r") % (errmsg), level=8) | ||||
self.bind = False | self.bind = None | ||||
try: | try: | ||||
auth_cache.del_entry(_filter) | auth_cache.del_entry(_filter) | ||||
except: | except: | ||||
log.error(_("Authentication cache failed to clear entry")) | log.error(_("Authentication cache failed to clear entry")) | ||||
pass | pass | ||||
return self.authenticate(login, realm) | return self.authenticate(login, realm) | ||||
▲ Show 20 Lines • Show All 698 Lines • ▼ Show 20 Lines | def recipient_policy(self, entry): | ||||
entry_modifications[secondary_mail_attribute] = list(set(secondary_mail_addresses)) | entry_modifications[secondary_mail_attribute] = list(set(secondary_mail_addresses)) | ||||
log.debug(_("Entry modifications list: %r") % (entry_modifications), level=8) | log.debug(_("Entry modifications list: %r") % (entry_modifications), level=8) | ||||
return entry_modifications | return entry_modifications | ||||
def reconnect(self): | def reconnect(self): | ||||
bind = self.bind | |||||
self._disconnect() | self._disconnect() | ||||
self.connect() | self.connect() | ||||
if bind is not None: | |||||
self._bind(bind['dn'], bind['pw']) | |||||
def search_entry_by_attribute(self, attr, value, **kw): | def search_entry_by_attribute(self, attr, value, **kw): | ||||
self._bind() | self._bind() | ||||
_filter = "(%s=%s)" % (attr, ldap.filter.escape_filter_chars(value)) | _filter = "(%s=%s)" % (attr, ldap.filter.escape_filter_chars(value)) | ||||
config_base_dn = self.config_get('base_dn') | config_base_dn = self.config_get('base_dn') | ||||
ldap_base_dn = self._kolab_domain_root_dn(self.domain) | ldap_base_dn = self._kolab_domain_root_dn(self.domain) | ||||
if not ldap_base_dn == None and not ldap_base_dn == config_base_dn: | if not ldap_base_dn == None and not ldap_base_dn == config_base_dn: | ||||
base_dn = ldap_base_dn | base_dn = ldap_base_dn | ||||
else: | else: | ||||
base_dn = config_base_dn | base_dn = config_base_dn | ||||
return self._search( | return self._search( | ||||
base_dn, | base_dn, | ||||
filterstr=_filter, | filterstr=_filter, | ||||
attrlist=[ | attrlist=[ | ||||
'*', | '*', | ||||
], | ], | ||||
override_search='_regular_search' | override_search='_regular_search' | ||||
) | ) | ||||
def set_entry_attribute(self, entry_id, attribute, value): | def set_entry_attribute(self, entry_id, attribute, value): | ||||
log.debug(_("Setting entry attribute %r to %r for %r") % (attribute, value, entry_id), level=9) | log.debug(_("Setting entry attribute %r to %r for %r") % (attribute, value, entry_id), level=9) | ||||
self.set_entry_attributes(entry_id, { attribute: value }) | self.set_entry_attributes(entry_id, { attribute: value }) | ||||
def set_entry_attributes(self, entry_id, attributes): | def set_entry_attributes(self, entry_id, attributes): | ||||
self._bind() | bind_dn = self.config_get('bind_dn') | ||||
bind_pw = self.config_get('bind_pw') | |||||
self._bind(bind_dn, bind_pw) | |||||
entry_dn = self.entry_dn(entry_id) | entry_dn = self.entry_dn(entry_id) | ||||
entry = self.get_entry_attributes(entry_dn, ['*']) | entry = self.get_entry_attributes(entry_dn, ['*']) | ||||
attrs = {} | attrs = {} | ||||
for attribute in attributes.keys(): | for attribute in attributes.keys(): | ||||
Show All 17 Lines | def set_entry_attributes(self, entry_id, attributes): | ||||
dn = entry_dn | dn = entry_dn | ||||
if len(modlist) > 0: | if len(modlist) > 0: | ||||
try: | try: | ||||
self.ldap.modify_s(dn, modlist) | self.ldap.modify_s(dn, modlist) | ||||
except: | except: | ||||
log.error(_("Could not update dn %r:\n%r") % (dn, modlist)) | log.error(_("Could not update dn %r:\n%r") % (dn, modlist)) | ||||
# Drop the privileges | |||||
self._unbind() | |||||
def synchronize(self, mode=0, callback=None): | def synchronize(self, mode=0, callback=None): | ||||
""" | """ | ||||
Synchronize with LDAP | Synchronize with LDAP | ||||
""" | """ | ||||
self._bind() | self._bind() | ||||
_filter = self._kolab_filter() | _filter = self._kolab_filter() | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | def user_quota(self, entry_id, folder): | ||||
else: | else: | ||||
if not new_quota == None: | if not new_quota == None: | ||||
self.imap.set_quota(folder, new_quota) | self.imap.set_quota(folder, new_quota) | ||||
### | ### | ||||
### API depth level increasing! | ### API depth level increasing! | ||||
### | ### | ||||
def _bind(self): | def _bind(self, bind_dn=None, bind_pw=None): | ||||
if self.ldap == None: | # If we have no LDAP, we have no previous state. | ||||
if self.ldap is None: | |||||
self.bind = None | |||||
self.connect() | self.connect() | ||||
if not self.bind: | # If we are to bind as foo, we have no state. | ||||
bind_dn = self.config_get('bind_dn') | if bind_dn is not None: | ||||
bind_pw = self.config_get('bind_pw') | self.bind = None | ||||
# Only if we have no state and no bind credentials specified in the | |||||
# function call. | |||||
if self.bind is None: | |||||
if bind_dn is None: | |||||
bind_dn = self.config_get('service_bind_dn') | |||||
if bind_pw is None: | |||||
bind_pw = self.config_get('service_bind_pw') | |||||
if bind_dn is not None: | |||||
log.debug(_("Binding with bind_dn: %s and password: %s") | |||||
% (bind_dn, '*' * len(bind_pw))) | |||||
# TODO: Binding errors control | # TODO: Binding errors control | ||||
try: | try: | ||||
self.ldap.simple_bind_s(bind_dn, bind_pw) | self.ldap.simple_bind_s(bind_dn, bind_pw) | ||||
self.bind = True | self.bind = {'dn': bind_dn, 'pw': bind_pw} | ||||
return True | |||||
except ldap.SERVER_DOWN, errmsg: | except ldap.SERVER_DOWN, errmsg: | ||||
log.error(_("LDAP server unavailable: %r") % (errmsg)) | log.error(_("LDAP server unavailable: %r") % (errmsg)) | ||||
log.error(_("%s") % (traceback.format_exc())) | log.error(_("%s") % (traceback.format_exc())) | ||||
return False | |||||
except ldap.INVALID_CREDENTIALS: | except ldap.INVALID_CREDENTIALS: | ||||
log.error(_("Invalid DN, username and/or password.")) | log.error(_("Invalid DN, username and/or password.")) | ||||
machniak: The whole 'else' block here is useless. I propose to remove it and change line 1220 to be: if… | |||||
Not Done Inline ActionsI'll take it for cases where it is not reconnect() calling _bind() vanmeeuwen: I'll take it for cases where it is not reconnect() calling _bind() | |||||
Not Done Inline ActionsBut it does not make sense because line 1220 is: if self.bind is None: which iimo is correct, because there's no need to bind a user who is already bound. machniak: But it does not make sense because line 1220 is: if self.bind is None: which iimo is correct… | |||||
return False | |||||
else: | else: | ||||
log.debug(_("_bind called, but already bound"), level=9) | log.debug(_("bind() called but already bound"), level=8) | ||||
return True | |||||
def _change_add_group(self, entry, change): | def _change_add_group(self, entry, change): | ||||
""" | """ | ||||
An entry of type group was added. | An entry of type group was added. | ||||
The Kolab daemon has little to do for this type of action on this | The Kolab daemon has little to do for this type of action on this | ||||
type of entry. | type of entry. | ||||
""" | """ | ||||
▲ Show 20 Lines • Show All 760 Lines • ▼ Show 20 Lines | def _change_none_user(self, entry, change): | ||||
result_attribute | result_attribute | ||||
) | ) | ||||
) | ) | ||||
def _disconnect(self): | def _disconnect(self): | ||||
self._unbind() | self._unbind() | ||||
del self.ldap | del self.ldap | ||||
self.ldap = None | self.ldap = None | ||||
self.bind = False | self.bind = None | ||||
def _domain_naming_context(self, domain): | def _domain_naming_context(self, domain): | ||||
self._bind() | self._bind() | ||||
# The list of naming contexts in the LDAP server | # The list of naming contexts in the LDAP server | ||||
attrs = self.get_entry_attributes("", ['namingContexts']) | attrs = self.get_entry_attributes("", ['namingContexts']) | ||||
naming_contexts = attrs['namingcontexts'] | naming_contexts = attrs['namingcontexts'] | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | def _kolab_domain_root_dn(self, domain): | ||||
if self.domain_rootdns.has_key(domain): | if self.domain_rootdns.has_key(domain): | ||||
log.debug(_("Returning from cache: %r") % (self.domain_rootdns[domain]), level=8) | log.debug(_("Returning from cache: %r") % (self.domain_rootdns[domain]), level=8) | ||||
return self.domain_rootdns[domain] | return self.domain_rootdns[domain] | ||||
self._bind() | self._bind() | ||||
log.debug(_("Finding domain root dn for domain %s") % (domain), level=8) | log.debug(_("Finding domain root dn for domain %s") % (domain), level=8) | ||||
bind_dn = conf.get('ldap', 'bind_dn') | |||||
bind_pw = conf.get('ldap', 'bind_pw') | |||||
domain_base_dn = conf.get('ldap', 'domain_base_dn', quiet=True) | domain_base_dn = conf.get('ldap', 'domain_base_dn', quiet=True) | ||||
domain_filter = conf.get('ldap', 'domain_filter') | domain_filter = conf.get('ldap', 'domain_filter') | ||||
if not domain_filter == None: | if not domain_filter == None: | ||||
if not domain == None: | if not domain == None: | ||||
domain_filter = domain_filter.replace('*', domain) | domain_filter = domain_filter.replace('*', domain) | ||||
if not domain_base_dn == "": | if not domain_base_dn == "": | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | def _list_domains(self, domain=None): | ||||
name and a list of secondary domain names. | name and a list of secondary domain names. | ||||
This function should only be called by the primary instance of Auth. | This function should only be called by the primary instance of Auth. | ||||
""" | """ | ||||
log.debug(_("Listing domains..."), level=8) | log.debug(_("Listing domains..."), level=8) | ||||
self.connect() | self.connect() | ||||
self._bind() | |||||
bind_dn = conf.get('ldap', 'bind_dn') | |||||
bind_pw = conf.get('ldap', 'bind_pw') | |||||
domain_base_dn = conf.get('ldap', 'domain_base_dn', quiet=True) | domain_base_dn = conf.get('ldap', 'domain_base_dn', quiet=True) | ||||
if domain_base_dn == "": | if domain_base_dn == "": | ||||
# No domains are to be found in LDAP, return an empty list. | # No domains are to be found in LDAP, return an empty list. | ||||
# Note that the Auth() base itself handles this case. | # Note that the Auth() base itself handles this case. | ||||
return [] | return [] | ||||
# If we haven't returned already, let's continue searching | # If we haven't returned already, let's continue searching | ||||
domain_filter = conf.get('ldap', 'domain_filter') | domain_filter = conf.get('ldap', 'domain_filter') | ||||
if not domain == None: | if not domain == None: | ||||
domain_filter = domain_filter.replace('*', domain) | domain_filter = domain_filter.replace('*', domain) | ||||
if domain_base_dn == None or domain_filter == None: | if domain_base_dn == None or domain_filter == None: | ||||
return [] | return [] | ||||
# TODO: this function should be wrapped for error handling | |||||
try: | |||||
self.ldap.simple_bind_s(bind_dn, bind_pw) | |||||
except ldap.SERVER_DOWN, e: | |||||
raise AuthBackendError, _("Authentication database DOWN") | |||||
dna = self.config_get('domain_name_attribute') | dna = self.config_get('domain_name_attribute') | ||||
if dna == None: | if dna == None: | ||||
dna = 'associateddomain' | dna = 'associateddomain' | ||||
try: | try: | ||||
_search = self._search( | _search = self._search( | ||||
domain_base_dn, | domain_base_dn, | ||||
ldap.SCOPE_SUBTREE, | ldap.SCOPE_SUBTREE, | ||||
Show All 20 Lines | def _list_domains(self, domain=None): | ||||
secondary_domains = [x.lower() for x in domain_attrs[dna]] | secondary_domains = [x.lower() for x in domain_attrs[dna]] | ||||
else: | else: | ||||
primary_domain = domain_attrs[dna].lower() | primary_domain = domain_attrs[dna].lower() | ||||
domains.append((primary_domain,secondary_domains)) | domains.append((primary_domain,secondary_domains)) | ||||
return domains | return domains | ||||
def _reconnect(self): | |||||
""" | |||||
Reconnect to LDAP | |||||
""" | |||||
self._disconnect() | |||||
self.connect() | |||||
def _synchronize_callback(self, *args, **kw): | def _synchronize_callback(self, *args, **kw): | ||||
""" | """ | ||||
Determine the characteristics of the callback being placed, and | Determine the characteristics of the callback being placed, and | ||||
what data is contained within *args and **kw exactly. | what data is contained within *args and **kw exactly. | ||||
The exact form and shape of the feedback very much depends on the | The exact form and shape of the feedback very much depends on the | ||||
supportedControl used to even get the data. | supportedControl used to even get the data. | ||||
""" | """ | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | def _unbind(self): | ||||
""" | """ | ||||
Discard the current set of bind credentials. | Discard the current set of bind credentials. | ||||
Virtually disconnects the LDAP connection, and should be followed by | Virtually disconnects the LDAP connection, and should be followed by | ||||
a call to _bind() afterwards. | a call to _bind() afterwards. | ||||
""" | """ | ||||
self.ldap.unbind() | self.ldap.unbind() | ||||
self.bind = False | self.bind = None | ||||
### | ### | ||||
### Backend search functions | ### Backend search functions | ||||
### | ### | ||||
def _persistent_search(self, | def _persistent_search(self, | ||||
base_dn, | base_dn, | ||||
scope=ldap.SCOPE_SUBTREE, | scope=ldap.SCOPE_SUBTREE, | ||||
▲ Show 20 Lines • Show All 412 Lines • Show Last 20 Lines |
The whole 'else' block here is useless. I propose to remove it and change line 1220 to be: if bind_dn is not None: