mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-27 16:46:42 -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:
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user