diff --git a/authserv/ldap_model.py b/authserv/ldap_model.py index 7251641cd6f39fcb6e3819971d6c08ddcee7ae58..8a903e2b6b763b1768774451316a8122ebb2c765 100644 --- a/authserv/ldap_model.py +++ b/authserv/ldap_model.py @@ -1,6 +1,8 @@ import contextlib import ldap -from ldap.ldapobject import ReconnectLDAPObj +from ldap.dn import escape_dn_chars +from ldap.filter import escape_filter_chars +from ldap.ldapobject import LDAPObject from authserv import model @@ -24,35 +26,49 @@ class UserDb(model.UserDb): @contextlib.contextmanager def _conn(self): - c = ReconnectLDAPObj(self.ldap_uri) + c = LDAPObject(self.ldap_uri) c.protocol_version = ldap.VERSION3 c.simple_bind_s(self.ldap_bind_dn, self.ldap_bind_pw) yield c c.unbind_s() def _query_user(self, username, service): + # Allow referencing a service to another, by specifying a + # string rather than a dictionary as the value. If you build + # infinite loops this way, it's your fault. ldap_params = self.service_map.get(service) + while isinstance(ldap_params, basestring): + ldap_params = self.service_map.get(ldap_params) if not ldap_params: return None - if callable(ldap_params['dn']): - basedn = ldap_params['dn'](username) - else: - basedn = ldap_params['dn'] - with self._conn() as c: - result = c.search_s( - basedn, - ldap_params.get('scope', ldap.SCOPE_SUBTREE), - ldap_params['filter'].replace('%s', username), - self.ldap_attrs) - - if not result: - return None - if len(result) > 1: - raise Error('too many results from LDAP') - - return User(username, result[0][0], result[0][1]) + # LDAP queries can be built in two ways: + # + # - specify a fully-qualified 'dn' attribute, in which + # case we'll run a SCOPE_BASE query for that specific DN. + # In this case, 'filter' is optional. + # + # - specify 'base' and 'filter' together to identify a + # single object. This is a SCOPE_SUBTREE search and + # 'filter' is required. + # + if 'dn' in ldap_params: + base = ldap_params['dn'].replace('%s', escape_dn_chars(username)) + filt = ldap_params.get('filter', '').replace('%s', escape_filter_chars(username)) + scope = ldap.SCOPE_BASE + else: + base = ldap_params['base'].replace('%s', escape_dn_chars(username)) + filt = ldap_params['filter'].replace('%s', escape_filter_chars(username)) + scope = ldap.SCOPE_SUBTREE + result = c.search_s(base, scope, filt, self.ldap_attrs) + + if not result: + return None + if len(result) > 1: + raise Error('too many results from LDAP') + + return User(username, result[0][0], result[0][1]) def get_user(self, username, service): try: