Enable LDAP SASL authentication using a forwarded kerberos ticket

Handle both SASL auth and proxied authentication
Refactor LDAP connection code to be simpler
Other small bug fixes
This commit is contained in:
rcritten@redhat.com 2007-09-05 13:14:23 -04:00
parent 82943c31de
commit 9b30f46744
5 changed files with 176 additions and 138 deletions

View File

@ -50,7 +50,9 @@ def main():
client = ipaclient.IPAClient() client = ipaclient.IPAClient()
users = client.find_users(args[1], sattrs=['dn','uid','cn','homeDirectory']) users = client.find_users(args[1], sattrs=['dn','uid','cn','homeDirectory'])
if len(users) == 0: counter = users[0]
users = users[1:]
if counter == 0:
print "No entries found for", args[1] print "No entries found for", args[1]
return 0 return 0

View File

@ -59,6 +59,9 @@ def main():
except ipa.ipaerror.IPAError, e: except ipa.ipaerror.IPAError, e:
print "%s" % e.message print "%s" % e.message
return 1 return 1
except kerberos.GSSError, e:
print "Could not initialize GSSAPI: %s/%s" % (e[0][0][0], e[0][1][0])
return 1
if options.gecos: if options.gecos:
user.setValue('gecos', options.gecos) user.setValue('gecos', options.gecos)
@ -79,6 +82,9 @@ def main():
except xmlrpclib.ProtocolError, e: except xmlrpclib.ProtocolError, e:
print "Unable to connect to IPA server: %s" % (e.errmsg) print "Unable to connect to IPA server: %s" % (e.errmsg)
return 1 return 1
except ipa.ipaerror.IPAError, e:
print "%s" % (e.message)
return 1
return 0 return 0

View File

@ -35,13 +35,14 @@ import cStringIO
import time import time
import operator import operator
import struct import struct
import ldap.sasl
from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples from ldap.controls import LDAPControl,DecodeControlTuples,EncodeControlTuples
from ldap.modlist import modifyModlist
from ldap.ldapobject import SimpleLDAPObject from ldap.ldapobject import SimpleLDAPObject
from ipa import ipaerror, ipautil from ipa import ipaerror, ipautil
# Global variable to define SASL auth
sasl_auth = ldap.sasl.sasl({},'GSSAPI')
class Entry: class Entry:
"""This class represents an LDAP Entry object. An LDAP entry consists of a DN """This class represents an LDAP Entry object. An LDAP entry consists of a DN
and a list of attributes. Each attribute consists of a name and a list of and a list of attributes. Each attribute consists of a name and a list of
@ -196,22 +197,34 @@ class IPAdmin(SimpleLDAPObject):
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e) raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e)
def __localinit__(self): def __localinit__(self):
"""If a CA certificate is provided then it is assumed that we are
doing SSL client authentication with proxy auth.
If a CA certificate is not present then it is assumed that we are
using a forwarded kerberos ticket for SASL auth. SASL provides
its own encryption.
"""
if self.cacert is not None:
SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port)) SimpleLDAPObject.__init__(self,'ldaps://%s:%d' % (self.host,self.port))
else:
SimpleLDAPObject.__init__(self,'ldap://%s:%d' % (self.host,self.port))
def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None): def __init__(self,host,port,cacert,bindcert,bindkey,proxydn=None):
"""We just set our instance variables and wrap the methods - the real work is """We just set our instance variables and wrap the methods - the real
done in __localinit__ and __initPart2 - these are separated out this way so work is done in __localinit__ and __initPart2 - these are separated
that we can call them from places other than instance creation e.g. when out this way so that we can call them from places other than
using the start command, we just need to reconnect, not create a new instance""" instance creation e.g. when we just need to reconnect, not create a
new instance"""
# ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) # ldap.set_option(ldap.OPT_DEBUG_LEVEL,255)
if cacert is not None:
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,cacert) ldap.set_option(ldap.OPT_X_TLS_CACERTFILE,cacert)
ldap.set_option(ldap.OPT_X_TLS_CERTFILE,bindcert) ldap.set_option(ldap.OPT_X_TLS_CERTFILE,bindcert)
ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey) ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey)
self.__wrapmethods() self.__wrapmethods()
self.port = port or 389 self.port = port or 389
self.sslport = 0
self.host = host self.host = host
self.cacert = cacert
self.bindcert = bindcert self.bindcert = bindcert
self.bindkey = bindkey self.bindkey = bindkey
self.proxydn = proxydn self.proxydn = proxydn
@ -251,6 +264,12 @@ class IPAdmin(SimpleLDAPObject):
def set_proxydn(self, proxydn): def set_proxydn(self, proxydn):
self.proxydn = proxydn self.proxydn = proxydn
def set_keytab(self, keytab):
if keytab is not None:
os.environ["KRB5CCNAME"] = keytab
self.sasl_interactive_bind_s("", sasl_auth)
self.proxydn = None
def getEntry(self,*args): def getEntry(self,*args):
"""This wraps the search function. It is common to just get one entry""" """This wraps the search function. It is common to just get one entry"""
@ -346,6 +365,7 @@ class IPAdmin(SimpleLDAPObject):
sctrl = self.__get_server_controls__() sctrl = self.__get_server_controls__()
try: try:
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.add_s(*args) self.add_s(*args)
except ldap.ALREADY_EXISTS: except ldap.ALREADY_EXISTS:
@ -366,6 +386,7 @@ class IPAdmin(SimpleLDAPObject):
raise ipaerror.gen_exception(ipaerror.LDAP_EMPTY_MODLIST) raise ipaerror.gen_exception(ipaerror.LDAP_EMPTY_MODLIST)
try: try:
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.modify_s(dn, modlist) self.modify_s(dn, modlist)
# this is raised when a 'delete' attribute isn't found. # this is raised when a 'delete' attribute isn't found.
@ -428,6 +449,7 @@ class IPAdmin(SimpleLDAPObject):
modlist.append((operation, "nsAccountlock", "true")) modlist.append((operation, "nsAccountlock", "true"))
try: try:
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.modify_s(dn, modlist) self.modify_s(dn, modlist)
except ldap.LDAPError, e: except ldap.LDAPError, e:
@ -440,6 +462,7 @@ class IPAdmin(SimpleLDAPObject):
sctrl = self.__get_server_controls__() sctrl = self.__get_server_controls__()
try: try:
if sctrl is not None:
self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl)
self.delete_s(*args) self.delete_s(*args)
except ldap.LDAPError, e: except ldap.LDAPError, e:

