mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-08-13 06:25:00 -05:00
Initial implementation of a generic search routine.
This commit is contained in:
@@ -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
@@ -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 = {}
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user