def __init__(self, admin_field, admin_value, base_dn, cache_time, service_dn, service_password, service_username, url, user_search_filter, user_dn_format, ignore_cert, ignore_referrals, ignore_multiple_results): self._url = url self._service_dn = service_dn self._service_password = service_password self._base_dn = base_dn self._user_search_filter = user_search_filter self._user_dn_format = user_dn_format if user_dn_format is not None: if base_dn is not None or user_search_filter is not None: raise ValueError("Cannot use user_dn_format with base_dn " "and user_search_filter") else: if base_dn is None or user_search_filter is None: raise ValueError("Must provide user_dn_format or both base_dn " "and user_search_filter") self._admin_field = admin_field self._admin_value = admin_value self._server = None if cache_time is not None: cache_time = int(cache_time) self._cache = TimedCache(cache_time, self._fetch_user) if service_username is not None: self._cache.set_expire(service_username, User(service_username, service_dn, True), None) self._ignore_cert = ignore_cert self._ignore_referrals = ignore_referrals self._ignore_multiple_results = ignore_multiple_results
def __init__( self, admin_field, admin_group_dn, admin_value, base_dn, cache_time, service_dn, service_password, service_username, url, user_search_filter, user_dn_format, ignore_cert, ignore_referrals, ignore_multiple_results, ): self._url = url self._service_dn = service_dn self._service_password = service_password self._base_dn = base_dn self._user_search_filter = user_search_filter self._user_dn_format = user_dn_format if user_dn_format is not None: if base_dn is not None or user_search_filter is not None: raise ValueError( "Cannot use user_dn_format with base_dn " "and user_search_filter" ) else: if base_dn is None or user_search_filter is None: raise ValueError( "Must provide user_dn_format or both base_dn " "and user_search_filter" ) self._admin_field = admin_field self._admin_group_dn = admin_group_dn if admin_group_dn and not self._user_dn_format: raise ValueError( "ldap.admin_group_dn must be used with ldap.user_dn_format" ) self._admin_value = set(admin_value) self._server = None if cache_time is not None: cache_time = int(cache_time) self._cache = TimedCache(cache_time, self._fetch_user) if service_username is not None: self._cache.set_expire( service_username, User(service_username, service_dn, True), None ) self._ignore_cert = ignore_cert self._ignore_referrals = ignore_referrals self._ignore_multiple_results = ignore_multiple_results self._admin_member_type = None
class LDAP(object): """ Handles interactions with the remote LDAP server """ def __init__( self, admin_field, admin_group_dn, admin_value, base_dn, cache_time, service_dn, service_password, service_username, url, user_search_filter, user_dn_format, ignore_cert, ignore_referrals, ignore_multiple_results, ): self._url = url self._service_dn = service_dn self._service_password = service_password self._base_dn = base_dn self._user_search_filter = user_search_filter self._user_dn_format = user_dn_format if user_dn_format is not None: if base_dn is not None or user_search_filter is not None: raise ValueError("Cannot use user_dn_format with base_dn " "and user_search_filter") else: if base_dn is None or user_search_filter is None: raise ValueError("Must provide user_dn_format or both base_dn " "and user_search_filter") self._admin_field = admin_field self._admin_group_dn = admin_group_dn if admin_group_dn and not self._user_dn_format: raise ValueError( "ldap.admin_group_dn must be used with ldap.user_dn_format") self._admin_value = set(admin_value) self._server = None if cache_time is not None: cache_time = int(cache_time) self._cache = TimedCache(cache_time, self._fetch_user) if service_username is not None: self._cache.set_expire(service_username, User(service_username, service_dn, True), None) self._ignore_cert = ignore_cert self._ignore_referrals = ignore_referrals self._ignore_multiple_results = ignore_multiple_results self._admin_member_type = None def connect(self): """ Initializes the python-ldap module and does the initial bind """ if self._ignore_cert: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) if self._ignore_referrals: ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) LOG.debug("LDAP connecting to %s", self._url) self._server = ldap.initialize(self._url, bytes_mode=False) self._bind_to_service() @property def admin_member_type(self): if self._admin_member_type is None: LOG.debug("Fetching admin group %s", self._admin_group_dn) try: results = self._server.search_s(self._admin_group_dn, ldap.SCOPE_BASE, attrlist=["objectClass"]) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) return "member" dn, attributes = results[0] classes = [ self._decode_attribute(x) for x in attributes["objectClass"] ] if "groupOfUniqueNames" in classes: self._admin_member_type = "uniqueMember" else: self._admin_member_type = "member" return self._admin_member_type def _bind_to_service(self): """ Bind to the service account or anonymous """ if self._service_dn: # bind with the service_dn self._server.simple_bind_s(self._service_dn, self._service_password) else: # force a connection without binding self._server.whoami_s() @reconnect def test_connection(self): """ Binds to service. Will throw if bad connection """ self._bind_to_service() @reconnect def _fetch_user(self, username): """ Fetch a user entry from the LDAP server """ LOG.debug("LDAP fetching user %s", username) search_attrs = [] if self._admin_field is not None: search_attrs.append(self._admin_field) if self._user_dn_format is not None: dn = self._user_dn_format.format(username=username) LOG.debug("LDAP searching user %r with dn %r", username, dn) try: results = self._server.search_s(dn, ldap.SCOPE_BASE, attrlist=search_attrs) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) return else: search_filter = self._user_search_filter.format(username=username) LOG.debug("LDAP searching user %r with filter %r", username, search_filter) try: results = self._server.search_s(self._base_dn, ldap.SCOPE_SUBTREE, search_filter, search_attrs) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) return except ldap.NO_RESULTS_RETURNED as e: LOG.debug("NO_RESULTS_RETURNED %s", e) return if not results: LOG.debug("LDAP user %r not found", username) return None if len(results) > 1: err_msg = "More than one user found for %r: %r" % ( username, [r[0] for r in results], ) if self._ignore_multiple_results: LOG.warning(err_msg) else: raise ValueError(err_msg) dn, attributes = results[0] LOG.debug("dn: %r, attributes %r", dn, attributes) is_admin = False if self._admin_field is not None: if self._admin_field in attributes: is_admin = bool( self._admin_value.intersection([ self._decode_attribute(x) for x in attributes[self._admin_field] ])) if not is_admin and self._admin_group_dn: user_dn = self._user_dn_format.format(username=username) search_filter = "(%s=%s)" % (self.admin_member_type, user_dn) LOG.debug("Searching admin group %s for %s", self._admin_group_dn, search_filter) try: results = self._server.search_s(self._admin_group_dn, ldap.SCOPE_BASE, search_filter) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) else: is_admin = bool(results) return User(username, dn, is_admin) def _decode_attribute(self, attribute): if attribute and hasattr(attribute, "decode"): decoded = attribute.decode("utf-8") return decoded else: return attribute def get_user(self, username): """ Get the User object or None """ return self._cache.get(username) @reconnect def verify_user(self, username, password): """ Attempts to bind as the user, then rebinds as service user again """ LOG.debug("LDAP verifying user %s", username) # Empty password may successfully complete an anonymous bind. # Explicitly disallow empty passwords. if password == "": return False user = self._cache.get(username) if user is None: return False try: LOG.debug("LDAP binding user %r", user.dn) self._server.simple_bind_s(user.dn, password) except ldap.INVALID_CREDENTIALS: return False else: return True finally: self._bind_to_service()
class LDAP(object): """ Handles interactions with the remote LDAP server """ def __init__( self, admin_field, admin_group_dn, admin_value, base_dn, cache_time, service_dn, service_password, service_username, url, user_search_filter, user_dn_format, ignore_cert, ignore_referrals, ignore_multiple_results, ): self._url = url self._service_dn = service_dn self._service_password = service_password self._base_dn = base_dn self._user_search_filter = user_search_filter self._user_dn_format = user_dn_format if user_dn_format is not None: if base_dn is not None or user_search_filter is not None: raise ValueError( "Cannot use user_dn_format with base_dn " "and user_search_filter" ) else: if base_dn is None or user_search_filter is None: raise ValueError( "Must provide user_dn_format or both base_dn " "and user_search_filter" ) self._admin_field = admin_field self._admin_group_dn = admin_group_dn if admin_group_dn and not self._user_dn_format: raise ValueError( "ldap.admin_group_dn must be used with ldap.user_dn_format" ) self._admin_value = set(admin_value) self._server = None if cache_time is not None: cache_time = int(cache_time) self._cache = TimedCache(cache_time, self._fetch_user) if service_username is not None: self._cache.set_expire( service_username, User(service_username, service_dn, True), None ) self._ignore_cert = ignore_cert self._ignore_referrals = ignore_referrals self._ignore_multiple_results = ignore_multiple_results self._admin_member_type = None def connect(self): """ Initializes the python-ldap module and does the initial bind """ if self._ignore_cert: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) if self._ignore_referrals: ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) LOG.debug("LDAP connecting to %s", self._url) self._server = ldap.initialize(self._url, bytes_mode=False) self._bind_to_service() @property def admin_member_type(self): if self._admin_member_type is None: LOG.debug("Fetching admin group %s", self._admin_group_dn) try: results = self._server.search_s( self._admin_group_dn, ldap.SCOPE_BASE, attrlist=["objectClass"] ) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) return "member" dn, attributes = results[0] classes = [self._decode_attribute(x) for x in attributes["objectClass"]] if "groupOfUniqueNames" in classes: self._admin_member_type = "uniqueMember" else: self._admin_member_type = "member" return self._admin_member_type def _bind_to_service(self): """ Bind to the service account or anonymous """ if self._service_dn: # bind with the service_dn self._server.simple_bind_s(self._service_dn, self._service_password) else: # force a connection without binding self._server.whoami_s() @reconnect def test_connection(self): """ Binds to service. Will throw if bad connection """ self._bind_to_service() @reconnect def _fetch_user(self, username): """ Fetch a user entry from the LDAP server """ LOG.debug("LDAP fetching user %s", username) search_attrs = [] if self._admin_field is not None: search_attrs.append(self._admin_field) if self._user_dn_format is not None: dn = self._user_dn_format.format(username=username) LOG.debug("LDAP searching user %r with dn %r", username, dn) try: results = self._server.search_s( dn, ldap.SCOPE_BASE, attrlist=search_attrs ) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) return else: search_filter = self._user_search_filter.format(username=username) LOG.debug("LDAP searching user %r with filter %r", username, search_filter) try: results = self._server.search_s( self._base_dn, ldap.SCOPE_SUBTREE, search_filter, search_attrs ) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) return except ldap.NO_RESULTS_RETURNED as e: LOG.debug("NO_RESULTS_RETURNED %s", e) return if not results: LOG.debug("LDAP user %r not found", username) return None if len(results) > 1: err_msg = "More than one user found for %r: %r" % ( username, [r[0] for r in results], ) if self._ignore_multiple_results: LOG.warning(err_msg) else: raise ValueError(err_msg) dn, attributes = results[0] LOG.debug("dn: %r, attributes %r", dn, attributes) is_admin = False if self._admin_field is not None: if self._admin_field in attributes: is_admin = bool( self._admin_value.intersection( [ self._decode_attribute(x) for x in attributes[self._admin_field] ] ) ) if not is_admin and self._admin_group_dn: user_dn = self._user_dn_format.format(username=username) search_filter = "(%s=%s)" % (self.admin_member_type, user_dn) LOG.debug( "Searching admin group %s for %s", self._admin_group_dn, search_filter ) try: results = self._server.search_s( self._admin_group_dn, ldap.SCOPE_BASE, search_filter ) except ldap.NO_SUCH_OBJECT as e: LOG.debug("NO_SUCH_OBJECT %s", e) else: is_admin = bool(results) return User(username, dn, is_admin) def _decode_attribute(self, attribute): if attribute and hasattr(attribute, "decode"): decoded = attribute.decode("utf-8") return decoded else: return attribute def get_user(self, username): """ Get the User object or None """ return self._cache.get(username) @reconnect def verify_user(self, username, password): """ Attempts to bind as the user, then rebinds as service user again """ LOG.debug("LDAP verifying user %s", username) # Empty password may successfully complete an anonymous bind. # Explicitly disallow empty passwords. if password == "": return False user = self._cache.get(username) if user is None: return False try: LOG.debug("LDAP binding user %r", user.dn) self._server.simple_bind_s(user.dn, password) except ldap.INVALID_CREDENTIALS: return False else: return True finally: self._bind_to_service()
class LDAP(object): """ Handles interactions with the remote LDAP server """ def __init__(self, admin_field, admin_value, base_dn, cache_time, service_dn, service_password, service_username, url, user_search_filter, user_dn_format, ignore_cert, ignore_referrals, ignore_multiple_results): self._url = url self._service_dn = service_dn self._service_password = service_password self._base_dn = base_dn self._user_search_filter = user_search_filter self._user_dn_format = user_dn_format if user_dn_format is not None: if base_dn is not None or user_search_filter is not None: raise ValueError("Cannot use user_dn_format with base_dn " "and user_search_filter") else: if base_dn is None or user_search_filter is None: raise ValueError("Must provide user_dn_format or both base_dn " "and user_search_filter") self._admin_field = admin_field self._admin_value = admin_value self._server = None if cache_time is not None: cache_time = int(cache_time) self._cache = TimedCache(cache_time, self._fetch_user) if service_username is not None: self._cache.set_expire(service_username, User(service_username, service_dn, True), None) self._ignore_cert = ignore_cert self._ignore_referrals = ignore_referrals self._ignore_multiple_results = ignore_multiple_results def connect(self): """ Initializes the python-ldap module and does the initial bind """ if self._ignore_cert: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) if self._ignore_referrals: ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) LOG.debug("LDAP connecting to %s", self._url) self._server = ldap.initialize(self._url) self._bind_to_service() def _bind_to_service(self): """ Bind to the service account or anonymous """ if self._service_dn: # bind with the service_dn self._server.simple_bind_s(self._service_dn, self._service_password) else: # force a connection without binding self._server.whoami_s() @reconnect def _fetch_user(self, username): """ Fetch a user entry from the LDAP server """ LOG.debug("LDAP fetching user %s", username) search_attrs = [] if self._admin_field is not None: search_attrs.append(self._admin_field) if self._user_dn_format is not None: dn = self._user_dn_format.format(username=username) LOG.debug("LDAP searching user %r with dn %r", username, dn) results = self._server.search_s(dn, ldap.SCOPE_BASE, attrlist=search_attrs) else: search_filter = self._user_search_filter.format(username=username) LOG.debug("LDAP searching user %r with filter %r", username, search_filter) results = self._server.search_s( self._base_dn, ldap.SCOPE_SUBTREE, search_filter, search_attrs, ) if not results: LOG.debug("LDAP user %r not found", username) return None if len(results) > 1: err_msg = "More than one user found for %r: %r" % ( username, [r[0] for r in results]) if self._ignore_multiple_results: LOG.warning(err_msg) else: raise ValueError(err_msg) dn, attributes = results[0] is_admin = False if self._admin_field is not None: if self._admin_field in attributes: is_admin = any((val in attributes[self._admin_field] for val in self._admin_value)) return User(username, dn, is_admin) def get_user(self, username): """ Get the User object or None """ return self._cache.get(username) @reconnect def verify_user(self, username, password): """ Attempts to bind as the user, then rebinds as service user again """ LOG.debug("LDAP verifying user %s", username) # Empty password may successfully complete an anonymous bind. # Explicitly disallow empty passwords. if password == "": return False user = self._cache.get(username) if user is None: return False try: LOG.debug("LDAP binding user %r", user.dn) self._server.simple_bind_s(user.dn, password) except ldap.INVALID_CREDENTIALS: return False else: return True finally: self._bind_to_service()