summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin McCarthy <kmccarth@redhat.com>2007-08-27 11:30:26 -0700
committerKevin McCarthy <kmccarth@redhat.com>2007-08-27 11:30:26 -0700
commite31b526c8174e7c55f69b1fdf31a6ee78197e8bc (patch)
tree94bddae4f80c5e5efaee0fb5ec1bd6d8df802e8d
parentf437ecfad16a8c735a99b70949545c56c5e430e1 (diff)
downloadfreeipa-e31b526c8174e7c55f69b1fdf31a6ee78197e8bc.tar.gz
freeipa-e31b526c8174e7c55f69b1fdf31a6ee78197e8bc.tar.xz
freeipa-e31b526c8174e7c55f69b1fdf31a6ee78197e8bc.zip
Enhanced user search:
- "configurable" fields to search on - tokenize search words - prioritize exact matches over partial matches - split match filter generation into a re-usable function. Other updates: - use finally block to return ldap connections - update web gui to use new get_user methods
-rw-r--r--ipa-server/ipa-gui/ipagui/controllers.py4
-rw-r--r--ipa-server/xmlrpc-server/funcs.py117
2 files changed, 95 insertions, 26 deletions
diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py
index eb89e5a6c..7dff9c908 100644
--- a/ipa-server/ipa-gui/ipagui/controllers.py
+++ b/ipa-server/ipa-gui/ipagui/controllers.py
@@ -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 --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py
index 8994be915..a00498333 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -90,8 +90,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
@@ -139,8 +141,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)
@@ -169,8 +173,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, oldentry, newentry)
- _LDAPPool.releaseConn(m1)
+ try:
+ res = m1.updateEntry(moddn, oldentry, newentry)
+ finally:
+ _LDAPPool.releaseConn(m1)
return res
def __safe_filter(self, criteria):
@@ -181,9 +187,34 @@ 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)
+ criteria = re.sub(r'[\(\)\\\*]', ldap_search_escape, criteria)
return criteria
+
+ def __generate_match_filters(self, search_fields, criteria_words):
+ """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)...)
+ search_pattern = "(|"
+ for field in search_fields:
+ search_pattern += "(" + field + "=%(match)s)"
+ search_pattern += ")"
+ gen_search_pattern = lambda word: search_pattern % {'match':word}
+
+ # 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 += ")"
+
+ return (exact_match_filter, partial_match_filter)
# User support
@@ -282,8 +313,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):
@@ -344,8 +377,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:
@@ -364,20 +399,46 @@ class IPAServer:
dn = self.get_dn_from_principal(self.princ)
+ # 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, ",")
+
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 []
- filter = "(|(uid=%s)(cn=%s))" % (criteria, criteria)
+ (exact_match_filter, partial_match_filter) = self.__generate_match_filters(
+ search_fields, criteria_words)
+
+ m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
try:
- m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
- results = m1.getList(self.basedn, self.scope, filter, sattrs)
+ try:
+ exact_results = m1.getList(self.basedn, self.scope,
+ exact_match_filter, sattrs)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ exact_results = []
+
+ try:
+ partial_results = m1.getList(self.basedn, self.scope,
+ partial_match_filter, sattrs)
+ except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
+ partial_results = []
+ finally:
_LDAPPool.releaseConn(m1)
- except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
- results = []
+
+ # 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):
@@ -416,8 +477,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
# Group support
@@ -484,8 +547,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)
def find_groups (self, criteria, sattrs=None, opts=None):
"""Return a list containing a User object for each
@@ -501,12 +566,13 @@ class IPAServer:
criteria = self.__safe_filter(criteria)
filter = "(&(cn=%s)(objectClass=posixGroup))" % criteria
+ m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
try:
- m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
results = m1.getList(self.basedn, self.scope, filter, sattrs)
- _LDAPPool.releaseConn(m1)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
results = []
+ finally:
+ _LDAPPool.releaseConn(m1)
groups = []
for u in results:
@@ -645,5 +711,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