[Freeipa-devel] [PATCH] enhanced user search
Kevin McCarthy
kmccarth at redhat.com
Thu Aug 23 20:17:13 UTC 2007
Enhanced user search:
- "configurable" fields to search on (this is hardcoded but can be
moved to the external configuration easily)
- tokenize search words
- prioritize exact matches over partial matches
Other updates:
- use finally block to return ldap connections
- update web gui to use new get_user methods
-Kevin
-------------- next part --------------
# HG changeset patch
# User Kevin McCarthy <kmccarth at redhat.com>
# Date 1187900189 25200
# Node ID 4ef808c8f4f0536c1aed310cf604bb9fdd8fb662
# Parent e311a6e3555583aff5eb10d390015ce0308d334c
Enhanced user search:
- "configurable" fields to search on
- tokenize search words
- prioritize exact matches over partial matches
Other updates:
- use finally block to return ldap connections
- update web gui to use new get_user methods
diff -r e311a6e35555 -r 4ef808c8f4f0 ipa-server/ipa-gui/ipagui/controllers.py
--- a/ipa-server/ipa-gui/ipagui/controllers.py Thu Aug 23 11:57:25 2007 -0400
+++ b/ipa-server/ipa-gui/ipagui/controllers.py Thu Aug 23 13:16:29 2007 -0700
@@ -92,7 +92,7 @@ class Root(controllers.RootController):
if tg_errors:
turbogears.flash("There was a problem with the form!")
- user = client.get_user(uid)
+ user = client.get_user_by_uid(uid)
user_dict = user.toDict()
# store a copy of the original user for the update later
user_data = b64encode(dumps(user_dict))
@@ -155,7 +155,7 @@ class Root(controllers.RootController):
def usershow(self, uid):
"""Retrieve a single user for display"""
try:
- user = client.get_user(uid)
+ user = client.get_user_by_uid(uid)
return dict(user=user.toDict(), fields=forms.user.UserFields())
except ipaerror.IPAError, e:
turbogears.flash("User show failed: " + str(e))
diff -r e311a6e35555 -r 4ef808c8f4f0 ipa-server/xmlrpc-server/funcs.py
--- a/ipa-server/xmlrpc-server/funcs.py Thu Aug 23 11:57:25 2007 -0400
+++ b/ipa-server/xmlrpc-server/funcs.py Thu Aug 23 13:16:29 2007 -0700
@@ -89,8 +89,10 @@ class IPAServer:
filter = "(krbPrincipalName=" + princ + ")"
# The only anonymous search we should have
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,None)
- ent = m1.getEntry(self.basedn, self.scope, filter, ['dn'])
- _LDAPPool.releaseConn(m1)
+ try:
+ ent = m1.getEntry(self.basedn, self.scope, filter, ['dn'])
+ finally:
+ _LDAPPool.releaseConn(m1)
return "dn:" + ent.dn
@@ -138,8 +140,10 @@ class IPAServer:
dn = self.get_dn_from_principal(self.princ)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
- ent = m1.getEntry(base, self.scope, filter, sattrs)
- _LDAPPool.releaseConn(m1)
+ try:
+ ent = m1.getEntry(base, self.scope, filter, sattrs)
+ finally:
+ _LDAPPool.releaseConn(m1)
return self.convert_entry(ent)
@@ -223,8 +227,10 @@ class IPAServer:
dn = self.get_dn_from_principal(self.princ)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
- res = m1.addEntry(entry)
- _LDAPPool.releaseConn(m1)
+ try:
+ res = m1.addEntry(entry)
+ finally:
+ _LDAPPool.releaseConn(m1)
return res
def get_add_schema (self):
@@ -286,8 +292,10 @@ class IPAServer:
filter = "(objectclass=posixAccount)"
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
- all_users = m1.getList(self.basedn, self.scope, filter, None)
- _LDAPPool.releaseConn(m1)
+ try:
+ all_users = m1.getList(self.basedn, self.scope, filter, None)
+ finally:
+ _LDAPPool.releaseConn(m1)
users = []
for u in all_users:
@@ -314,23 +322,63 @@ class IPAServer:
# where the second byte in a multi-byte character
# is (illegally) ')' and make sure python-ldap
# bombs out.
- criteria = re.sub(r'[\(\)\\]', ldap_search_escape, criteria)
-
- # FIXME: Is this the filter we want or do we want to do searches of
- # cn as well? Or should the caller pass in the filter?
- filter = "(|(uid=%s)(cn=%s))" % (criteria, criteria)
+ criteria = re.sub(r'[\(\)\\\*]', ldap_search_escape, criteria)
+ if len(criteria) == 0:
+ return []
+
+ # 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 = "uid,givenName,sn,telephoneNumber"
+ search_fields = string.split(search_fields_conf_str, ",")
+
+ # construct search pattern for a single word
+ # (|(f1=word)(f2=word)...)
+ search_pattern = "(|"
+ for field in search_fields:
+ search_pattern += "(" + field + "=%(match)s)"
+ search_pattern += ")"
+ gen_search_pattern = lambda word: search_pattern % {'match':word}
+
+ criteria_words = re.split(r'\s+', criteria)
+ criteria_words = filter(lambda value:value!="", criteria_words)
+
+ # construct the giant match for all words
+ exact_match_filter = "(&"
+ partial_match_filter = "(&"
+ for word in criteria_words:
+ exact_match_filter += gen_search_pattern(word)
+ partial_match_filter += gen_search_pattern("*%s*" % word)
+ exact_match_filter += ")"
+ partial_match_filter += ")"
+
+
basedn = user_container + "," + self.basedn
- try:
- m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
- results = m1.getList(basedn, self.scope, filter, sattrs)
- _LDAPPool.releaseConn(m1)
- except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
- results = []
+ m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
+ try:
+ try:
+ exact_results = m1.getList(basedn, self.scope,
+ exact_match_filter, sattrs)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ exact_results = []
+
+ try:
+ partial_results = m1.getList(basedn, self.scope,
+ partial_match_filter, sattrs)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ partial_results = []
+ finally:
+ _LDAPPool.releaseConn(m1)
+
+ # 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)
users = []
- for u in results:
+ for u in exact_results + partial_results:
users.append(self.convert_entry(u))
-
+
return users
def convert_scalar_values(self, orig_dict):
@@ -365,8 +413,10 @@ class IPAServer:
proxydn = self.get_dn_from_principal(self.princ)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,proxydn)
- res = m1.updateEntry(moddn, olduser, newuser)
- _LDAPPool.releaseConn(m1)
+ try:
+ res = m1.updateEntry(moddn, olduser, newuser)
+ finally:
+ _LDAPPool.releaseConn(m1)
return res
def mark_user_deleted (self, uid, opts=None):
@@ -390,8 +440,10 @@ class IPAServer:
has_key = False
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,proxydn)
- res = m1.inactivateEntry(user['dn'], has_key)
- _LDAPPool.releaseConn(m1)
+ try:
+ res = m1.inactivateEntry(user['dn'], has_key)
+ finally:
+ _LDAPPool.releaseConn(m1)
return res
@@ -408,5 +460,8 @@ def ldap_search_escape(match):
return "\\29"
elif value == "\\":
return "\\5c"
+ elif value == "*":
+ # drop '*' from input. search performs its own wildcarding
+ return ""
else:
return value
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 2228 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/freeipa-devel/attachments/20070823/c8ad1fbc/attachment.bin>
More information about the Freeipa-devel
mailing list