Initial implementation of a generic search routine.

This commit is contained in:
Rob Crittenden
2008-10-15 16:12:27 -04:00
parent af7c0ee595
commit 3268b65ae0
3 changed files with 92 additions and 87 deletions
+71 -3
View File
@@ -60,9 +60,8 @@ class ldap(CrudBackend):
object_type = "posixGroup"
elif key_attribute == "krbprincipal": # Service
object_type = "krbPrincipal"
if not object_type:
return None
else:
return None
filter = "(&(%s=%s)(objectclass=%s))" % (
key_attribute,
@@ -76,6 +75,31 @@ class ldap(CrudBackend):
return entry['dn']
def generate_search_filters(self, **kw):
"""Generates a search filter based on a list of words and a list
of fields to search against.
Returns a tuple of two filters: (exact_match, partial_match)
"""
# construct search pattern for a single word
# (|(f1=word)(f2=word)...)
exact_pattern = "(|"
for field in kw.keys():
exact_pattern += "(%s=%s)" % (field, kw[field])
exact_pattern += ")"
sub_pattern = "(|"
for field in kw.keys():
sub_pattern += "(%s=*%s*)" % (field, kw[field])
sub_pattern += ")"
# construct the giant match for all words
exact_match_filter = "(&" + exact_pattern + ")"
partial_match_filter = "(|" + sub_pattern + ")"
return (exact_match_filter, partial_match_filter)
def create(self, **kw):
if servercore.entry_exists(kw['dn']):
raise errors.DuplicateEntry("entry already exists")
@@ -107,6 +131,50 @@ class ldap(CrudBackend):
def delete(self, dn):
return servercore.delete_entry(dn)
def search(self, **kw):
objectclass = kw.get('objectclass')
if objectclass:
del kw['objectclass']
(exact_match_filter, partial_match_filter) = self.generate_search_filters(**kw)
if objectclass:
exact_match_filter = "(&(objectClass=%s)%s)" % (objectclass, exact_match_filter)
partial_match_filter = "(&(objectClass=%s)%s)" % (objectclass, partial_match_filter)
search_base = "%s, %s" % (self.api.env.container_accounts, self.api.env.basedn)
try:
exact_results = servercore.search(search_base,
exact_match_filter, ["*"])
except errors.NotFound:
exact_results = [0]
try:
partial_results = servercore.search(search_base,
partial_match_filter, ["*"])
except errors.NotFound:
partial_results = [0]
exact_counter = exact_results[0]
partial_counter = partial_results[0]
exact_results = exact_results[1:]
partial_results = partial_results[1:]
# Remove exact matches from the partial_match list
exact_dns = set(map(lambda e: e.get('dn'), exact_results))
partial_results = filter(lambda e: e.get('dn') not in exact_dns,
partial_results)
if (exact_counter == -1) or (partial_counter == -1):
counter = -1
else:
counter = len(exact_results) + len(partial_results)
results = [counter]
for r in exact_results + partial_results:
results.append(r)
return results
api.register(ldap)
def delete(self, dn):
+17 -79
View File
@@ -230,85 +230,6 @@ def uid_too_long(uid):
return False
def find_users (criteria, sattrs, sizelimit=-1, timelimit=-1):
"""Returns a list: counter followed by the results.
If the results are truncated, counter will be set to -1."""
"""
if not isinstance(criteria,basestring) or len(criteria) == 0:
raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
if sattrs is not None and not isinstance(sattrs, list):
raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
if not isinstance(sizelimit,int):
raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
if not isinstance(timelimit,int):
raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
"""
# logging.info("IPA: find_users '%s'" % criteria)
config = get_ipa_config()
if timelimit < 0:
timelimit = float(config.get('ipasearchtimelimit'))
if sizelimit < 0:
sizelimit = int(config.get('ipasearchrecordslimit'))
# Assume the list of fields to search will come from a central
# configuration repository. A good format for that would be
# a comma-separated list of fields
search_fields_conf_str = config.get('ipausersearchfields')
search_fields = string.split(search_fields_conf_str, ",")
# criteria = self.__safe_filter(criteria)
criteria_words = re.split(r'\s+', criteria)
criteria_words = filter(lambda value:value!="", criteria_words)
if len(criteria_words) == 0:
return [0]
(exact_match_filter, partial_match_filter) = generate_match_filters(
search_fields, criteria_words)
#
# further constrain search to just the objectClass
# TODO - need to parameterize this into generate_match_filters,
# and work it into the field-specification search feature
#
exact_match_filter = "(&(objectClass=person)%s)" % exact_match_filter
partial_match_filter = "(&(objectClass=person)%s)" % partial_match_filter
try:
exact_results = context.conn.getConn().getListAsync("cn=accounts," + basedn, ldap.SCOPE_SUBTREE, exact_match_filter, sattrs, 0, None, None, timelimit, sizelimit)
# except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
except Exception:
exact_results = [0]
try:
partial_results = context.conn.getConn().getListAsync("cn=accounts," + basedn, ldap.SCOPE_SUBTREE, partial_match_filter, sattrs, 0, None, None, timelimit, sizelimit)
# except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
except Exception:
partial_results = [0]
exact_counter = exact_results[0]
partial_counter = partial_results[0]
exact_results = exact_results[1:]
partial_results = partial_results[1:]
# Remove exact matches from the partial_match list
exact_dns = set(map(lambda e: e.dn, exact_results))
partial_results = filter(lambda e: e.dn not in exact_dns,
partial_results)
if (exact_counter == -1) or (partial_counter == -1):
counter = -1
else:
counter = len(exact_results) + len(partial_results)
users = [counter]
for u in exact_results + partial_results:
users.append(convert_entry(u))
return users
def update_entry (entry):
"""Update an LDAP entry
@@ -341,6 +262,23 @@ def delete_entry(dn):
"""Remove an entry"""
return context.conn.getConn().deleteEntry(dn)
# FIXME, get time and search limit from cn=ipaconfig
def search(base, filter, attributes, timelimit=1, sizelimit=3000):
"""Perform an LDAP query"""
try:
timelimit = float(timelimit)
results = context.conn.getConn().getListAsync(base, ldap.SCOPE_SUBTREE,
filter, attributes, 0, None, None, timelimit, sizelimit)
except ldap.NO_SUCH_OBJECT:
raise errors.NotFound
counter = results[0]
entries = [counter]
for r in results[1:]:
entries.append(convert_entry(r))
return entries
def uniq_list(x):
"""Return a unique list, preserving order and ignoring case"""
myset = {}
+4 -5
View File
@@ -237,11 +237,10 @@ api.register(user_mod)
class user_find(crud.Find):
'Search the users.'
def execute(self, *args, **kw):
uid=args[0]
result = servercore.find_users(uid, ["*"])
return result
def forward(self, *args, **kw):
users = super(crud.Find, self).forward(*args, **kw)
ldap = self.api.Backend.ldap
kw['uid'] = args[0]
return ldap.search(**kw)
def output_for_cli(self, users):
if not users:
return
counter = users[0]