mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Basic LDAP connection pooling
Implement user search
This commit is contained in:
parent
794ef65abc
commit
cfaa28150b
@ -48,19 +48,22 @@ def main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
client = ipaclient.IPAClient()
|
client = ipaclient.IPAClient()
|
||||||
ent = client.get_user(args[1])
|
users = client.find_users(args[1], sattrs=['dn','uid','cn','homeDirectory'])
|
||||||
attr = ent.attrList()
|
for ent in users:
|
||||||
|
attr = ent.attrList()
|
||||||
|
|
||||||
print "dn: " + ent.dn
|
print "dn: " + ent.dn
|
||||||
|
|
||||||
for a in attr:
|
for a in attr:
|
||||||
value = ent.getValues(a)
|
value = ent.getValues(a)
|
||||||
if isinstance(value,str):
|
if isinstance(value,str):
|
||||||
print a + ": " + value
|
print a + ": " + value
|
||||||
else:
|
else:
|
||||||
print a + ": "
|
print a + ": "
|
||||||
for l in value:
|
for l in value:
|
||||||
print "\t" + l
|
print "\t" + l
|
||||||
|
# blank line between results
|
||||||
|
print
|
||||||
|
|
||||||
except xmlrpclib.Fault, fault:
|
except xmlrpclib.Fault, fault:
|
||||||
print fault.faultString
|
print fault.faultString
|
||||||
|
@ -85,3 +85,14 @@ class IPAClient:
|
|||||||
def get_add_schema(self):
|
def get_add_schema(self):
|
||||||
result = self.transport.get_add_schema()
|
result = self.transport.get_add_schema()
|
||||||
return result
|
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
|
||||||
|
|
||||||
|
@ -118,3 +118,20 @@ class RPCClient:
|
|||||||
raise xmlrpclib.Fault(value, msg)
|
raise xmlrpclib.Fault(value, msg)
|
||||||
|
|
||||||
return result
|
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
|
||||||
|
@ -228,15 +228,14 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.host + ":" + str(self.port)
|
return self.host + ":" + str(self.port)
|
||||||
|
|
||||||
def toLDAPURL(self):
|
def __get_server_controls__(self):
|
||||||
return "ldap://%s:%d/" % (self.host,self.port)
|
"""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):
|
import sys
|
||||||
"""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
|
|
||||||
|
|
||||||
if self.proxydn is not None:
|
if self.proxydn is not None:
|
||||||
proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn;
|
proxydn = chr(0x04) + chr(4|0x80) + struct.pack('l', socket.htonl(len(self.proxydn))) + self.proxydn;
|
||||||
@ -247,7 +246,24 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
else:
|
else:
|
||||||
sctrl=None
|
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)
|
type, obj = self.result(res)
|
||||||
if not obj:
|
if not obj:
|
||||||
@ -260,6 +276,11 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
def getList(self,*args):
|
def getList(self,*args):
|
||||||
"""This wraps the search function to find all users."""
|
"""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)
|
res = self.search(*args)
|
||||||
type, obj = self.result(res)
|
type, obj = self.result(res)
|
||||||
if not obj:
|
if not obj:
|
||||||
@ -274,18 +295,8 @@ class IPAdmin(SimpleLDAPObject):
|
|||||||
def addEntry(self,*args):
|
def addEntry(self,*args):
|
||||||
"""This wraps the add function. It assumes that the entry is already
|
"""This wraps the add function. It assumes that the entry is already
|
||||||
populated with all of the desired objectclasses and attributes"""
|
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 = self.__get_server_controls__()
|
||||||
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))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
|
||||||
|
@ -28,10 +28,37 @@ import string
|
|||||||
from types import *
|
from types import *
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
import ipa.config
|
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:
|
class IPAServer:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
global _LDAPPool
|
||||||
# FIXME, this needs to be auto-discovered
|
# FIXME, this needs to be auto-discovered
|
||||||
self.host = 'localhost'
|
self.host = 'localhost'
|
||||||
self.port = 636
|
self.port = 636
|
||||||
@ -39,6 +66,8 @@ class IPAServer:
|
|||||||
self.bindkey = "/usr/share/ipa/key.pem"
|
self.bindkey = "/usr/share/ipa/key.pem"
|
||||||
self.bindca = "/usr/share/ipa/cacert.asc"
|
self.bindca = "/usr/share/ipa/cacert.asc"
|
||||||
|
|
||||||
|
if _LDAPPool is None:
|
||||||
|
_LDAPPool = IPAConnPool()
|
||||||
ipa.config.init_config()
|
ipa.config.init_config()
|
||||||
self.basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm())
|
self.basedn = ipaserver.util.realm_to_suffix(ipa.config.config.get_realm())
|
||||||
self.scope = ldap.SCOPE_SUBTREE
|
self.scope = ldap.SCOPE_SUBTREE
|
||||||
@ -49,10 +78,15 @@ class IPAServer:
|
|||||||
|
|
||||||
def get_dn_from_principal(self, princ):
|
def get_dn_from_principal(self, princ):
|
||||||
"""Given a kerberls principal get the LDAP uid"""
|
"""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 + ")"
|
filter = "(krbPrincipalName=" + princ + ")"
|
||||||
try:
|
try:
|
||||||
m1 = ipaserver.ipaldap.IPAdmin(self.host,self.port,self.bindca,self.bindcert,self.bindkey)
|
# The only anonymous search we should have
|
||||||
ent = m1.getEntry(self.basedn, self.scope, filter, None)
|
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:
|
except ldap.LDAPError, e:
|
||||||
raise xmlrpclib.Fault(1, e)
|
raise xmlrpclib.Fault(1, e)
|
||||||
except ipaserver.ipaldap.NoSuchEntryError:
|
except ipaserver.ipaldap.NoSuchEntryError:
|
||||||
@ -95,6 +129,7 @@ class IPAServer:
|
|||||||
"""Get a specific user's entry. Return as a dict of values.
|
"""Get a specific user's entry. Return as a dict of values.
|
||||||
Multi-valued fields are represented as lists.
|
Multi-valued fields are represented as lists.
|
||||||
"""
|
"""
|
||||||
|
global _LDAPPool
|
||||||
ent=""
|
ent=""
|
||||||
if opts:
|
if opts:
|
||||||
self.set_principal(opts['remoteuser'])
|
self.set_principal(opts['remoteuser'])
|
||||||
@ -110,8 +145,9 @@ class IPAServer:
|
|||||||
|
|
||||||
filter = "(uid=" + username + ")"
|
filter = "(uid=" + username + ")"
|
||||||
try:
|
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)
|
ent = m1.getEntry(self.basedn, self.scope, filter, None)
|
||||||
|
_LDAPPool.releaseConn(m1)
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError, e:
|
||||||
raise xmlrpclib.Fault(1, e)
|
raise xmlrpclib.Fault(1, e)
|
||||||
except ipaserver.ipaldap.NoSuchEntryError:
|
except ipaserver.ipaldap.NoSuchEntryError:
|
||||||
@ -121,6 +157,7 @@ class IPAServer:
|
|||||||
|
|
||||||
def add_user (self, user, user_container="ou=users,ou=default",opts=None):
|
def add_user (self, user, user_container="ou=users,ou=default",opts=None):
|
||||||
"""Add a user in LDAP"""
|
"""Add a user in LDAP"""
|
||||||
|
global _LDAPPool
|
||||||
if (isinstance(user, tuple)):
|
if (isinstance(user, tuple)):
|
||||||
user = user[0]
|
user = user[0]
|
||||||
dn="uid=%s,%s,%s" % (user['uid'], user_container,self.basedn)
|
dn="uid=%s,%s,%s" % (user['uid'], user_container,self.basedn)
|
||||||
@ -154,8 +191,9 @@ class IPAServer:
|
|||||||
raise xmlrpclib.Fault(2, "No such user")
|
raise xmlrpclib.Fault(2, "No such user")
|
||||||
|
|
||||||
try:
|
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)
|
res = m1.addEntry(entry)
|
||||||
|
_LDAPPool.releaseConn(m1)
|
||||||
return res
|
return res
|
||||||
except ldap.ALREADY_EXISTS:
|
except ldap.ALREADY_EXISTS:
|
||||||
raise xmlrpclib.Fault(3, "User already exists")
|
raise xmlrpclib.Fault(3, "User already exists")
|
||||||
@ -210,12 +248,24 @@ class IPAServer:
|
|||||||
"""Return a list containing a User object for each
|
"""Return a list containing a User object for each
|
||||||
existing user.
|
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?
|
# FIXME: Is this the filter we want or should it be more specific?
|
||||||
filter = "(objectclass=posixAccount)"
|
filter = "(objectclass=posixAccount)"
|
||||||
try:
|
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)
|
all_users = m1.getList(self.basedn, self.scope, filter, None)
|
||||||
|
_LDAPPool.releaseConn(m1)
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError, e:
|
||||||
raise xmlrpclib.Fault(1, e)
|
raise xmlrpclib.Fault(1, e)
|
||||||
except ipaserver.ipaldap.NoSuchEntryError:
|
except ipaserver.ipaldap.NoSuchEntryError:
|
||||||
@ -226,3 +276,52 @@ class IPAServer:
|
|||||||
users.append(self.convert_entry(u))
|
users.append(self.convert_entry(u))
|
||||||
|
|
||||||
return users
|
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
|
||||||
|
@ -278,6 +278,7 @@ def handler(req, profiling=False):
|
|||||||
h.register_function(f.add_user)
|
h.register_function(f.add_user)
|
||||||
h.register_function(f.get_add_schema)
|
h.register_function(f.get_add_schema)
|
||||||
h.register_function(f.get_all_users)
|
h.register_function(f.get_all_users)
|
||||||
|
h.register_function(f.find_users)
|
||||||
h.handle_request(req)
|
h.handle_request(req)
|
||||||
finally:
|
finally:
|
||||||
pass
|
pass
|
||||||
|
Loading…
Reference in New Issue
Block a user