From a5b7e3f56b8a3bddfafa1811061cd146f7c818e1 Mon Sep 17 00:00:00 2001 From: "rcritten@redhat.com" Date: Mon, 27 Aug 2007 13:45:28 -0400 Subject: Include any LDAP error strings in XML-RPC Fault exceptions Put a try/except around attempts to determine user uniqueness --- ipa-server/xmlrpc-server/funcs.py | 7 +++---- ipa-server/xmlrpc-server/ipaxmlrpc.py | 8 +++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index f388f3f4f..8994be915 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -192,11 +192,10 @@ class IPAServer: uid = self.__safe_filter(uid) filter = "(&(uid=%s)(objectclass=posixAccount))" % uid - entry = self.__get_entry(self.basedn, filter, ['dn','uid'], opts) - - if entry is not None: + try: + entry = self.__get_entry(self.basedn, filter, ['dn','uid'], opts) return 0 - else: + except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): return 1 def get_user_by_uid (self, uid, sattrs=None, opts=None): diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index 3f30f8fbd..32a7e8089 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -36,6 +36,7 @@ from mod_python import apache import ipaserver import funcs from ipa import ipaerror +import ldap import string import base64 @@ -150,7 +151,12 @@ class ModXMLRPCRequestHandler(object): response = dumps(response, methodresponse=1, allow_none=1) except ipaerror.IPAError, e: self.traceback = True - response = dumps(Fault(e.code, str(e))) + + if (isinstance(e.detail, ldap.LDAPError)): +u err = ": %s: %s" % (e.detail.args[0]['desc'], e.detail.args[0].get('info','')) + response = dumps(Fault(e.code, str(e) + err)) + else: + response = dumps(Fault(e.code, str(e))) except: self.traceback = True # report exception back to server -- cgit From f437ecfad16a8c735a99b70949545c56c5e430e1 Mon Sep 17 00:00:00 2001 From: Karl MacMillan Date: Tue, 28 Aug 2007 09:50:43 -0400 Subject: Fix minor error in previous patch. --- ipa-python/Makefile | 2 +- ipa-server/xmlrpc-server/ipaxmlrpc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ipa-python/Makefile b/ipa-python/Makefile index b2e4660f8..542c50820 100644 --- a/ipa-python/Makefile +++ b/ipa-python/Makefile @@ -1,4 +1,4 @@ -PYTHONLIBDIR ?= $(shell python -c "from distutils.sysconfig import *; print get_python_lib(1)") +PYTHONLIBDIR ?= $(shell python -c "from distutils.sysconfig import *; print get_python_lib()") PACKAGEDIR ?= $(DESTDIR)/$(PYTHONLIBDIR)/ipa CONFIGDIR ?= $(DESTDIR)/etc/ipa diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index 32a7e8089..9ef1e1b0e 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -153,7 +153,7 @@ class ModXMLRPCRequestHandler(object): self.traceback = True if (isinstance(e.detail, ldap.LDAPError)): -u err = ": %s: %s" % (e.detail.args[0]['desc'], e.detail.args[0].get('info','')) + err = ": %s: %s" % (e.detail.args[0]['desc'], e.detail.args[0].get('info','')) response = dumps(Fault(e.code, str(e) + err)) else: response = dumps(Fault(e.code, str(e))) -- cgit From e31b526c8174e7c55f69b1fdf31a6ee78197e8bc Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Mon, 27 Aug 2007 11:30:26 -0700 Subject: 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 --- ipa-server/ipa-gui/ipagui/controllers.py | 4 +- ipa-server/xmlrpc-server/funcs.py | 117 ++++++++++++++++++++++++------- 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 -- cgit From 6eea6664e079d187c3b0420b4283af35205d3b03 Mon Sep 17 00:00:00 2001 From: Karl MacMillan Date: Tue, 28 Aug 2007 09:58:10 -0400 Subject: This patch wraps binary data in an xmlrpclib Binary object. This removes the need for LDIF conversion. It will make TurboGears direct code faster, but should keep xmlrpc about the same speed. The patch also swaps out ldap.cidict for the IPA CIDict class. IPA code should only use the CIDict class now. --- ipa-python/entity.py | 24 ++++++------- ipa-python/ipaclient.py | 14 ++------ ipa-python/ipautil.py | 64 +++++++++++++++++++++++++++++++++++ ipa-python/rpcclient.py | 59 ++++++++++++++++++-------------- ipa-server/ipaserver/ipaldap.py | 10 +++--- ipa-server/xmlrpc-server/funcs.py | 39 ++++++--------------- ipa-server/xmlrpc-server/ipaxmlrpc.py | 6 ++-- 7 files changed, 131 insertions(+), 85 deletions(-) diff --git a/ipa-python/entity.py b/ipa-python/entity.py index 9153c4159..2f0629cda 100644 --- a/ipa-python/entity.py +++ b/ipa-python/entity.py @@ -3,6 +3,8 @@ import ldif import re import cStringIO +import ipa.ipautil + def utf8_encode_value(value): if isinstance(value,unicode): return value.encode('utf-8') @@ -22,8 +24,8 @@ class Entity: In python-ldap, entries are returned as a list of 2-tuples. Instance variables: dn - string - the string DN of the entry - data - cidict - case insensitive dict of the attributes and values - orig_data - cidict - case insentiive dict of the original attributes and values""" + data - CIDict - case insensitive dict of the attributes and values + orig_data - CIDict - case insentiive dict of the original attributes and values""" def __init__(self,entrydata=None): """data is the raw data returned from the python-ldap result method, @@ -32,19 +34,19 @@ class Entity: if entrydata: if isinstance(entrydata,tuple): self.dn = entrydata[0] - self.data = ldap.cidict.cidict(entrydata[1]) + self.data = ipa.ipautil.CIDict(entrydata[1]) elif isinstance(entrydata,str) or isinstance(entrydata,unicode): self.dn = entrydata - self.data = ldap.cidict.cidict() + self.data = ipa.ipautil.CIDict() elif isinstance(entrydata,dict): self.dn = entrydata['dn'] del entrydata['dn'] - self.data = ldap.cidict.cidict(entrydata) + self.data = ipa.ipautil.CIDict(entrydata) else: self.dn = '' - self.data = ldap.cidict.cidict() + self.data = ipa.ipautil.CIDict() - self.orig_data = dict(self.data) + self.orig_data = ipa.ipautil.CIDict(self.data) def __nonzero__(self): """This allows us to do tests like if entry: returns false if there is no data, @@ -112,9 +114,7 @@ class Entity: def toDict(self): """Convert the attrs and values to a dict. The dict is keyed on the attribute name. The value is either single value or a list of values.""" - result = {} - for k in self.data.keys(): - result[k] = self.data[k] + result = ipa.ipautil.CIDict(self.data) result['dn'] = self.dn return result @@ -124,9 +124,7 @@ class Entity: def origDataDict(self): """Returns a dict of the original values of the user. Used for updates.""" - result = {} - for k in self.orig_data.keys(): - result[k] = self.orig_data[k] + result = ipa.ipautil.CIDict(self.orig_data) result['dn'] = self.dn return result diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py index 28573acad..71def70fd 100644 --- a/ipa-python/ipaclient.py +++ b/ipa-python/ipaclient.py @@ -30,14 +30,6 @@ import group import ipa import config -def cidict_to_dict(cid): - """Convert a cidict to a standard dict for sending across the wire""" - newdict = {} - kindex = cid.keys() - for dkey in kindex: - newdict[dkey] = cid[dkey] - return newdict - class IPAClient: def __init__(self,local=None): @@ -89,7 +81,7 @@ class IPAClient: result = self.transport.get_all_users() all_users = [] - for (attrs) in result: + for attrs in result: if attrs is not None: all_users.append(user.User(attrs)) @@ -107,7 +99,7 @@ class IPAClient: result = self.transport.find_users(criteria, sattrs) users = [] - for (attrs) in result: + for attrs in result: if attrs is not None: users.append(user.User(attrs)) @@ -165,7 +157,7 @@ class IPAClient: result = self.transport.find_groups(criteria, sattrs) groups = [] - for (attrs) in result: + for attrs in result: if attrs is not None: groups.append(group.Group(attrs)) diff --git a/ipa-python/ipautil.py b/ipa-python/ipautil.py index 51337a8eb..74f7cfff6 100644 --- a/ipa-python/ipautil.py +++ b/ipa-python/ipautil.py @@ -18,6 +18,8 @@ # from string import lower +import re +import xmlrpclib class CIDict(dict): """ @@ -106,3 +108,65 @@ class CIDict(dict): return (key,value) +# +# The safe_string_re regexp and needs_base64 function are extracted from the +# python-ldap ldif module, which was +# written by Michael Stroeder +# http://python-ldap.sourceforge.net +# +# It was extracted because ipaldap.py is naughtily reaching into the ldif +# module and squashing this regexp. +# +SAFE_STRING_PATTERN = '(^(\000|\n|\r| |:|<)|[\000\n\r\200-\377]+|[ ]+$)' +safe_string_re = re.compile(SAFE_STRING_PATTERN) + +def needs_base64(s): + """ + returns 1 if s has to be base-64 encoded because of special chars + """ + return not safe_string_re.search(s) is None + + +def wrap_binary_data(data): + """Converts all binary data strings into Binary objects for transport + back over xmlrpc.""" + if isinstance(data, str): + if needs_base64(data): + return xmlrpclib.Binary(data) + else: + return data + elif isinstance(data, list) or isinstance(data,tuple): + retval = [] + for value in data: + retval.append(wrap_binary_data(value)) + return retval + elif isinstance(data, dict): + retval = {} + for (k,v) in data.iteritems(): + retval[k] = wrap_binary_data(v) + return retval + else: + return data + + +def unwrap_binary_data(data): + """Converts all Binary objects back into strings.""" + if isinstance(data, xmlrpclib.Binary): + # The data is decoded by the xmlproxy, but is stored + # in a binary object for us. + return str(data) + elif isinstance(data, str): + return data + elif isinstance(data, list) or isinstance(data,tuple): + retval = [] + for value in data: + retval.append(unwrap_binary_data(value)) + return retval + elif isinstance(data, dict): + retval = {} + for (k,v) in data.iteritems(): + retval[k] = unwrap_binary_data(v) + return retval + else: + return data + diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index 2301edde8..3e5bb113a 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -29,7 +29,7 @@ import os import base64 import user import ipa -from ipa import ipaerror +from ipa import ipaerror, ipautil # Some errors to catch # http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto @@ -83,7 +83,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def get_user_by_dn(self,dn,sattrs=None): """Get a specific user. If sattrs is not None then only those @@ -99,7 +99,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def add_user(self,user,user_container=None): """Add a new user. Takes as input a dict where the key is the @@ -111,13 +111,14 @@ class RPCClient: user_container = "__NONE__" try: - result = server.add_user(user, user_container) + result = server.add_user(ipautil.wrap_binary_data(user), + user_container) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def get_add_schema(self): """Get the list of attributes we need to ask when adding a new @@ -134,7 +135,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def get_all_users (self): """Return a list containing a User object for each existing user.""" @@ -147,7 +148,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def find_users (self, criteria, sattrs=None): """Return a list containing a User object for each user that matches @@ -164,20 +165,21 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def update_user(self,olduser,newuser): """Update an existing user. olduser and newuser are dicts of attributes""" server = self.setup_server() try: - result = server.update_user(olduser, newuser) + result = server.update_user(ipautil.wrap_binary_data(olduser), + ipautil.wrap_binary_data(newuser)) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def mark_user_deleted(self,uid): """Mark a user as deleted/inactive""" @@ -190,7 +192,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) # Group support @@ -208,7 +210,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def get_group_by_dn(self,dn,sattrs=None): """Get a specific group. If sattrs is not None then only those @@ -224,7 +226,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def add_group(self,group,group_container=None): """Add a new group. Takes as input a dict where the key is the @@ -236,7 +238,8 @@ class RPCClient: group_container = "__NONE__" try: - result = server.add_group(group, group_container) + result = server.add_group(ipautil.wrap_binary_data(group), + group_container) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): @@ -257,7 +260,7 @@ class RPCClient: except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def add_user_to_group(self, user, group): """Add a user to an existing group. @@ -266,13 +269,14 @@ class RPCClient: """ server = self.setup_server() try: - result = server.add_user_to_group(user, group) + result = server.add_user_to_group(ipautil.wrap_binary_data(user), + ipautil.wrap_binary_data(group)) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def add_users_to_group(self, users, group): """Add several users to an existing group. @@ -283,13 +287,14 @@ class RPCClient: """ server = self.setup_server() try: - result = server.add_users_to_group(users, group) + result = server.add_users_to_group(ipautil.wrap_binary_data(users), + ipautil.wrap_binary_data(group)) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def remove_user_from_group(self, user, group): """Remove a user from an existing group. @@ -298,13 +303,14 @@ class RPCClient: """ server = self.setup_server() try: - result = server.remove_user_from_group(user, group) + result = server.remove_user_from_group(ipautil.wrap_binary_data(user), + ipautil.wrap_binary_data(group)) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def remove_users_from_group(self, users, group): """Remove several users from an existing group. @@ -315,23 +321,26 @@ class RPCClient: """ server = self.setup_server() try: - result = server.remove_users_from_group(users, group) + result = server.remove_users_from_group( + ipautil.wrap_binary_data(users), + ipautil.wrap_binary_data(group)) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) def update_group(self,oldgroup,newgroup): """Update an existing group. oldgroup and newgroup are dicts of attributes""" server = self.setup_server() try: - result = server.update_group(oldgroup, newgroup) + result = server.update_group(ipautil.wrap_binary_data(oldgroup), + ipautil.wrap_binary_data(newgroup)) except xmlrpclib.Fault, fault: raise ipaerror.gen_exception(fault.faultCode, fault.faultString) except socket.error, (value, msg): raise xmlrpclib.Fault(value, msg) - return result + return ipautil.unwrap_binary_data(result) diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index 0b2a152e9..344e6dc3a 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -39,7 +39,7 @@ from ldap.modlist import modifyModlist from ldap.ldapobject import SimpleLDAPObject -from ipa import ipaerror +from ipa import ipaerror, ipautil class Entry: """This class represents an LDAP Entry object. An LDAP entry consists of a DN @@ -47,7 +47,7 @@ class Entry: values. In python-ldap, entries are returned as a list of 2-tuples. Instance variables: dn - string - the string DN of the entry - data - cidict - case insensitive dict of the attributes and values""" + data - CIDict - case insensitive dict of the attributes and values""" def __init__(self,entrydata): """data is the raw data returned from the python-ldap result method, which is @@ -56,13 +56,13 @@ class Entry: if entrydata: if isinstance(entrydata,tuple): self.dn = entrydata[0] - self.data = ldap.cidict.cidict(entrydata[1]) + self.data = ipautil.CIDict(entrydata[1]) elif isinstance(entrydata,str) or isinstance(entrydata,unicode): self.dn = entrydata - self.data = ldap.cidict.cidict() + self.data = ipautil.CIDict() else: self.dn = '' - self.data = ldap.cidict.cidict() + self.data = ipautil.CIDict() def __nonzero__(self): """This allows us to do tests like if entry: returns false if there is no data, diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index a00498333..fe48a1ffa 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -98,36 +98,19 @@ class IPAServer: return "dn:" + ent.dn def convert_entry(self, ent): - - # Convert to LDIF - entry = str(ent) + entry = dict(ent.data) + entry['dn'] = ent.dn + # For now convert single entry lists to a string for the ui. + # TODO: we need to deal with multi-values better + for key,value in entry.iteritems(): + if isinstance(value,list) or isinstance(value,tuple): + if len(value) == 0: + entry[key] = '' + elif len(value) == 1: + entry[key] = value[0] + return entry - # Strip off any junk - entry = entry.strip() - # Don't need to identify binary fields and this breaks the parser so - # remove double colons - entry = entry.replace('::', ':') - specs = [spec.split(':') for spec in entry.split('\n')] - - # Convert into a dict. We need to handle multi-valued attributes as well - # so we'll convert those into lists. - obj={} - for (k,v) in specs: - k = k.lower() - if obj.get(k) is not None: - if isinstance(obj[k],list): - obj[k].append(v.strip()) - else: - first = obj[k] - obj[k] = [] - obj[k].append(first) - obj[k].append(v.strip()) - else: - obj[k] = v.strip() - - return obj - def __get_entry (self, base, filter, sattrs=None, opts=None): """Get a specific entry. Return as a dict of values. Multi-valued fields are represented as lists. diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index 9ef1e1b0e..16ced2cda 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -35,7 +35,7 @@ from mod_python import apache import ipaserver import funcs -from ipa import ipaerror +from ipa import ipaerror, ipautil import ldap import string @@ -173,14 +173,14 @@ class ModXMLRPCRequestHandler(object): if func is None: raise Fault(1, "Invalid method: %s" % method) - args = list(params) + args = list(ipautil.unwrap_binary_data(params)) for i in range(len(args)): if args[i] == '__NONE__': args[i] = None ret = func(*args) - return ret + return ipautil.wrap_binary_data(ret) def multiCall(self, calls): """Execute a multicall. Execute each method call in the calls list, collecting -- cgit