diff options
-rw-r--r-- | ipaserver/install/krbinstance.py | 4 | ||||
-rw-r--r-- | ipaserver/install/service.py | 2 | ||||
-rw-r--r-- | ipaserver/ipaldap.py | 105 | ||||
-rw-r--r-- | ipaserver/plugins/ldap2.py | 124 |
4 files changed, 164 insertions, 71 deletions
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py index 6ed38516..6566d8a3 100644 --- a/ipaserver/install/krbinstance.py +++ b/ipaserver/install/krbinstance.py @@ -259,7 +259,7 @@ class KrbInstance(service.Service): entry.setValues("nsSaslMapFilterTemplate", '(krbPrincipalName=\\1@\\2)') try: - self.admin_conn.add_s(entry) + self.admin_conn.addEntry(entry) except ldap.ALREADY_EXISTS: root_logger.critical("failed to add Full Principal Sasl mapping") raise e @@ -272,7 +272,7 @@ class KrbInstance(service.Service): entry.setValues("nsSaslMapFilterTemplate", '(krbPrincipalName=&@%s)' % self.realm) try: - self.admin_conn.add_s(entry) + self.admin_conn.addEntry(entry) except ldap.ALREADY_EXISTS: root_logger.critical("failed to add Name Only Sasl mapping") raise e diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py index 249727b1..d9e6def3 100644 --- a/ipaserver/install/service.py +++ b/ipaserver/install/service.py @@ -289,7 +289,7 @@ class Service(object): "enabledService", "startOrder " + str(order)) try: - conn.add_s(entry) + conn.addEntry(entry) except ldap.ALREADY_EXISTS, e: root_logger.critical("failed to add %s Service startup entry" % name) raise e diff --git a/ipaserver/ipaldap.py b/ipaserver/ipaldap.py index fdcbe624..82feacbc 100644 --- a/ipaserver/ipaldap.py +++ b/ipaserver/ipaldap.py @@ -37,10 +37,52 @@ from ipaserver import ipautil from ipalib import errors from ipapython.ipautil import format_netloc from ipapython.entity import Entity +from ipaserver.plugins.ldap2 import IPASimpleLDAPObject # Global variable to define SASL auth SASL_AUTH = ldap.sasl.sasl({},'GSSAPI') +class IPAEntryLDAPObject(IPASimpleLDAPObject): + def __init__(self, *args, **kwds): + IPASimpleLDAPObject.__init__(self, *args, **kwds) + + def result(self, msgid=ldap.RES_ANY, all=1, timeout=None): + objtype, data = IPASimpleLDAPObject.result(self, msgid, all, timeout) + # data is either a 2-tuple or a list of 2-tuples + if data: + if isinstance(data, tuple): + return objtype, Entry(data) + elif isinstance(data, list): + return objtype, [Entry(x) for x in data] + else: + raise TypeError, "unknown data type %s returned by result" % type(data) + else: + return objtype, data + + def add(self, dn, modlist): + if isinstance(dn, Entry): + return IPASimpleLDAPObject.add(self, dn.dn, dn.toTupleList()) + else: + return IPASimpleLDAPObject.add(self, dn, modlist) + + def add_s(self, dn, modlist): + if isinstance(dn, Entry): + return IPASimpleLDAPObject.add_s(self, dn.dn, dn.toTupleList()) + else: + return IPASimpleLDAPObject.add_s(self, dn, modlist) + + def add_ext(self, dn, modlist, serverctrls=None, clientctrls=None): + if isinstance(dn, Entry): + return IPASimpleLDAPObject.add_ext(self, dn.dn, dn.toTupleList(), serverctrls, clientctrls) + else: + return IPASimpleLDAPObject.add_ext(self, dn, modlist, serverctrls, clientctrls) + + def add_ext_s(self, dn, modlist, serverctrls=None, clientctrls=None): + if isinstance(dn, Entry): + return IPASimpleLDAPObject.add_ext_s(self, dn.dn, dn.toTupleList(), serverctrls, clientctrls) + else: + return IPASimpleLDAPObject.add_ext_s(self, dn, modlist, serverctrls, clientctrls) + class Entry: """ This class represents an LDAP Entry object. An LDAP entry consists of @@ -165,49 +207,7 @@ class Entry: ldif.LDIFWriter(sio,Entry.base64_attrs,1000).unparse(self.dn,newdata) return sio.getvalue() -def wrapper(f,name): - """This is the method that wraps all of the methods of the superclass. - This seems to need to be an unbound method, that's why it's outside - of IPAdmin. Perhaps there is some way to do this with the new - classmethod or staticmethod of 2.4. Basically, we replace every call - to a method in SimpleLDAPObject (the superclass of IPAdmin) with a - call to inner. The f argument to wrapper is the bound method of - IPAdmin (which is inherited from the superclass). Bound means that it - will implicitly be called with the self argument, it is not in the - args list. name is the name of the method to call. If name is a - method that returns entry objects (e.g. result), we wrap the data - returned by an Entry class. If name is a method that takes an entry - argument, we extract the raw data from the entry object to pass in. - """ - def inner(*args, **kargs): - if name == 'result': - objtype, data = f(*args, **kargs) - # data is either a 2-tuple or a list of 2-tuples - # print data - if data: - if isinstance(data,tuple): - return objtype, Entry(data) - elif isinstance(data,list): - return objtype, [Entry(x) for x in data] - else: - raise TypeError, "unknown data type %s returned by result" % type(data) - else: - return objtype, data - elif name.startswith('add'): - # the first arg is self - # the second and third arg are the dn and the data to send - # We need to convert the Entry into the format used by - # python-ldap - ent = args[0] - if isinstance(ent,Entry): - return f(ent.dn, ent.toTupleList(), *args[2:]) - else: - return f(*args, **kargs) - else: - return f(*args, **kargs) - return inner - -class IPAdmin(SimpleLDAPObject): +class IPAdmin(IPAEntryLDAPObject): def __localinit(self): """If a CA certificate is provided then it is assumed that we are @@ -218,12 +218,12 @@ class IPAdmin(SimpleLDAPObject): its own encryption. """ if self.cacert is not None: - SimpleLDAPObject.__init__(self,'ldaps://%s' % format_netloc(self.host, self.port)) + IPAEntryLDAPObject.__init__(self,'ldaps://%s' % format_netloc(self.host, self.port)) else: if self.ldapi: - SimpleLDAPObject.__init__(self,'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % "-".join(self.realm.split("."))) + IPAEntryLDAPObject.__init__(self,'ldapi://%%2fvar%%2frun%%2fslapd-%s.socket' % "-".join(self.realm.split("."))) else: - SimpleLDAPObject.__init__(self,'ldap://%s' % format_netloc(self.host, self.port)) + IPAEntryLDAPObject.__init__(self,'ldap://%s' % format_netloc(self.host, self.port)) def __init__(self,host='',port=389,cacert=None,bindcert=None,bindkey=None,proxydn=None,debug=None,ldapi=False,realm=None): """We just set our instance variables and wrap the methods - the real @@ -240,7 +240,6 @@ class IPAdmin(SimpleLDAPObject): if bindkey is not None: ldap.set_option(ldap.OPT_X_TLS_KEYFILE,bindkey) - self.__wrapmethods() self.port = port self.host = host self.cacert = cacert @@ -468,7 +467,7 @@ class IPAdmin(SimpleLDAPObject): self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) self.add_s(entry.dn, entry.toTupleList()) except ldap.LDAPError, e: - arg_desc = 'entry=%s, modlist=%s' % (entry) + arg_desc = 'entry=%s' % (entry) self.__handle_errors(e, arg_desc=arg_desc) return True @@ -621,16 +620,6 @@ class IPAdmin(SimpleLDAPObject): self.__handle_errors(e) return True - def __wrapmethods(self): - """This wraps all methods of SimpleLDAPObject, so that we can intercept - the methods that deal with entries. Instead of using a raw list of tuples - of lists of hashes of arrays as the entry object, we want to wrap entries - in an Entry class that provides some useful methods""" - for name in dir(self.__class__.__bases__[0]): - attr = getattr(self, name) - if callable(attr): - setattr(self, name, wrapper(attr, name)) - def waitForEntry(self, dn, timeout=7200, attr='', quiet=True): scope = ldap.SCOPE_BASE filter = "(objectclass=*)" diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index 1229e5bb..4bfc849d 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -39,6 +39,7 @@ import pwd import krbV from ipapython.ipa_log_manager import * import ldap as _ldap +from ldap.ldapobject import SimpleLDAPObject import ldap.filter as _ldap_filter import ldap.sasl as _ldap_sasl from ldap.controls import LDAPControl @@ -55,7 +56,6 @@ from ipalib.crud import CrudBackend from ipalib.encoder import Encoder, encode_args, decode_retval from ipalib.request import context - # Group Member types MEMBERS_ALL = 0 MEMBERS_DIRECT = 1 @@ -80,6 +80,110 @@ def _encode_bool(self, value): # set own Bool parameter encoder Bool._encode = _encode_bool +class IPASimpleLDAPObject(SimpleLDAPObject): + ''' + This is a thin layer over SimpleLDAPObject which allows us to utilize + IPA specific types with the python-ldap API without the IPA caller needing + to perform the type translation, consider this a convenience layer for the + IPA programmer. + + This subclass performs the following translations: + + * DN objects may be passed into any ldap function expecting a dn. The DN + object will be converted to a string before being passed to the python-ldap + function. This allows us to maintain DN objects as DN objects in the rest + of the code (useful for DN manipulation and DN information) and not have + to worry about conversion to a string prior to passing it ldap. + + ''' + def __init__(self, *args, **kwds): + SimpleLDAPObject.__init__(self, *args, **kwds) + + def add(self, dn, modlist): + return SimpleLDAPObject.add(self, str(dn), modlist) + + def add_ext(self, dn, modlist, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.add_ext(self, str(dn), modlist, serverctrls, clientctrls) + + def add_ext_s(self, dn, modlist, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.add_ext_s(self, str(dn), modlist, serverctrls, clientctrls) + + def add_s(self, dn, modlist): + return SimpleLDAPObject.add_s(self, str(dn), modlist) + + def compare(self, dn, attr, value): + return SimpleLDAPObject.compare(self, str(dn), attr, value) + + def compare_ext(self, dn, attr, value, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.compare_ext(self, str(dn), attr, value, serverctrls, clientctrls) + + def compare_ext_s(self, dn, attr, value, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.compare_ext_s(self, str(dn), attr, value, serverctrls, clientctrls) + + def compare_s(self, dn, attr, value): + return SimpleLDAPObject.compare_s(self, str(dn), attr, value) + + def delete(self, dn): + return SimpleLDAPObject.delete(self, str(dn)) + + def delete_ext(self, dn, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.delete_ext(self, str(dn), serverctrls, clientctrls) + + def delete_ext_s(self, dn, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.delete_ext_s(self, str(dn), serverctrls, clientctrls) + + def delete_s(self, dn): + return SimpleLDAPObject.delete_s(self, str(dn)) + + def modify(self, dn, modlist): + return SimpleLDAPObject.modify(self, str(dn), modlist) + + def modify_ext(self, dn, modlist, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.modify_ext(self, str(dn), modlist, serverctrls, clientctrls) + + def modify_ext_s(self, dn, modlist, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.modify_ext_s(self, str(dn), modlist, serverctrls, clientctrls) + + def modify_s(self, dn, modlist): + return SimpleLDAPObject.modify_s(self, str(dn), modlist) + + def modrdn(self, dn, newrdn, delold=1): + return SimpleLDAPObject.modrdn(self, str(dn), str(newrdn), delold) + + def modrdn_s(self, dn, newrdn, delold=1): + return SimpleLDAPObject.modrdn_s(self, str(dn), str(newrdn), delold) + + def read_subschemasubentry_s(self, subschemasubentry_dn, attrs=None): + return SimpleLDAPObject.read_subschemasubentry_s(self, str(subschemasubentry_dn), attrs) + + def rename(self, dn, newrdn, newsuperior=None, delold=1, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.rename(self, str(dn), str(newrdn), newsuperior, delold, serverctrls, clientctrls) + + def rename_s(self, dn, newrdn, newsuperior=None, delold=1, serverctrls=None, clientctrls=None): + return SimpleLDAPObject.rename_s(self, str(dn), str(newrdn), newsuperior, delold, serverctrls, clientctrls) + + def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): + return SimpleLDAPObject.search(self, str(base), scope, filterstr, attrlist, attrsonly) + + def search_ext(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0, + serverctrls=None, clientctrls=None, timeout=-1, sizelimit=0): + return SimpleLDAPObject.search_ext(self, str(base), scope, filterstr, attrlist, attrsonly, + serverctrls, clientctrls, timeout, sizelimit) + + def search_ext_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0, + serverctrls=None, clientctrls=None, timeout=-1, sizelimit=0): + return SimpleLDAPObject.search_ext_s(self, str(base), scope, filterstr, attrlist, attrsonly, + serverctrls, clientctrls, timeout, sizelimit) + + def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): + return SimpleLDAPObject.search_s(self, str(base), scope, filterstr, attrlist, attrsonly) + + def search_st(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0, timeout=-1): + return SimpleLDAPObject.search_st(self, str(base), scope, filterstr, attrlist, attrsonly, timeout) + + def search_subschemasubentry_s(self, dn=''): + return SimpleLDAPObject.search_subschemasubentry_s(self, str(dn)) + # universal LDAPError handler def _handle_errors(e, **kw): """ @@ -180,7 +284,7 @@ def get_schema(url, conn=None): raise StandardError('Unable to retrieve LDAP schema. Error initializing principal %s in %s: %s' % (principal.name, '/etc/httpd/conf/ipa.keytab', str(e))) if conn is None: - conn = _ldap.initialize(url) + conn = IPASimpleLDAPObject(url) if url.startswith('ldapi://'): conn.set_option(_ldap.OPT_HOST_NAME, api.env.host) conn.sasl_interactive_bind_s('', SASL_AUTH) @@ -362,7 +466,7 @@ class ldap2(CrudBackend, Encoder): conn.simple_bind_s(bind_dn, bind_pw) except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) if _schema: object.__setattr__(self, 'schema', _schema) @@ -450,7 +554,7 @@ class ldap2(CrudBackend, Encoder): try: self.conn.add_s(dn, list(entry_attrs.iteritems())) except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) # generating filters for find_entry # some examples: @@ -624,7 +728,7 @@ class ldap2(CrudBackend, Encoder): _ldap.SIZELIMIT_EXCEEDED), e: truncated = True except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) if not res: raise errors.NotFound(reason='no such entry') @@ -843,7 +947,7 @@ class ldap2(CrudBackend, Encoder): self.conn.rename_s(dn, new_rdn, delold=int(del_old)) time.sleep(.3) # Give memberOf plugin a chance to work except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) def _generate_modlist(self, dn, entry_attrs, normalize): # get original entry @@ -911,7 +1015,7 @@ class ldap2(CrudBackend, Encoder): try: self.conn.modify_s(dn, modlist) except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) @encode_args(1) def delete_entry(self, dn, normalize=True): @@ -921,7 +1025,7 @@ class ldap2(CrudBackend, Encoder): try: self.conn.delete_s(dn) except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) @encode_args(1, 2, 3) def modify_password(self, dn, new_pass, old_pass=''): @@ -941,7 +1045,7 @@ class ldap2(CrudBackend, Encoder): try: self.conn.passwd_s(dn, old_pass, new_pass) except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) def add_entry_to_group(self, dn, group_dn, member_attr='member', allow_same=False): """ @@ -1156,7 +1260,7 @@ class ldap2(CrudBackend, Encoder): try: self.conn.modify_s(dn, mod) except _ldap.LDAPError, e: - _handle_errors(e, **{}) + _handle_errors(e) # CrudBackend methods |