summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrcritten@redhat.com <rcritten@redhat.com>2007-08-13 16:41:38 -0400
committerrcritten@redhat.com <rcritten@redhat.com>2007-08-13 16:41:38 -0400
commitcfaa28150b6f0599b61acbb9b033327de9fd45ee (patch)
tree90af9a3a27037a5be8fc940b88a42b0e428a1ebc
parent794ef65abc79e99d596cacc195dfd2a5956a9a37 (diff)
downloadfreeipa-cfaa28150b6f0599b61acbb9b033327de9fd45ee.tar.gz
freeipa-cfaa28150b6f0599b61acbb9b033327de9fd45ee.tar.xz
freeipa-cfaa28150b6f0599b61acbb9b033327de9fd45ee.zip
Basic LDAP connection pooling
Implement user search
-rw-r--r--ipa-admintools/ipa-finduser25
-rw-r--r--ipa-python/ipaclient.py11
-rw-r--r--ipa-python/rpcclient.py17
-rw-r--r--ipa-server/ipaserver/ipaldap.py51
-rw-r--r--ipa-server/xmlrpc-server/funcs.py109
-rw-r--r--ipa-server/xmlrpc-server/ipaxmlrpc.py1
6 files changed, 178 insertions, 36 deletions
diff --git a/ipa-admintools/ipa-finduser b/ipa-admintools/ipa-finduser
index a54e141e7..b16c19d99 100644
--- a/ipa-admintools/ipa-finduser
+++ b/ipa-admintools/ipa-finduser
@@ -48,19 +48,22 @@ def main():
try:
client = ipaclient.IPAClient()
- ent = client.get_user(args[1])
- attr = ent.attrList()
+ users = client.find_users(args[1], sattrs=['dn','uid','cn','homeDirectory'])
+ for ent in users:
+ attr = ent.attrList()
- print "dn: " + ent.dn
+ print "dn: " + ent.dn
- for a in attr:
- value = ent.getValues(a)
- if isinstance(value,str):
- print a + ": " + value
- else:
- print a + ": "
- for l in value:
- print "\t" + l
+ for a in attr:
+ value = ent.getValues(a)
+ if isinstance(value,str):
+ print a + ": " + value
+ else:
+ print a + ": "
+ for l in value:
+ print "\t" + l
+ # blank line between results
+ print
except xmlrpclib.Fault, fault:
print fault.faultString
diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py
index c75b5bc96..2c24e77d0 100644
--- a/ipa-python/ipaclient.py
+++ b/ipa-python/ipaclient.py
@@ -85,3 +85,14 @@ class IPAClient:
def get_add_schema(self):
result = self.transport.get_add_schema()
return result
+
+ def find_users(self, criteria, sattrs=None):
+ result = self.transport.find_users(criteria, sattrs)
+
+ users = []
+ for (attrs) in result:
+ if attrs is not None:
+ users.append(user.User(attrs))
+
+ return users
+
diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py
index d4e645e1e..243b5485f 100644
--- a/ipa-python/rpcclient.py
+++ b/ipa-python/rpcclient.py
@@ -118,3 +118,20 @@ class RPCClient:
raise xmlrpclib.Fault(value, msg)
return result
+
+ def find_users (self, criteria, sattrs=None):
+ """Return a list containing a User object for each user that matches
+ the criteria."""
+
+ server = self.setup_server()
+ try:
+ if sattrs is not None:
+ result = server.find_users(criteria, sattrs)
+ else:
+ result = server.find_users(criteria)
+ except xmlrpclib.Fault, fault:
+ raise xmlrpclib.Fault(fault.faultCode, fault.faultString)
+ except socket.error, (value, msg):
+ raise xmlrpclib.Fault(value, msg)
+
+ return result
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