def normalize_dn(dn): # Capitalize the attribute names as an LDAP server might. # NOTE(blk-u): Special case for this tested value, used with # test_user_id_comma. The call to str2dn here isn't always correct # here, because `dn` is escaped for an LDAP filter. str2dn() normally # works only because there's no special characters in `dn`. if dn == 'cn=Doe\\5c, John,ou=Users,cn=example,cn=com': return 'CN=Doe\\, John,OU=Users,CN=example,CN=com' # NOTE(blk-u): Another special case for this tested value. When a # roleOccupant has an escaped comma, it gets converted to \2C. if dn == 'cn=Doe\\, John,ou=Users,cn=example,cn=com': return 'CN=Doe\\2C John,OU=Users,CN=example,CN=com' try: dn = ldap.dn.str2dn(core.utf8_encode(dn)) except ldap.DECODING_ERROR: # NOTE(amakarov): In case of IDs instead of DNs in group members # they must be handled as regular values. return normalize_value(dn) norm = [] for part in dn: name, val, i = part[0] name = core.utf8_decode(name) name = name.upper() name = core.utf8_encode(name) norm.append([(name, val, i)]) return core.utf8_decode(ldap.dn.dn2str(norm))
def simple_bind_s(self, who='', cred='', serverctrls=None, clientctrls=None): """This method is ignored, but provided for compatibility.""" if server_fail: raise ldap.SERVER_DOWN whos = ['cn=Admin', CONF.ldap.user] if who in whos and cred in ['password', CONF.ldap.password]: return try: attrs = self.db[self.key(who)] except KeyError: LOG.debug('bind fail: who=%s not found', core.utf8_decode(who)) raise ldap.NO_SUCH_OBJECT db_password = None try: db_password = attrs['userPassword'][0] except (KeyError, IndexError): LOG.debug('bind fail: password for who=%s not found', core.utf8_decode(who)) raise ldap.INAPPROPRIATE_AUTH if cred != db_password: LOG.debug('bind fail: password for who=%s does not match', core.utf8_decode(who)) raise ldap.INVALID_CREDENTIALS
def add_s(self, dn, modlist): """Add an object with the specified attributes at dn.""" if server_fail: raise ldap.SERVER_DOWN id_attr_in_modlist = False id_attr = self._dn_to_id_attr(dn) id_value = self._dn_to_id_value(dn) # The LDAP API raises a TypeError if attr name is None. for k, dummy_v in modlist: if k is None: raise TypeError('must be string, not None. modlist=%s' % modlist) if k == id_attr: for val in dummy_v: if core.utf8_decode(val) == id_value: id_attr_in_modlist = True if not id_attr_in_modlist: LOG.debug('id_attribute=%(attr)s missing, attributes=%(attrs)s' % {'attr': id_attr, 'attrs': modlist}) raise ldap.NAMING_VIOLATION key = self.key(dn) LOG.debug('add item: dn=%(dn)s, attrs=%(attrs)s', { 'dn': core.utf8_decode(dn), 'attrs': modlist}) if key in self.db: LOG.debug('add item failed: dn=%s is already in store.', core.utf8_decode(dn)) raise ldap.ALREADY_EXISTS(dn) self.db[key] = {k: _internal_attr(k, v) for k, v in modlist} self.db.sync()
def normalize_dn(dn): # Capitalize the attribute names as an LDAP server might. dn = ldap.dn.str2dn(core.utf8_encode(dn)) norm = [] for part in dn: name, val, i = part[0] name = core.utf8_decode(name) name = name.upper() name = core.utf8_encode(name) norm.append([(name, val, i)]) return core.utf8_decode(ldap.dn.dn2str(norm))
def modify_s(self, dn, modlist): """Modify the object at dn using the attribute list. :param dn: an LDAP DN :param modlist: a list of tuples in the following form: ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value) """ if server_fail: raise ldap.SERVER_DOWN key = self.key(dn) LOG.debug('modify item: dn=%(dn)s attrs=%(attrs)s', { 'dn': core.utf8_decode(dn), 'attrs': modlist}) try: entry = self.db[key] except KeyError: LOG.debug('modify item failed: dn=%s not found.', core.utf8_decode(dn)) raise ldap.NO_SUCH_OBJECT for cmd, k, v in modlist: values = entry.setdefault(k, []) if cmd == ldap.MOD_ADD: v = _internal_attr(k, v) for x in v: if x in values: raise ldap.TYPE_OR_VALUE_EXISTS values += v elif cmd == ldap.MOD_REPLACE: values[:] = _internal_attr(k, v) elif cmd == ldap.MOD_DELETE: if v is None: if not values: LOG.debug('modify item failed: ' 'item has no attribute "%s" to delete', k) raise ldap.NO_SUCH_ATTRIBUTE values[:] = [] else: for val in _internal_attr(k, v): try: values.remove(val) except ValueError: LOG.debug('modify item failed: ' 'item has no attribute "%(k)s" with ' 'value "%(v)s" to delete', { 'k': k, 'v': val}) raise ldap.NO_SUCH_ATTRIBUTE else: LOG.debug('modify item failed: unknown command %s', cmd) raise NotImplementedError('modify_s action %s not' ' implemented' % cmd) self.db[key] = entry self.db.sync()
def delete_s(self, dn): """Remove the ldap object at specified dn.""" if server_fail: raise ldap.SERVER_DOWN key = self.key(dn) LOG.debug('delete item: dn=%s', core.utf8_decode(dn)) try: del self.db[key] except KeyError: LOG.debug('delete item failed: dn=%s not found.', core.utf8_decode(dn)) raise ldap.NO_SUCH_OBJECT self.db.sync()
def normalize_dn(dn): # Capitalize the attribute names as an LDAP server might. # NOTE(blk-u): Special case for this tested value, used with # test_user_id_comma. The call to str2dn here isn't always correct # here, because `dn` is escaped for an LDAP filter. str2dn() normally # works only because there's no special characters in `dn`. if dn == 'cn=Doe\\5c, John,ou=Users,cn=example,cn=com': return 'CN=Doe\\, John,OU=Users,CN=example,CN=com' dn = ldap.dn.str2dn(core.utf8_encode(dn)) norm = [] for part in dn: name, val, i = part[0] name = core.utf8_decode(name) name = name.upper() name = core.utf8_encode(name) norm.append([(name, val, i)]) return core.utf8_decode(ldap.dn.dn2str(norm))
def delete_ext_s(self, dn, serverctrls, clientctrls=None): """Remove the ldap object at specified dn.""" if server_fail: raise ldap.SERVER_DOWN try: if CONTROL_TREEDELETE in [c.controlType for c in serverctrls]: LOG.debug('FakeLdap subtree_delete item: dn=%s', core.utf8_decode(dn)) children = self._getChildren(dn) for c in children: del self.db[c] key = self.key(dn) LOG.debug('FakeLdap delete item: dn=%s', core.utf8_decode(dn)) del self.db[key] except KeyError: LOG.debug('delete item failed: dn=%s not found.', core.utf8_decode(dn)) raise ldap.NO_SUCH_OBJECT self.db.sync()
def add_s(self, dn, modlist): """Add an object with the specified attributes at dn.""" if server_fail: raise ldap.SERVER_DOWN # The LDAP API raises a TypeError if attr name is None. for k, dummy_v in modlist: if k is None: raise TypeError('must be string, not None. modlist=%s' % modlist) key = self.key(dn) LOG.debug('add item: dn=%(dn)s, attrs=%(attrs)s', { 'dn': core.utf8_decode(dn), 'attrs': modlist}) if key in self.db: LOG.debug('add item failed: dn=%s is already in store.', core.utf8_decode(dn)) raise ldap.ALREADY_EXISTS(dn) self.db[key] = dict([(k, _internal_attr(k, v)) for k, v in modlist]) self.db.sync()
def delete_ext_s(self, dn, serverctrls, clientctrls=None): """Remove the ldap object at specified dn.""" if server_fail: raise ldap.SERVER_DOWN try: if CONTROL_TREEDELETE in [c.controlType for c in serverctrls]: LOG.debug('FakeLdap subtree_delete item: dn=%s', core.utf8_decode(dn)) children = [k for k, v in six.iteritems(self.db) if re.match('%s.*,%s' % ( re.escape(self.__prefix), re.escape(self.dn(dn))), k)] for c in children: del self.db[c] key = self.key(dn) LOG.debug(_('FakeLdap delete item: dn=%s'), dn) del self.db[key] except KeyError: LOG.debug('delete item failed: dn=%s not found.', core.utf8_decode(dn)) raise ldap.NO_SUCH_OBJECT self.db.sync()
def delete_ext_s(self, dn, serverctrls, clientctrls=None): """Remove the ldap object at specified dn.""" if server_fail: raise ldap.SERVER_DOWN try: children = self._getChildren(dn) if children: raise ldap.NOT_ALLOWED_ON_NONLEAF except KeyError: LOG.debug('delete item failed: dn=%s not found.', core.utf8_decode(dn)) raise ldap.NO_SUCH_OBJECT super(FakeLdapNoSubtreeDelete, self).delete_ext_s(dn, serverctrls, clientctrls)
def _dn_to_id_value(self, dn): return core.utf8_decode(ldap.dn.str2dn(core.utf8_encode(dn))[0][0][1])
def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): """Search for all matching objects under base using the query. Args: base -- dn to search under scope -- search scope (base, subtree, onelevel) filterstr -- filter objects by attrlist -- attrs to return. Returns all attrs if not specified """ if server_fail: raise ldap.SERVER_DOWN if (not filterstr) and (scope != ldap.SCOPE_BASE): raise AssertionError('Search without filter on onelevel or ' 'subtree scope') if scope == ldap.SCOPE_BASE: try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_BASE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] elif scope == ldap.SCOPE_SUBTREE: # FIXME - LDAP search with SUBTREE scope must return the base # entry, but the code below does _not_. Unfortunately, there are # several tests that depend on this broken behavior, and fail # when the base entry is returned in the search results. The # fix is easy here, just initialize results as above for # the SCOPE_BASE case. # https://bugs.launchpad.net/keystone/+bug/1368772 try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_SUBTREE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] extraresults = [(k[len(self.__prefix):], v) for k, v in self.db.items() if re.match('%s.*,%s' % (re.escape(self.__prefix), re.escape(self.dn(base))), k)] results.extend(extraresults) elif scope == ldap.SCOPE_ONELEVEL: def get_entries(): base_dn = ldap.dn.str2dn(core.utf8_encode(base)) base_len = len(base_dn) for k, v in self.db.items(): if not k.startswith(self.__prefix): continue k_dn_str = k[len(self.__prefix):] k_dn = ldap.dn.str2dn(core.utf8_encode(k_dn_str)) if len(k_dn) != base_len + 1: continue if k_dn[-base_len:] != base_dn: continue yield (k_dn_str, v) results = list(get_entries()) else: # openldap client/server raises PROTOCOL_ERROR for unexpected scope raise ldap.PROTOCOL_ERROR objects = [] for dn, attrs in results: # filter the objects by filterstr id_attr, id_val, _ = ldap.dn.str2dn(core.utf8_encode(dn))[0][0] id_attr = core.utf8_decode(id_attr) id_val = core.utf8_decode(id_val) match_attrs = attrs.copy() match_attrs[id_attr] = [id_val] attrs_checked = set() if not filterstr or _match_query(filterstr, match_attrs, attrs_checked): if (filterstr and (scope != ldap.SCOPE_BASE) and ('objectclass' not in attrs_checked)): raise AssertionError('No objectClass in search filter') # filter the attributes by attrlist attrs = {k: v for k, v in attrs.items() if not attrlist or k in attrlist} objects.append((dn, attrs)) return objects
def normalize_value(value): return core.utf8_decode(value)
def dn(self, dn): return core.utf8_decode(dn)
def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): """Search for all matching objects under base using the query. Args: base -- dn to search under scope -- only SCOPE_BASE and SCOPE_SUBTREE are supported filterstr -- filter objects by attrlist -- attrs to return. Returns all attrs if not specified """ if server_fail: raise ldap.SERVER_DOWN if scope == ldap.SCOPE_BASE: try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_BASE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] elif scope == ldap.SCOPE_SUBTREE: # FIXME - LDAP search with SUBTREE scope must return the base # entry, but the code below does _not_. Unfortunately, there are # several tests that depend on this broken behavior, and fail # when the base entry is returned in the search results. The # fix is easy here, just initialize results as above for # the SCOPE_BASE case. # https://bugs.launchpad.net/keystone/+bug/1368772 results = [] try: item_dict = self.db[self.key(base)] results.extend([(base, item_dict)]) except KeyError: LOG.debug('search fail: dn not found for SCOPE_SUBTREE') extraresults = [(k[len(self.__prefix):], v) for k, v in six.iteritems(self.db) if re.match('%s.*,%s' % (re.escape(self.__prefix), re.escape(self.dn(base))), k)] results.extend(extraresults) elif scope == ldap.SCOPE_ONELEVEL: def get_entries(): base_dn = ldap.dn.str2dn(core.utf8_encode(base)) base_len = len(base_dn) for k, v in six.iteritems(self.db): if not k.startswith(self.__prefix): continue k_dn_str = k[len(self.__prefix):] k_dn = ldap.dn.str2dn(core.utf8_encode(k_dn_str)) if len(k_dn) != base_len + 1: continue if k_dn[-base_len:] != base_dn: continue yield (k_dn_str, v) results = list(get_entries()) else: LOG.debug('search fail: unknown scope %s', scope) raise NotImplementedError('Search scope %s not implemented.' % scope) objects = [] for dn, attrs in results: # filter the objects by filterstr id_attr, id_val, _ = ldap.dn.str2dn(core.utf8_encode(dn))[0][0] id_attr = core.utf8_decode(id_attr) id_val = core.utf8_decode(id_val) match_attrs = attrs.copy() match_attrs[id_attr] = [id_val] if not filterstr or _match_query(filterstr, match_attrs): # filter the attributes by attrlist attrs = dict([(k, v) for k, v in six.iteritems(attrs) if not attrlist or k in attrlist]) objects.append((dn, attrs)) return objects
def normalize_value(value): return core.utf8_decode(value)
def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): """Search for all matching objects under base using the query. Args: base -- dn to search under scope -- only SCOPE_BASE and SCOPE_SUBTREE are supported filterstr -- filter objects by attrlist -- attrs to return. Returns all attrs if not specified """ if server_fail: raise ldap.SERVER_DOWN if scope == ldap.SCOPE_BASE: try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_BASE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] elif scope == ldap.SCOPE_SUBTREE: results = [ (k[len(self.__prefix):], v) for k, v in six.iteritems(self.db) if re.match( '%s.*,%s' % (re.escape(self.__prefix), re.escape(self.dn(base))), k) ] elif scope == ldap.SCOPE_ONELEVEL: def get_entries(): base_dn = ldap.dn.str2dn(core.utf8_encode(base)) base_len = len(base_dn) for k, v in six.iteritems(self.db): if not k.startswith(self.__prefix): continue k_dn_str = k[len(self.__prefix):] k_dn = ldap.dn.str2dn(core.utf8_encode(k_dn_str)) if len(k_dn) != base_len + 1: continue if k_dn[-base_len:] != base_dn: continue yield (k_dn_str, v) results = list(get_entries()) else: LOG.debug('search fail: unknown scope %s', scope) raise NotImplementedError('Search scope %s not implemented.' % scope) objects = [] for dn, attrs in results: # filter the objects by filterstr id_attr, id_val, _ = ldap.dn.str2dn(core.utf8_encode(dn))[0][0] id_attr = core.utf8_decode(id_attr) id_val = core.utf8_decode(id_val) match_attrs = attrs.copy() match_attrs[id_attr] = [id_val] if not filterstr or _match_query(filterstr, match_attrs): # filter the attributes by attrlist attrs = dict([(k, v) for k, v in six.iteritems(attrs) if not attrlist or k in attrlist]) objects.append((dn, attrs)) return objects
def _dn_to_id_value(self, dn): return core.utf8_decode(ldap.dn.str2dn(core.utf8_encode(dn))[0][0][1])
def dn(self, dn): return core.utf8_decode(dn)
def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): """Search for all matching objects under base using the query. Args: base -- dn to search under scope -- only SCOPE_BASE and SCOPE_SUBTREE are supported filterstr -- filter objects by attrlist -- attrs to return. Returns all attrs if not specified """ if server_fail: raise ldap.SERVER_DOWN if scope == ldap.SCOPE_BASE: try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_BASE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] elif scope == ldap.SCOPE_SUBTREE: results = [(k[len(self.__prefix):], v) for k, v in six.iteritems(self.db) if re.match('%s.*,%s' % (re.escape(self.__prefix), re.escape(self.dn(base))), k)] elif scope == ldap.SCOPE_ONELEVEL: def get_entries(): base_dn = ldap.dn.str2dn(core.utf8_encode(base)) base_len = len(base_dn) for k, v in six.iteritems(self.db): if not k.startswith(self.__prefix): continue k_dn_str = k[len(self.__prefix):] k_dn = ldap.dn.str2dn(core.utf8_encode(k_dn_str)) if len(k_dn) != base_len + 1: continue if k_dn[-base_len:] != base_dn: continue yield (k_dn_str, v) results = list(get_entries()) else: LOG.debug('search fail: unknown scope %s', scope) raise NotImplementedError('Search scope %s not implemented.' % scope) objects = [] for dn, attrs in results: # filter the objects by filterstr id_attr, id_val, _ = ldap.dn.str2dn(core.utf8_encode(dn))[0][0] id_attr = core.utf8_decode(id_attr) id_val = core.utf8_decode(id_val) match_attrs = attrs.copy() match_attrs[id_attr] = [id_val] if not filterstr or _match_query(filterstr, match_attrs): # filter the attributes by attrlist attrs = dict([(k, v) for k, v in six.iteritems(attrs) if not attrlist or k in attrlist]) objects.append((dn, attrs)) return objects
def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): """Search for all matching objects under base using the query. Args: base -- dn to search under scope -- only SCOPE_BASE and SCOPE_SUBTREE are supported filterstr -- filter objects by attrlist -- attrs to return. Returns all attrs if not specified """ if server_fail: raise ldap.SERVER_DOWN if scope == ldap.SCOPE_BASE: try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_BASE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] elif scope == ldap.SCOPE_SUBTREE: # FIXME - LDAP search with SUBTREE scope must return the base # entry, but the code below does _not_. Unfortunately, there are # several tests that depend on this broken behavior, and fail # when the base entry is returned in the search results. The # fix is easy here, just initialize results as above for # the SCOPE_BASE case. # https://bugs.launchpad.net/keystone/+bug/1368772 results = [] try: item_dict = self.db[self.key(base)] results.extend([(base, item_dict)]) except KeyError: LOG.debug('search fail: dn not found for SCOPE_SUBTREE') extraresults = [(k[len(self.__prefix):], v) for k, v in six.iteritems(self.db) if re.match('%s.*,%s' % (re.escape(self.__prefix), re.escape(self.dn(base))), k)] results.extend(extraresults) elif scope == ldap.SCOPE_ONELEVEL: def get_entries(): base_dn = ldap.dn.str2dn(core.utf8_encode(base)) base_len = len(base_dn) for k, v in six.iteritems(self.db): if not k.startswith(self.__prefix): continue k_dn_str = k[len(self.__prefix):] k_dn = ldap.dn.str2dn(core.utf8_encode(k_dn_str)) if len(k_dn) != base_len + 1: continue if k_dn[-base_len:] != base_dn: continue yield (k_dn_str, v) results = list(get_entries()) else: LOG.debug('search fail: unknown scope %s', scope) raise NotImplementedError('Search scope %s not implemented.' % scope) objects = [] for dn, attrs in results: # filter the objects by filterstr id_attr, id_val, _ = ldap.dn.str2dn(core.utf8_encode(dn))[0][0] id_attr = core.utf8_decode(id_attr) id_val = core.utf8_decode(id_val) match_attrs = attrs.copy() match_attrs[id_attr] = [id_val] if not filterstr or _match_query(filterstr, match_attrs): # filter the attributes by attrlist attrs = dict([(k, v) for k, v in six.iteritems(attrs) if not attrlist or k in attrlist]) objects.append((dn, attrs)) return objects
def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): """Search for all matching objects under base using the query. Args: base -- dn to search under scope -- search scope (base, subtree, onelevel) filterstr -- filter objects by attrlist -- attrs to return. Returns all attrs if not specified """ if server_fail: raise ldap.SERVER_DOWN if (not filterstr) and (scope != ldap.SCOPE_BASE): raise AssertionError('Search without filter on onelevel or ' 'subtree scope') if scope == ldap.SCOPE_BASE: try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_BASE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] elif scope == ldap.SCOPE_SUBTREE: # FIXME - LDAP search with SUBTREE scope must return the base # entry, but the code below does _not_. Unfortunately, there are # several tests that depend on this broken behavior, and fail # when the base entry is returned in the search results. The # fix is easy here, just initialize results as above for # the SCOPE_BASE case. # https://bugs.launchpad.net/keystone/+bug/1368772 try: item_dict = self.db[self.key(base)] except KeyError: LOG.debug('search fail: dn not found for SCOPE_SUBTREE') raise ldap.NO_SUCH_OBJECT results = [(base, item_dict)] extraresults = [ (k[len(self.__prefix):], v) for k, v in self.db.items() if re.match( '%s.*,%s' % (re.escape(self.__prefix), re.escape(self.dn(base))), k) ] results.extend(extraresults) elif scope == ldap.SCOPE_ONELEVEL: def get_entries(): base_dn = ldap.dn.str2dn(core.utf8_encode(base)) base_len = len(base_dn) for k, v in self.db.items(): if not k.startswith(self.__prefix): continue k_dn_str = k[len(self.__prefix):] k_dn = ldap.dn.str2dn(core.utf8_encode(k_dn_str)) if len(k_dn) != base_len + 1: continue if k_dn[-base_len:] != base_dn: continue yield (k_dn_str, v) results = list(get_entries()) else: # openldap client/server raises PROTOCOL_ERROR for unexpected scope raise ldap.PROTOCOL_ERROR objects = [] for dn, attrs in results: # filter the objects by filterstr id_attr, id_val, _ = ldap.dn.str2dn(core.utf8_encode(dn))[0][0] id_attr = core.utf8_decode(id_attr) id_val = core.utf8_decode(id_val) match_attrs = attrs.copy() match_attrs[id_attr] = [id_val] attrs_checked = set() if not filterstr or _match_query(filterstr, match_attrs, attrs_checked): if (filterstr and (scope != ldap.SCOPE_BASE) and ('objectclass' not in attrs_checked)): raise AssertionError('No objectClass in search filter') # filter the attributes by attrlist attrs = { k: v for k, v in attrs.items() if not attrlist or k in attrlist } objects.append((dn, attrs)) return objects