View File

@ -47,19 +47,30 @@ DefaultGroupContainer = "cn=groups,cn=accounts"
# this is not anticipated. # this is not anticipated.
class IPAConnPool: class IPAConnPool:
def __init__(self): def __init__(self):
self.numentries = 0
self.freelist = [] self.freelist = []
def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None): def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None, keytab=None):
self.numentries = self.numentries + 1 conn = None
if len(self.freelist) > 0: if len(self.freelist) > 0:
conn = self.freelist.pop() for i in range(len(self.freelist)):
else: c = self.freelist[i]
if ((c.host == host) and (c.port == port)):
conn = self.freelist.pop(i)
break
if conn is None:
conn = ipaserver.ipaldap.IPAdmin(host,port,bindca,bindcert,bindkey) conn = ipaserver.ipaldap.IPAdmin(host,port,bindca,bindcert,bindkey)
if proxydn is not None:
conn.set_proxydn(proxydn) conn.set_proxydn(proxydn)
else:
conn.set_keytab(keytab)
return conn return conn
def releaseConn(self, conn): def releaseConn(self, conn):
# We can't re-use SASL connections. If proxydn is None it means
# we have a keytab set. See ipaldap.set_keytab
if conn.proxydn is None:
conn.unbind_s()
else:
self.freelist.append(conn) self.freelist.append(conn)
class IPAServer: class IPAServer:
@ -68,7 +79,8 @@ class IPAServer:
global _LDAPPool 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 = 389
self.sslport = 636
self.bindcert = "/usr/share/ipa/cert.pem" self.bindcert = "/usr/share/ipa/cert.pem"
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"
@ -79,24 +91,84 @@ class IPAServer:
self.basedn = ipa.ipautil.realm_to_suffix(ipa.config.config.get_realm()) self.basedn = ipa.ipautil.realm_to_suffix(ipa.config.config.get_realm())
self.scope = ldap.SCOPE_SUBTREE self.scope = ldap.SCOPE_SUBTREE
self.princ = None self.princ = None
self.keytab = None
def set_principal(self, princ): def set_principal(self, princ):
self.princ = princ self.princ = princ
def set_keytab(self, keytab):
self.keytab = keytab
def get_dn_from_principal(self, princ): def get_dn_from_principal(self, princ):
"""Given a kerberls principal get the LDAP uid""" """Given a kerberos principal get the LDAP uid"""
global _LDAPPool global _LDAPPool
filter = "(krbPrincipalName=" + princ + ")" filter = "(krbPrincipalName=" + princ + ")"
# The only anonymous search we should have # The only anonymous search we should have
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,None) conn = _LDAPPool.getConn(self.host,self.sslport,self.bindca,self.bindcert,self.bindkey,None,None)
try: try:
ent = m1.getEntry(self.basedn, self.scope, filter, ['dn']) ent = conn.getEntry(self.basedn, self.scope, filter, ['dn'])
finally: finally:
_LDAPPool.releaseConn(m1) _LDAPPool.releaseConn(conn)
return "dn:" + ent.dn return "dn:" + ent.dn
def __setup_connection(self, opts):
"""Set up common things done in the connection.
If there is a keytab then return None as the proxy dn and the keytab
otherwise return the proxy dn and None as the keytab.
We only want one or the other used at one time and we prefer
the keytab. So if there is a keytab, return that and None for
proxy dn to make calling getConn() easier.
"""
if opts:
if opts.get('keytab'):
self.set_keytab(opts['keytab'])
self.set_principal(None)
else:
self.set_keytab(None)
self.set_principal(opts['remoteuser'])
else:
self.set_keytab(None)
# The caller should have already set the principal
if self.princ is not None:
return self.get_dn_from_principal(self.princ), None
else:
return None, self.keytab
def getConnection(self, opts):
"""Wrapper around IPAConnPool.getConn() so we don't have to pass
around self.* every time a connection is needed.
For SASL connections (where we have a keytab) we can't set
the SSL variables for certificates. It confuses the ldap
module.
"""
global _LDAPPool
(proxy_dn, keytab) = self.__setup_connection(opts)
if keytab is not None:
bindca = None
bindcert = None
bindkey = None
port = self.port
else:
bindca = self.bindca
bindcert = self.bindcert
bindkey = self.bindkey
port = self.sslport
return _LDAPPool.getConn(self.host,port,bindca,bindcert,bindkey,proxy_dn,keytab)
def releaseConnection(self, conn):
global _LDAPPool
_LDAPPool.releaseConn(conn)
def convert_entry(self, ent): def convert_entry(self, ent):
entry = dict(ent.data) entry = dict(ent.data)
entry['dn'] = ent.dn entry['dn'] = ent.dn
@ -110,24 +182,17 @@ class IPAServer:
entry[key] = value[0] entry[key] = value[0]
return entry return entry
def __get_entry (self, base, filter, sattrs=None, opts=None): def __get_entry (self, base, filter, sattrs=None, opts=None):
"""Get a specific entry. Return as a dict of values. """Get a specific 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: conn = self.getConnection(opts)
self.set_principal(opts['remoteuser'])
dn = self.get_dn_from_principal(self.princ)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
try: try:
ent = m1.getEntry(base, self.scope, filter, sattrs) ent = conn.getEntry(base, self.scope, filter, sattrs)
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
return self.convert_entry(ent) return self.convert_entry(ent)
@ -137,8 +202,6 @@ class IPAServer:
oldentry is a dict oldentry is a dict
newentry is a dict newentry is a dict
""" """
global _LDAPPool
oldentry = self.convert_scalar_values(oldentry) oldentry = self.convert_scalar_values(oldentry)
newentry = self.convert_scalar_values(newentry) newentry = self.convert_scalar_values(newentry)
@ -150,16 +213,11 @@ class IPAServer:
except KeyError, e: except KeyError, e:
raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN) raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN)
if opts: conn = self.getConnection(opts)
self.set_principal(opts['remoteuser'])
proxydn = self.get_dn_from_principal(self.princ)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,proxydn)
try: try:
res = m1.updateEntry(moddn, oldentry, newentry) res = conn.updateEntry(moddn, oldentry, newentry)
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
return res return res
def __safe_filter(self, criteria): def __safe_filter(self, criteria):
@ -234,8 +292,6 @@ class IPAServer:
attribute name and the value is either a string or in the case attribute name and the value is either a string or in the case
of a multi-valued field a list of values. user_container sets of a multi-valued field a list of values. user_container sets
where in the tree the user is placed.""" where in the tree the user is placed."""
global _LDAPPool
if user_container is None: if user_container is None:
user_container = DefaultUserContainer user_container = DefaultUserContainer
@ -288,16 +344,11 @@ class IPAServer:
for u in user: for u in user:
entry.setValues(u, user[u]) entry.setValues(u, user[u])
if opts: conn = self.getConnection(opts)
self.set_principal(opts['remoteuser'])
dn = self.get_dn_from_principal(self.princ)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
try: try:
res = m1.addEntry(entry) res = conn.addEntry(entry)
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
return res return res
def get_add_schema (self): def get_add_schema (self):
@ -348,20 +399,13 @@ 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'])
dn = self.get_dn_from_principal(self.princ)
filter = "(objectclass=posixAccount)" filter = "(objectclass=posixAccount)"
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) conn = self.getConnection(opts)
try: try:
all_users = m1.getList(self.basedn, self.scope, filter, None) all_users = conn.getList(self.basedn, self.scope, filter, None)
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
users = [] users = []
for u in all_users: for u in all_users:
@ -372,13 +416,6 @@ class IPAServer:
def find_users (self, criteria, sattrs=None, opts=None): def find_users (self, criteria, sattrs=None, opts=None):
"""Returns a list: counter followed by the results. """Returns a list: counter followed by the results.
If the results are truncated, counter will be set to -1.""" If the results are truncated, counter will be set to -1."""
global _LDAPPool
if opts:
self.set_principal(opts['remoteuser'])
dn = self.get_dn_from_principal(self.princ)
# Assume the list of fields to search will come from a central # Assume the list of fields to search will come from a central
# configuration repository. A good format for that would be # configuration repository. A good format for that would be
# a comma-separated list of fields # a comma-separated list of fields
@ -394,21 +431,21 @@ class IPAServer:
(exact_match_filter, partial_match_filter) = self.__generate_match_filters( (exact_match_filter, partial_match_filter) = self.__generate_match_filters(
search_fields, criteria_words) search_fields, criteria_words)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) conn = self.getConnection(opts)
try: try:
try: try:
exact_results = m1.getListAsync(self.basedn, self.scope, exact_results = conn.getListAsync(self.basedn, self.scope,
exact_match_filter, sattrs) exact_match_filter, sattrs)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
exact_results = [0] exact_results = [0]
try: try:
partial_results = m1.getListAsync(self.basedn, self.scope, partial_results = conn.getListAsync(self.basedn, self.scope,
partial_match_filter, sattrs) partial_match_filter, sattrs)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
partial_results = [0] partial_results = [0]
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
exact_counter = exact_results[0] exact_counter = exact_results[0]
partial_counter = partial_results[0] partial_counter = partial_results[0]
@ -450,13 +487,6 @@ class IPAServer:
def mark_user_deleted (self, uid, opts=None): def mark_user_deleted (self, uid, opts=None):
"""Mark a user as inactive in LDAP. We aren't actually deleting """Mark a user as inactive in LDAP. We aren't actually deleting
users here, just making it so they can't log in, etc.""" users here, just making it so they can't log in, etc."""
global _LDAPPool
if opts:
self.set_principal(opts['remoteuser'])
proxydn = self.get_dn_from_principal(self.princ)
user = self.get_user_by_uid(uid, ['dn', 'uid', 'nsAccountlock'], opts) user = self.get_user_by_uid(uid, ['dn', 'uid', 'nsAccountlock'], opts)
# Are we doing an add or replace operation? # Are we doing an add or replace operation?
@ -467,11 +497,11 @@ class IPAServer:
else: else:
has_key = False has_key = False
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,proxydn) conn = self.getConnection(opts)
try: try:
res = m1.inactivateEntry(user['dn'], has_key) res = conn.inactivateEntry(user['dn'], has_key)
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
return res return res
def delete_user (self, uid, opts=None): def delete_user (self, uid, opts=None):
@ -483,18 +513,15 @@ class IPAServer:
The memberOf plugin handles removing the user from any other The memberOf plugin handles removing the user from any other
groups. groups.
""" """
if opts:
self.set_principal(opts['remoteuser'])
dn = self.get_dn_from_principal(self.princ)
user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts) user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts)
if user_dn is None: if user_dn is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) conn = self.getConnection(opts)
res = m1.deleteEntry(user_dn['dn']) try:
_LDAPPool.releaseConn(m1) res = conn.deleteEntry(user_dn['dn'])
finally:
self.releaseConnection(conn)
return res return res
# Group support # Group support
@ -532,8 +559,6 @@ class IPAServer:
attribute name and the value is either a string or in the case attribute name and the value is either a string or in the case
of a multi-valued field a list of values. group_container sets of a multi-valued field a list of values. group_container sets
where in the tree the group is placed.""" where in the tree the group is placed."""
global _LDAPPool
if group_container is None: if group_container is None:
group_container = DefaultGroupContainer group_container = DefaultGroupContainer
@ -554,38 +579,26 @@ class IPAServer:
for g in group: for g in group:
entry.setValues(g, group[g]) entry.setValues(g, group[g])
if opts: conn = self.getConnection(opts)
self.set_principal(opts['remoteuser'])
dn = self.get_dn_from_principal(self.princ)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn)
try: try:
res = m1.addEntry(entry) res = conn.addEntry(entry)
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
def find_groups (self, criteria, sattrs=None, opts=None): def find_groups (self, criteria, sattrs=None, opts=None):
"""Return a list containing a User object for each """Return a list containing a User object for each
existing group that matches the criteria. existing group that matches the criteria.
""" """
global _LDAPPool
if opts:
self.set_principal(opts['remoteuser'])
dn = self.get_dn_from_principal(self.princ)
criteria = self.__safe_filter(criteria) criteria = self.__safe_filter(criteria)
filter = "(&(cn=%s)(objectClass=posixGroup))" % criteria filter = "(&(cn=%s)(objectClass=posixGroup))" % criteria
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) conn = self.getConnection(opts)
try: try:
results = m1.getList(self.basedn, self.scope, filter, sattrs) results = conn.getList(self.basedn, self.scope, filter, sattrs)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND): except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
results = [] results = []
finally: finally:
_LDAPPool.releaseConn(m1) self.releaseConnection(conn)
groups = [] groups = []
for u in results: for u in results:
@ -599,9 +612,6 @@ class IPAServer:
group is the cn of the group to be added to group is the cn of the group to be added to
""" """
if opts:
self.set_principal(opts['remoteuser'])
old_group = self.get_group_by_cn(group, None, opts) old_group = self.get_group_by_cn(group, None, opts)
if old_group is None: if old_group is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@ -652,9 +662,6 @@ class IPAServer:
group is the cn of the group to be removed from group is the cn of the group to be removed from
""" """
if opts:
self.set_principal(opts['remoteuser'])
old_group = self.get_group_by_cn(group, None, opts) old_group = self.get_group_by_cn(group, None, opts)
if old_group is None: if old_group is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@ -718,19 +725,16 @@ class IPAServer:
The memberOf plugin handles removing the group from any other The memberOf plugin handles removing the group from any other
groups. groups.
""" """
if opts:
self.set_principal(opts['remoteuser'])
dn = self.get_dn_from_principal(self.princ)
group = self.get_group_by_cn(group_cn, ['dn', 'cn'], opts) group = self.get_group_by_cn(group_cn, ['dn', 'cn'], opts)
if len(group) != 1: if len(group) != 1:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
m1 = _LDAPPool.getConn(self.host,self.port,self.bindca,self.bindcert,self.bindkey,dn) conn = self.getConnection(opts)
res = m1.deleteEntry(group[0]['dn']) try:
_LDAPPool.releaseConn(m1) res = conn.deleteEntry(group[0]['dn'])
finally:
self.releaseConnection(conn)
return res return res
def add_group_to_group(self, group, tgroup, opts=None): def add_group_to_group(self, group, tgroup, opts=None):
@ -739,9 +743,6 @@ class IPAServer:
tgroup is the cn of the group to be added to tgroup is the cn of the group to be added to
""" """
if opts:
self.set_principal(opts['remoteuser'])
old_group = self.get_group_by_cn(tgroup, None, opts) old_group = self.get_group_by_cn(tgroup, None, opts)
if old_group is None: if old_group is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)

View File

@ -126,13 +126,19 @@ class ModXMLRPCRequestHandler(object):
def register_instance(self,instance): def register_instance(self,instance):
self.register_module(instance) self.register_module(instance)
def _marshaled_dispatch(self, data, remoteuser): def _marshaled_dispatch(self, data, req):
"""Dispatches an XML-RPC method from marshalled (XML) data.""" """Dispatches an XML-RPC method from marshalled (XML) data."""
params, method = loads(data) params, method = loads(data)
# Populate the Apache environment variables
req.add_common_vars()
opts={} opts={}
opts['remoteuser'] = remoteuser opts['remoteuser'] = req.user
if req.subprocess_env.get("KRB5CCNAME") is not None:
opts['keytab'] = req.subprocess_env.get("KRB5CCNAME")
# Tack onto the end of the passed-in arguments any options we also # Tack onto the end of the passed-in arguments any options we also
# need # need
@ -263,7 +269,7 @@ class ModXMLRPCRequestHandler(object):
req.allow_methods(['POST'],1) req.allow_methods(['POST'],1)
raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
response = self._marshaled_dispatch(req.read(), req.user) response = self._marshaled_dispatch(req.read(), req)
req.content_type = "text/xml" req.content_type = "text/xml"
req.set_content_length(len(response)) req.set_content_length(len(response))