diff options
author | rcritten@redhat.com <rcritten@redhat.com> | 2007-08-13 16:41:38 -0400 |
---|---|---|
committer | rcritten@redhat.com <rcritten@redhat.com> | 2007-08-13 16:41:38 -0400 |
commit | cfaa28150b6f0599b61acbb9b033327de9fd45ee (patch) | |
tree | 90af9a3a27037a5be8fc940b88a42b0e428a1ebc /ipa-server | |
parent | 794ef65abc79e99d596cacc195dfd2a5956a9a37 (diff) | |
download | freeipa-cfaa28150b6f0599b61acbb9b033327de9fd45ee.tar.gz freeipa-cfaa28150b6f0599b61acbb9b033327de9fd45ee.tar.xz freeipa-cfaa28150b6f0599b61acbb9b033327de9fd45ee.zip |
Basic LDAP connection pooling
Implement user search
Diffstat (limited to 'ipa-server')
-rw-r--r-- | ipa-server/ipaserver/ipaldap.py | 51 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/funcs.py | 109 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/ipaxmlrpc.py | 1 |
3 files changed, 136 insertions, 25 deletions
diff --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index ee0388cab..7268594a9 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -228,15 +228,14 @@ class IPAdmin(SimpleLDAPObject): def __str__(self): return self.host + ":" + str(self.port) - def toLDAPURL(self): - return "ldap://%s:%d/" % (self.host,self.port) + def __get_server_controls__(self): + """Create the proxy user server control. The control has the form + 0x04 = Octet String + 4|0x80 sets the length of the string length field at 4 bytes + the struct() gets us the length in bytes of string self.proxydn + self.proxydn is the proxy dn to send""" - def getEntry(self,*args): - """This wraps the search function. It is common to just get one entry""" - # 0x04 = Octet String - # 4|0x80 sets the length of the length at 4 bytes - # the struct() gets us the length in bytes of string s - # s is the proxy dn to send + import sys if self.proxydn is not None: proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn; @@ -247,7 +246,24 @@ class IPAdmin(SimpleLDAPObject): else: sctrl=None - res = self.search_ext(args[0], args[1], filterstr=args[2], serverctrls=sctrl) + return sctrl + + def toLDAPURL(self): + return "ldap://%s:%d/" % (self.host,self.port) + + def set_proxydn(self, proxydn): + self.proxydn = proxydn + + def getEntry(self,*args): + """This wraps the search function. It is common to just get one entry""" + sctrl = self.__get_server_controls__() + + if sctrl is not None: + self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) + + res = self.search(*args) + +# res = self.search_ext(args[0], args[1], filterstr=args[2], attrlist=args[3], serverctrls=sctrl) type, obj = self.result(res) if not obj: @@ -260,6 +276,11 @@ class IPAdmin(SimpleLDAPObject): def getList(self,*args): """This wraps the search function to find all users.""" + sctrl = self.__get_server_controls__() + + if sctrl is not None: + self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) + res = self.search(*args) type, obj = self.result(res) if not obj: @@ -274,18 +295,8 @@ class IPAdmin(SimpleLDAPObject): def addEntry(self,*args): """This wraps the add function. It assumes that the entry is already populated with all of the desired objectclasses and attributes""" - if self.proxydn is not None: - proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn; - - # Create the proxy control - sctrl=[] - sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn)) - else: - sctrl=None - # Create the proxy control - sctrl=[] - sctrl.append(LDAPControl('2.16.840.1.113730.3.4.18',True,proxydn)) + sctrl = self.__get_server_controls__() try: self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index 01f5b0307..daad1a6b8 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -28,10 +28,37 @@ import string from types import * import xmlrpclib import ipa.config +import os + +# Need a global to store this between requests +_LDAPPool = None + +# +# Apache runs in multi-process mode so each process will have its own +# connection. This could theoretically drive the total number of connections +# very high but since this represents just the administrative interface +# this is not anticipated. +class IPAConnPool: + def __init__(self): + self.numentries = 0 + self.freelist = [] + + def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None): + self.numentries = self.numentries + 1 + if len(self.freelist) > 0: + conn = self.freelist.pop() + else: + conn = ipaserver.ipaldap.IPAdmin(host,port,bindca,bindcert,bindkey) + conn.set_proxydn(proxydn) + return conn + + def releaseConn(self, conn): + self.freelist.append(conn) class IPAServer: def __init__(self): + global _LDAPPool # FIXME, this needs to be auto-discovered self.host = 'localhost' self.port = 636 @@ -39,6 +66,8 @@ class IPAServer: self.bindkey = "/usr/share/ipa/key.pem" self.bindca = "/usr/share/ipa/cacert.asc" + if _LDAPPool is None: + _LDAPPool = IPAConnPool() ipa.config.init_config() self.basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm()) self.scope = ldap.SCOPE_SUBTREE @@ -49,10 +78,15 @@ class IPAServer: def get_dn_from_principal(self, princ): """Given a kerberls principal get the LDAP uid""" + global _LDAPPool + + # FIXME: should we search for this in a specific area of the tree? filter = "(krbPrincipalName=" + princ + ")" try: - m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey) - ent = m1.getEntry(self.basedn, self.scope, filter, None) + # 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) except ldap.LDAPError, e: raise xmlrpclib.Fault(1, e) except ipaserver.ipaldap.NoSuchEntryError: @@ -95,6 +129,7 @@ class IPAServer: """Get a specific user's entry. Return as a dict of values. Multi-valued fields are represented as lists. """ + global _LDAPPool ent="" if opts: self.set_principal(opts['remoteuser']) @@ -110,8 +145,9 @@ class IPAServer: filter = "(uid=" + username + ")" try: - m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) + m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) ent = m1.getEntry(self.basedn, self.scope, filter, None) + _LDAPPool.releaseConn(m1) except ldap.LDAPError, e: raise xmlrpclib.Fault(1, e) except ipaserver.ipaldap.NoSuchEntryError: @@ -121,6 +157,7 @@ class IPAServer: def add_user (self, user, user_container="ou=users,ou=default",opts=None): """Add a user in LDAP""" + global _LDAPPool if (isinstance(user, tuple)): user = user[0] dn="uid=%s,%s,%s" % (user['uid'], user_container,self.basedn) @@ -154,8 +191,9 @@ class IPAServer: raise xmlrpclib.Fault(2, "No such user") try: - m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) + m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) res = m1.addEntry(entry) + _LDAPPool.releaseConn(m1) return res except ldap.ALREADY_EXISTS: raise xmlrpclib.Fault(3, "User already exists") @@ -210,12 +248,24 @@ class IPAServer: """Return a list containing a User object for each existing user. """ + global _LDAPPool + + if opts: + self.set_principal(opts['remoteuser']) + + try: + dn = self.get_dn_from_principal(self.princ) + except ldap.LDAPError, e: + raise xmlrpclib.Fault(1, e) + except ipaserver.ipaldap.NoSuchEntryError: + raise xmlrpclib.Fault(2, "No such user") # FIXME: Is this the filter we want or should it be more specific? filter = "(objectclass=posixAccount)" try: - m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey) + 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) except ldap.LDAPError, e: raise xmlrpclib.Fault(1, e) except ipaserver.ipaldap.NoSuchEntryError: @@ -226,3 +276,52 @@ class IPAServer: users.append(self.convert_entry(u)) return users + + def find_users (self, args, sattrs=None, opts=None): + """Return a list containing a User object for each + existing user that matches the criteria. + """ + global _LDAPPool + + # The XML-RPC server marshals the arguments into one variable + # while the direct caller has them separate. So do a little + # bit of gymnastics to figure things out. There has to be a + # better way, so FIXME + if isinstance(args,tuple): + opts = sattrs + if len(args) == 2: + criteria = args[0] + sattrs = args[1] + else: + criteria = args + sattrs = None + else: + criteria = args + + if opts: + self.set_principal(opts['remoteuser']) + + try: + dn = self.get_dn_from_principal(self.princ) + except ldap.LDAPError, e: + raise xmlrpclib.Fault(1, e) + except ipaserver.ipaldap.NoSuchEntryError: + raise xmlrpclib.Fault(2, "No such user") + + # 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)" % criteria + 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 ldap.LDAPError, e: + raise xmlrpclib.Fault(1, e) + except ipaserver.ipaldap.NoSuchEntryError: + raise xmlrpclib.Fault(2, "No such user") + + users = [] + for u in results: + users.append(self.convert_entry(u)) + + return users diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index 7bad9ab5e..963117f0e 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -278,6 +278,7 @@ def handler(req, profiling=False): h.register_function(f.add_user) h.register_function(f.get_add_schema) h.register_function(f.get_all_users) + h.register_function(f.find_users) h.handle_request(req) finally: pass |