class DatabaseWrapper(BaseDatabaseWrapper): def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.charset = "utf-8" self.creation = DatabaseCreation(self) self.features = DatabaseFeatures(self) if django.VERSION > (1, 4): self.ops = DatabaseOperations(self) else: self.ops = DatabaseOperations() self.settings_dict['SUPPORTS_TRANSACTIONS'] = False def close(self): pass def _commit(self): pass def _cursor(self): if self.connection is None: # self.connection = ldap.initialize(self.settings_dict['NAME']) self.connection = ReconnectLDAPObject(self.settings_dict['NAME']) self.connection.simple_bind_s( self.settings_dict['USER'], self.settings_dict['PASSWORD']) return DatabaseCursor(self.connection) def _rollback(self): pass def add_s(self, dn, modlist): cursor = self._cursor() return cursor.connection.add_s(dn.encode(self.charset), modlist) def delete_s(self, dn): cursor = self._cursor() return cursor.connection.delete_s(dn.encode(self.charset)) def modify_s(self, dn, modlist): cursor = self._cursor() return cursor.connection.modify_s(dn.encode(self.charset), modlist) def rename_s(self, dn, newrdn): cursor = self._cursor() return cursor.connection.rename_s(dn.encode(self.charset), newrdn.encode(self.charset)) def search_s(self, base, scope, filterstr='(objectClass=*)',attrlist=None): logger.debug("base: %s; scope: %s; filter: %s, attrs: %s" % (base, scope, filterstr, attrlist)) cursor = self._cursor() results = cursor.connection.search_s(base, scope, filterstr.encode(self.charset), attrlist) output = [] for dn, attrs in results: output.append((dn.decode(self.charset), attrs)) return output
def simple_bind_s(self, who='', cred='', serverctrls=None, clientctrls=None): res = ReconnectLDAPObject.simple_bind_s(self, who, cred, serverctrls, clientctrls) self.connected = True self.who = who self.cred = cred return res
def _cursor(self): if self.connection is None: # self.connection = ldap.initialize(self.settings_dict['NAME']) self.connection = ReconnectLDAPObject(self.settings_dict['NAME']) self.connection.simple_bind_s( self.settings_dict['USER'], self.settings_dict['PASSWORD']) return DatabaseCursor(self.connection)
def unbind_ext_s(self, serverctrls=None, clientctrls=None): try: return ReconnectLDAPObject.unbind_ext_s(self, serverctrls, clientctrls) finally: self.connected = False self.who = None self.cred = None
def connect(self): try: self.connection = ReconnectLDAPObject(self.uri) if self.user is not None: self.connection.simple_bind_s(self.user, self.password) except: logger.exception("LDAP connection failed") return False return True
def simple_bind_s(self, who='', cred='', serverctrls=None, clientctrls=None): res = ReconnectLDAPObject.simple_bind_s(self, who, cred, serverctrls, clientctrls) self.connected = True self.who = who self.cred = cred if self._connection_time is None: self._connection_time = time.time() return res
def ensure_connection(self): if self.connection is None: #self.connection = ldap.initialize(self.settings_dict['NAME']) self.connection = ReconnectLDAPObject(self.settings_dict['NAME']) options = self.settings_dict.get('CONNECTION_OPTIONS', {}) for opt, value in options.items(): self.connection.set_option(opt, value) if self.settings_dict.get('TLS', False): self.connection.start_tls_s() self.connection.simple_bind_s( self.settings_dict['USER'], self.settings_dict['PASSWORD'])
def main(): # Skip handshake when testing manually if stdin.isatty(): print('stdin is tty, skipping handshake') else: if stdin.readline() == 'HELO\t1\n': output('OK', 'pdns-ldap-py is ready') else: output('FAIL') stdin.readline() exit(1) # XXX Global variable global connection connection = ReconnectLDAPObject(config.uri, retry_max=4, retry_delay=5) # XXX A little ugly here connection.deref = ldap.DEREF_FINDING if config.start_tls: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) connection.start_tls_s() connection.bind_s(config.binddn, config.bindpw) while True: line = stdin.readline() if len(line) == 0: continue fields = line.rstrip('\n').split('\t') try: if respond(fields): output('END') else: output('FAIL') except Exception: output('FAIL') output('LOG', 'Shutting down on unexpected error. Traceback:') tb_lines = traceback.format_exc().split('\n') for line in tb_lines: output('LOG', line) break
def __init__(self, *args, **kw): ReconnectLDAPObject.__init__(self, *args, **kw) self.connected = False self.who = '' self.cred = '' self._connection_time = None
class DatabaseWrapper(BaseDatabaseWrapper): def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.charset = "utf-8" self.creation = DatabaseCreation(self) self.features = DatabaseFeatures(self) if django.VERSION > (1, 4): self.ops = DatabaseOperations(self) else: self.ops = DatabaseOperations() self.settings_dict['SUPPORTS_TRANSACTIONS'] = False def close(self): if hasattr(self, 'validate_thread_sharing'): # django >= 1.4 self.validate_thread_sharing() if self.connection is not None: self.connection.unbind_s() self.connection = None def _commit(self): pass def _cursor(self): if self.connection is None: # self.connection = ldap.initialize(self.settings_dict['NAME']) self.connection = ReconnectLDAPObject(self.settings_dict['NAME']) self.connection.simple_bind_s( self.settings_dict['USER'], self.settings_dict['PASSWORD']) return DatabaseCursor(self.connection) def _rollback(self): pass def add_s(self, dn, modlist): cursor = self._cursor() return cursor.connection.add_s(dn.encode(self.charset), modlist) def delete_s(self, dn): cursor = self._cursor() return cursor.connection.delete_s(dn.encode(self.charset)) def modify_s(self, dn, modlist): cursor = self._cursor() return cursor.connection.modify_s(dn.encode(self.charset), modlist) def rename_s(self, dn, newrdn): cursor = self._cursor() return cursor.connection.rename_s(dn.encode(self.charset), newrdn.encode(self.charset)) def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None): logger.debug("base: %s; scope: %s; filter: %s, attrs: %s" % (base, scope, filterstr, attrlist)) cursor = self._cursor() results = cursor.connection.search_s(base, scope, filterstr.encode(self.charset), attrlist) output = [] for dn, attrs in results: output.append((dn.decode(self.charset), attrs)) return output
def ldap_connect(settings, use_cache=True): """Establishes an LDAP connection. Establishes a connection to the LDAP server from the `uri` in the ``settings``. To establish a connection, the settings must be specified: - ``uri``: valid URI which points to a LDAP server, - ``bind_dn``: `dn` used to initially bind every LDAP connection - ``bind_password``" password used for the initial bind - ``tls``: ``True`` if the connection should use TLS encryption - ``starttls``: ``True`` to negotiate TLS with the server `Note`: ``starttls`` is ignored if the URI uses LDAPS and ``tls`` is set to ``True``. This function re-uses an existing LDAP connection if there is one available in the application context, unless caching is disabled. :param settings: dict -- The settings for a LDAP provider. :param use_cache: bool -- If the connection should be cached. :return: The ldap connection. """ if use_cache: cache = _get_ldap_cache() cache_key = frozenset( (k, hash(v)) for k, v in iteritems(settings) if k in conn_keys) conn = cache.get(cache_key) if conn is not None: return conn uri_info = urlparse(settings['uri']) use_ldaps = uri_info.scheme == 'ldaps' credentials = (settings['bind_dn'], settings['bind_password']) ldap_connection = ReconnectLDAPObject(settings['uri']) ldap_connection.protocol_version = ldap.VERSION3 ldap_connection.set_option(ldap.OPT_REFERRALS, 0) ldap_connection.set_option( ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND if use_ldaps else ldap.OPT_X_TLS_NEVER) if settings['verify_cert'] and settings['cert_file']: ldap_connection.set_option(ldap.OPT_X_TLS_CACERTFILE, settings['cert_file']) ldap_connection.set_option( ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND if settings['verify_cert'] else ldap.OPT_X_TLS_ALLOW) # force the creation of a new TLS context. This must be the last TLS option. # see: http://stackoverflow.com/a/27713355/298479 ldap_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0) if use_ldaps and settings['starttls']: warn( "Unable to start TLS, LDAP connection already secured over SSL (LDAPS)" ) elif settings['starttls']: ldap_connection.start_tls_s() # TODO: allow anonymous bind ldap_connection.simple_bind_s(*credentials) if use_cache: cache[cache_key] = ldap_connection return ldap_connection
class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'ldap' # NOTE: These are copied from the mysql DatabaseWrapper operators = { 'exact': '= %s', 'iexact': 'LIKE %s', 'contains': 'LIKE BINARY %s', 'icontains': 'LIKE %s', 'regex': 'REGEXP BINARY %s', 'iregex': 'REGEXP %s', 'gt': '> %s', 'gte': '>= %s', 'lt': '< %s', 'lte': '<= %s', 'startswith': 'LIKE BINARY %s', 'endswith': 'LIKE BINARY %s', 'istartswith': 'LIKE %s', 'iendswith': 'LIKE %s', } def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.charset = "utf-8" self.creation = DatabaseCreation(self) self.features = DatabaseFeatures(self) if django.VERSION > (1, 4): self.ops = DatabaseOperations(self) else: self.ops = DatabaseOperations() self.settings_dict['SUPPORTS_TRANSACTIONS'] = True self.autocommit = True def close(self): if hasattr(self, 'validate_thread_sharing'): # django >= 1.4 self.validate_thread_sharing() if self.connection is not None: self.connection.unbind_s() self.connection = None def ensure_connection(self): if self.connection is None: #self.connection = ldap.initialize(self.settings_dict['NAME']) self.connection = ReconnectLDAPObject(self.settings_dict['NAME']) options = self.settings_dict.get('CONNECTION_OPTIONS', {}) for opt, value in options.items(): self.connection.set_option(opt, value) if self.settings_dict.get('TLS', False): self.connection.start_tls_s() self.connection.simple_bind_s( self.settings_dict['USER'], self.settings_dict['PASSWORD']) def _commit(self): pass def _cursor(self): self.ensure_connection() return DatabaseCursor(self.connection) def _rollback(self): pass def _set_autocommit(self, autocommit): pass def add_s(self, dn, modlist): cursor = self._cursor() return cursor.connection.add_s(dn.encode(self.charset), modlist) def delete_s(self, dn): cursor = self._cursor() return cursor.connection.delete_s(dn.encode(self.charset)) def modify_s(self, dn, modlist): cursor = self._cursor() return cursor.connection.modify_s(dn.encode(self.charset), modlist) def rename_s(self, dn, newrdn): cursor = self._cursor() return cursor.connection.rename_s(dn.encode(self.charset), newrdn.encode(self.charset)) def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None): cursor = self._cursor() results = cursor.connection.search_s(base, scope, filterstr.encode(self.charset), attrlist) output = [] for dn, attrs in results: # skip referrals if dn is not None: output.append((dn.decode(self.charset), attrs)) return output
class LDAPUser(object): EPOCH_DATE = datetime.datetime.fromtimestamp(0) LOGIN_SHELL = "/bin/bash" HOME_BASE_DIRECTORY = "/home" GROUP_ID_NUMBER = None CREATOR = None VERIFY_CREATOR = True password_compile = re.compile("^{(%s)}(\S+)$" % "|".join(SUPPORT_METHOD)) def __init__(self, ldap_uri, base_dn, admin_user, admin_password, home_base_directory=None, group_id=None): self.ldap_uri = ldap_uri self.ldap_com = ReconnectLDAPObject(ldap_uri, retry_max=10, retry_delay=3) # self.ldap_com = ldap.initialize(ldap_uri) self.ldap_base_dn = base_dn if home_base_directory is not None: self.HOME_BASE_DIRECTORY = home_base_directory if group_id is not None: self.GROUP_ID_NUMBER = "%s" % group_id self.ldap_admin = "cn=%s,%s" % (admin_user, self.ldap_base_dn) self.people_base_cn = "ou=People,%s" % self.ldap_base_dn self.ldap_com.bind_s(self.ldap_admin, admin_password) def __get_next_uid_number(self): filter_str = "uid=*" attributes = ["uidNumber", "gidNumber"] items = self.ldap_com.search_s(self.people_base_cn, LDAP_SCOPE_SUBTREE, filter_str, attributes) max_number = 1000 for item in items: if "uidNumber" in item[1]: u_number = int(item[1]["uidNumber"][0]) if max_number < u_number: max_number = u_number return max_number + 1 def delete_user(self, user_name): if self.exist(user_name) is None: return True dn = "uid=%s,%s" % (user_name, self.people_base_cn) return self.ldap_com.delete_s(dn) def add_user(self, user_name, uid_number=None, gid_number=None, home_directory=None): dn = "uid=%s,%s" % (user_name, self.people_base_cn) attributes = dict() for key in ("uid", "cn", "sn"): attributes[key] = encode(user_name) attributes["objectClass"] = [ "top", "person", "inetOrgPerson", "posixAccount", "organizationalPerson", "shadowAccount" ] if uid_number is None: attributes["uidNumber"] = encode(self.__get_next_uid_number()) else: attributes["uidNumber"] = encode(uid_number) if gid_number is None: attributes["gidNumber"] = encode(self.GROUP_ID_NUMBER) else: attributes["gidNumber"] = encode(gid_number) if attributes["gidNumber"] is None: raise LDAPError("please set gidNumber") if home_directory is None: home_directory = os.path.join(self.HOME_BASE_DIRECTORY, user_name) attributes["homeDirectory"] = encode(home_directory) attributes["loginShell"] = encode(self.LOGIN_SHELL) if self.CREATOR is not None: attributes["givenName"] = encode(self.CREATOR) mod_list = ldap.modlist.addModlist(attributes) self.ldap_com.add_s(dn, mod_list) def expire_user(self, user_name, shadow_expire=None): user_entry = self.exist(user_name) if user_entry is None: return False old_attributes = user_entry[1] attributes = dict(**old_attributes) if shadow_expire is None: attributes["shadowExpire"] = [ "%s" % (datetime.datetime.now() - self.EPOCH_DATE).days ] else: attributes["shadowExpire"] = "" mod_list = ldap.modlist.modifyModlist(old_attributes, attributes) return self.ldap_com.modify_s(user_entry[0], mod_list) def block_user(self, user_name): return self.expire_user(user_name) def unlock_user(self, user_name): user_entry = self.exist(user_name, "pwdAccountLockedTime", "shadowExpire") if user_entry is None: return False old_attributes = user_entry[1] attributes = dict(**old_attributes) if "pwdAccountLockedTime" in attributes: attributes["pwdAccountLockedTime"] = "" if "shadowExpire" in attributes: attributes["shadowExpire"] = "" mod_list = ldap.modlist.modifyModlist(old_attributes, attributes) return self.ldap_com.modify_s(user_entry[0], mod_list) def _verify_creator(self, user_entry): if self.CREATOR is None or self.VERIFY_CREATOR is False: return True if "givenName" not in user_entry: return False return self.CREATOR in user_entry["givenName"] def exist(self, user_name, *attributes): l_attributes = set(attributes) sr = self.ldap_com.search_s(self.ldap_base_dn, LDAP_SCOPE_SUBTREE, "uid=%s" % user_name, l_attributes) if len(sr) <= 0: return None return sr[0] def verify_password(self, password, ldap_password): match_r = self.password_compile.match(ldap_password) if match_r is not None: method = match_r.groups()[0] cipher_text = match_r.groups()[1] return verify(method, password, cipher_text) return password == ldap_password def login(self, user_name, password): user_entry = self.exist(user_name) if user_entry is None: return False attr = user_entry[1] if "userPassword" not in attr: return False ldap_password = attr["userPassword"][0] return self.verify_password(password, ldap_password) def login2(self, user_name, password): user_entry = self.exist(user_name) if user_entry is None: return False attr = user_entry[1] if "userPassword" not in attr: return False try: self.ldap_com.simple_bind_s(user_entry[0], password) return True except ldap.INVALID_CREDENTIALS: return False def set_password(self, user_name, new_password): user_entry = self.exist(user_name) if user_entry is None: return False if self._verify_creator(user_entry) is False: return False pr = self.ldap_com.passwd_s(user_entry[0], None, new_password) if pr[0] is None and pr[1] is None: return True return False
def __init__(self,uri, trace_level=0,trace_file=None,trace_stack_limit=5, retry_max=1,retry_delay=60.0, who='',cred='', start_tls=1, tls_cacertfile=None,tls_cacertdir=None, tls_clcertfile=None,tls_clkeyfile=None, ): """ Return LDAPObject instance by opening LDAP connection to LDAP host specified by LDAP URL. Unlike ldap.initialize() this function also trys to bind explicitly with the bind DN and credential given as parameter, probe the supported LDAP version and trys to use StartTLS extended operation if this was specified. Parameters like ReconnectLDAPObject.__init__() with these additional arguments: who,cred The Bind-DN and credential to use for simple bind right after connecting. start_tls Determines if StartTLS extended operation is tried on a LDAPv3 server and if the LDAP URL scheme is ldap:. If LDAP URL scheme is not ldap: (e.g. ldaps: or ldapi:) this parameter is ignored. 0 Don't use StartTLS ext op 1 Try StartTLS ext op but proceed when unavailable 2 Try StartTLS ext op and re-raise exception if it fails tls_cacertfile tls_clcertfile tls_clkeyfile """ # Initialize LDAP connection ReconnectLDAPObject.__init__( self,uri, trace_level=trace_level, trace_file=trace_file, trace_stack_limit=trace_stack_limit, retry_max=retry_max, retry_delay=retry_delay ) # Set protocol version to LDAPv3 self.protocol_version = ldap.VERSION3 self.started_tls = 0 try: self.simple_bind_s(who,cred) except ldap.PROTOCOL_ERROR: # Drop connection completely self.unbind_s() ; del self._l self._l = ldap.functions._ldap_function_call(_ldap.initialize,self._uri) self.protocol_version = ldap.VERSION2 self.simple_bind_s(who,cred) # Try to start TLS if requested if start_tls>0 and uri[:5]=='ldaps:': if self.protocol_version>=ldap.VERSION3: try: self.start_tls_s() except (ldap.PROTOCOL_ERROR,ldap.CONNECT_ERROR): if start_tls>=2: # Application does not accept clear-text connection # => re-raise exception raise else: self.started_tls = 1 else: if start_tls>=2: raise ValueError,"StartTLS extended operation only possible on LDAPv3+ server!" if self.protocol_version==ldap.VERSION2 or (who and cred): self.simple_bind_s(who,cred)
class CSHLDAP: __domain__ = "csh.rit.edu" def __init__(self, bind_dn, bind_pw, batch_mods=False, sasl=False, ro=False): """Handler for bindings to CSH LDAP. Keyword arguments: batch_mods -- whether or not to batch LDAP writes (default False) sasl -- whether or not to bypass bind_dn and bind_pw and use SASL bind ro -- whether or not CSH LDAP is in read only mode (default False) """ if ro: print("########################################\n" "# #\n" "# CSH LDAP IS IN READ ONLY MODE #\n" "# #\n" "########################################") ldap_srvs = srvlookup.lookup("ldap", "tcp", self.__domain__) ldap_uris = "" for uri in ldap_srvs: ldap_uris += "ldaps://" + uri.hostname + "," self.__con__ = ReconnectLDAPObject(ldap_uris) if sasl: self.__con__.sasl_non_interactive_bind_s('') else: self.__con__.simple_bind_s(bind_dn, bind_pw) self.__mod_queue__ = {} self.__pending_mod_dn__ = [] self.__batch_mods__ = batch_mods self.__ro__ = ro def get_member(self, val, uid=False): """Get a CSHMember object. Arguments: val -- the uuid (or uid) of the member Keyword arguments: uid -- whether or not val is a uid (default False) """ return CSHMember(self, val, uid) def get_member_ibutton(self, val): """Get a CSHMember object. Arguments: val -- the iButton ID of the member Returns: None if the iButton supplied does not correspond to a CSH Member """ members = self.__con__.search_s(CSHMember.__ldap_user_ou__, ldap.SCOPE_SUBTREE, "(ibutton=%s)" % val, ['ipaUniqueID']) if members: return CSHMember(self, members[0][1]['ipaUniqueID'][0].decode('utf-8'), False) return None def get_group(self, val): """Get a CSHGroup object. Arguments: val -- the cn of the group """ return CSHGroup(self, val) def get_con(self): """Get the PyLDAP Connection""" return self.__con__ def get_directorship_heads(self, val): """Get the head of a directorship Arguments: val -- the cn of the directorship """ __ldap_group_ou__ = "cn=groups,cn=accounts,dc=csh,dc=rit,dc=edu" res = self.__con__.search_s(__ldap_group_ou__, ldap.SCOPE_SUBTREE, "(cn=eboard-%s)" % val, ['member']) ret = [] for member in res[0][1]['member']: try: ret.append(member.decode('utf-8')) except UnicodeDecodeError: ret.append(member) except KeyError: continue return [ CSHMember(self, dn.split('=')[1].split(',')[0], True) for dn in ret ] def enqueue_mod(self, dn, mod): """Enqueue a LDAP modification. Arguments: dn -- the distinguished name of the object to modify mod -- an ldap modfication entry to enqueue """ # mark for update if dn not in self.__pending_mod_dn__: self.__pending_mod_dn__.append(dn) self.__mod_queue__[dn] = [] self.__mod_queue__[dn].append(mod) def flush_mod(self): """Flush all pending LDAP modifications.""" for dn in self.__pending_mod_dn__: try: if self.__ro__: for mod in self.__mod_queue__[dn]: if mod[0] == ldap.MOD_DELETE: mod_str = "DELETE" elif mod[0] == ldap.MOD_ADD: mod_str = "ADD" else: mod_str = "REPLACE" print("{} VALUE {} = {} FOR {}".format( mod_str, mod[1], mod[2], dn)) else: self.__con__.modify_s(dn, self.__mod_queue__[dn]) except ldap.TYPE_OR_VALUE_EXISTS: print("Error! Conflicting Batch Modification: %s" % str(self.__mod_queue__[dn])) continue except ldap.NO_SUCH_ATTRIBUTE: print("Error! Conflicting Batch Modification: %s" % str(self.__mod_queue__[dn])) continue self.__mod_queue__[dn] = None self.__pending_mod_dn__ = []
class LDAPLookup: """ Wraps ldap library query Args: ldap_url: LDAP server in the form of 'ldap://ldaphost' ldap_base: LDAP base for search ('ou=users,dc=department,dc=org') ldap_retry_max: LDAP number of reconnect attempts ldap_retry_delay: LDAP seconds between reconnect attempts """ DEFAULT_QUERY_FIELDS: List[str] = ['uid'] DEFAULT_RETURN_FIELDS: List[str] = ['uid', 'cn', 'mail'] def __init__(self, ldap_url: str, ldap_base: str, ldap_retry_max: int = 3, ldap_retry_delay: float = 5.0): self.ldap_url = ldap_url self.ldap_base = ldap_base self.ldap_retry_max = ldap_retry_max self.ldap_retry_delay = ldap_retry_delay self._ldap_client = None # lazy client init @property def ldap_client(self): if not self._ldap_client: self._ldap_client = ReconnectLDAPObject( self.ldap_url, retry_max=self.ldap_retry_max, retry_delay=self.ldap_retry_delay) self._ldap_client.set_option(ldap.OPT_PROTOCOL_VERSION, 3) self._ldap_client.set_option(ldap.OPT_REFERRALS, 0) return self._ldap_client def query( self, query: str, query_fields: List[str] = None, return_fields: List[str] = None, raise_exception: bool = False, ) -> List[dict]: """ Perform LDAP query Args: query: String to search query_fields: Which LDAP fields to search in return_fields: What LDAP fields to return raise_exception: If True - raise exception if no results Returns: List if dicts with LDAP results Example: [ {'uid': 'us1', 'cn': 'user 1', 'mail': '*****@*****.**'}, {'uid': 'us2', 'cn': 'user 2', 'mail': '*****@*****.**'} ] Raises: LDAPQueryNotFoundError: No result while raise_exception True """ query = query.rstrip('*') if not query_fields: query_fields = self.DEFAULT_QUERY_FIELDS if not return_fields: return_fields = self.DEFAULT_RETURN_FIELDS if len(query_fields) == 1: query_string = f'{query_fields[0]}={query}' else: # Example: (|(cn=query*)(sn=query*)(mail=query*)) field_queries = [ f'({field}={query}*)' for field in query_fields if field ] query_string = '(|%s)' % ''.join(field_queries) res = self.ldap_client.search_s(self.ldap_base, ldap.SCOPE_SUBTREE, query_string, return_fields) if raise_exception and not res: raise LDAPQueryNotFoundError(f'Query not found in LDAP: {query}') # Extract first values, convert from bytes return [{k: v[0].decode('utf-8') for k, v in record[1].items()} for record in res]
class LdapLookup(object): connection = None uri = None user = None password = None user_searchbase = '' group_searchbase = '' user_searchfilter = {'objectClass': 'user'} group_searchfilter = {'objectClass': 'group'} def __init__(self, **kw): for key, item in kw.items(): if hasattr(self, key) and not key.startswith('_'): setattr(self, key, item) def connect(self): try: self.connection = ReconnectLDAPObject(self.uri) if self.user is not None: self.connection.simple_bind_s(self.user, self.password) except: logger.exception("LDAP connection failed") return False return True def get_safe(self, basedn, **kw): return self.get(basedn, **dict([(ldap_escape(k), ldap_escape(v)) for k, v in kw.iteritems()])) def get(self, basedn, **kw): search = '(&%s)' % ''.join(['(%s=%s)' % item for item in kw.iteritems()]) result = self.connection.search_s(basedn, ldap.SCOPE_SUBTREE, search) return result def get_dn(self, dn): res = self.connection.search_s(dn, ldap.SCOPE_BASE, '(objectClass=*)') if len(res) == 0: return None else: return res[0] def get_users(self): return self.get(self.user_searchbase, **self.user_searchfilter) def get_user(self, username): search = self.user_searchfilter.copy() if '@' in username: search['userPrincipalName'] = username else: search['sAMAccountName'] = username res = self.get_safe(self.user_searchbase, **search) if len(res) == 0: return None else: return res[0] def get_groups(self): return self.get(self.group_searchbase, **self.group_searchfilter) def get_group(self, groupname): search = self.group_searchfilter.copy() search['name'] = groupname res = self.get_safe(self.group_searchbase, **search) if len(res) == 0: return None else: return res[0]
def ldap_connect(settings, use_cache=True): """Establishes an LDAP connection. Establishes a connection to the LDAP server from the `uri` in the ``settings``. To establish a connection, the settings must be specified: - ``uri``: valid URI which points to a LDAP server, - ``bind_dn``: `dn` used to initially bind every LDAP connection - ``bind_password``" password used for the initial bind - ``tls``: ``True`` if the connection should use TLS encryption - ``starttls``: ``True`` to negotiate TLS with the server `Note`: ``starttls`` is ignored if the URI uses LDAPS and ``tls`` is set to ``True``. This function re-uses an existing LDAP connection if there is one available in the application context, unless caching is disabled. :param settings: dict -- The settings for a LDAP provider. :param use_cache: bool -- If the connection should be cached. :return: The ldap connection. """ if use_cache: cache = _get_ldap_cache() cache_key = frozenset((k, hash(v)) for k, v in iteritems(settings) if k in conn_keys) conn = cache.get(cache_key) if conn is not None: return conn uri_info = urlparse(settings['uri']) use_ldaps = uri_info.scheme == 'ldaps' credentials = (settings['bind_dn'], settings['bind_password']) ldap_connection = ReconnectLDAPObject(settings['uri']) ldap_connection.protocol_version = ldap.VERSION3 ldap_connection.set_option(ldap.OPT_REFERRALS, 0) ldap_connection.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND if use_ldaps else ldap.OPT_X_TLS_NEVER) if settings['cert_file']: ldap_connection.set_option(ldap.OPT_X_TLS_CACERTFILE, settings['cert_file']) ldap_connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND if settings['verify_cert'] else ldap.OPT_X_TLS_ALLOW) # force the creation of a new TLS context. This must be the last TLS option. # see: http://stackoverflow.com/a/27713355/298479 ldap_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0) if use_ldaps and settings['starttls']: warn("Unable to start TLS, LDAP connection already secured over SSL (LDAPS)") elif settings['starttls']: ldap_connection.start_tls_s() # TODO: allow anonymous bind ldap_connection.simple_bind_s(*credentials) if use_cache: cache[cache_key] = ldap_connection return ldap_connection
class LdapLookup(object): connection = None uri = None user = None password = None user_searchbase = '' group_searchbase = '' user_searchfilter = {'objectClass': 'user'} group_searchfilter = {'objectClass': 'group'} def __init__(self, **kw): for key, item in kw.items(): if hasattr(self, key) and not key.startswith('_'): setattr(self, key, item) def connect(self): try: self.connection = ReconnectLDAPObject(self.uri) if self.user is not None: self.connection.simple_bind_s(self.user, self.password) except: logger.exception("LDAP connection failed") return False return True def get_safe(self, basedn, **kw): return self.get( basedn, **dict([(ldap_escape(k), ldap_escape(v)) for k, v in kw.iteritems()])) def get(self, basedn, **kw): search = '(&%s)' % ''.join( ['(%s=%s)' % item for item in kw.iteritems()]) result = self.connection.search_s(basedn, ldap.SCOPE_SUBTREE, search) return result def get_dn(self, dn): res = self.connection.search_s(dn, ldap.SCOPE_BASE, '(objectClass=*)') if len(res) == 0: return None else: return res[0] def get_users(self): return self.get(self.user_searchbase, **self.user_searchfilter) def get_user(self, username): search = self.user_searchfilter.copy() if '@' in username: search['userPrincipalName'] = username else: search['sAMAccountName'] = username res = self.get_safe(self.user_searchbase, **search) if len(res) == 0: return None else: return res[0] def get_groups(self): return self.get(self.group_searchbase, **self.group_searchfilter) def get_group(self, groupname): search = self.group_searchfilter.copy() search['name'] = groupname res = self.get_safe(self.group_searchbase, **search) if len(res) == 0: return None else: return res[0]