Hi, the attached patches remove IPASimpleLDAPObject from ipaldap.
As a result, the one and only IPA LDAP API is the LDAPClient API. Honza -- Jan Cholasta
>From 89ffd2ed1a058f5d56129dd2b9f62383406898dc Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Thu, 23 Oct 2014 15:44:40 +0200 Subject: [PATCH 01/16] ldap: Drop python-ldap tuple compatibility --- ipapython/ipaldap.py | 69 +++------------------------------------------------- 1 file changed, 3 insertions(+), 66 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index ce07006..5ba2b8b 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -90,12 +90,6 @@ def value_to_utf8(val): return unicode(val).encode('utf-8') -# FIXME: Remove when python-ldap tuple compatibility is dropped -def raise_deprecation_error(): - raise RuntimeError( - "this API has been deprecated, see http://www.freeipa.org/page/" - "HowTo/Migrate_your_code_to_the_new_LDAP_API") - class _ServerSchema(object): ''' Properties of a schema retrieved from an LDAP server. @@ -670,13 +664,6 @@ class IPASimpleLDAPObject(object): return self.conn.unbind_s() -# Make python-ldap tuple style result compatible with Entry and Entity -# objects by allowing access to the dn (tuple index 0) via the 'dn' -# attribute name and the attr dict (tuple index 1) via the 'data' -# attribute name. Thus: -# r = result[0] -# r[0] == r.dn -# r[1] == r.data class LDAPEntry(collections.MutableMapping): __slots__ = ('_conn', '_dn', '_names', '_nice', '_raw', '_sync', '_not_list', '_orig', '_raw_view', '_single_value_view') @@ -935,10 +922,6 @@ class LDAPEntry(collections.MutableMapping): return value def __getitem__(self, name): - # FIXME: Remove when python-ldap tuple compatibility is dropped - if name in (0, 1): - raise_deprecation_error() - return self._get_nice(name) def __delitem__(self, name): @@ -1023,49 +1006,9 @@ class LDAPEntry(collections.MutableMapping): return modlist - # FIXME: Remove when python-ldap tuple compatibility is dropped def __iter__(self): - raise_deprecation_error() - - # FIXME: Remove when python-ldap tuple compatibility is dropped - def iterkeys(self): - return self._nice.iterkeys() - - # FIXME: Remove when python-ldap tuple compatibility is dropped - def itervalues(self): - for name in self.iterkeys(): - yield self[name] - - # FIXME: Remove when python-ldap tuple compatibility is dropped - def iteritems(self): - for name in self.iterkeys(): - yield (name, self[name]) - - # FIXME: Remove when python-ldap tuple compatibility is dropped - def keys(self): - return list(self.iterkeys()) - - # FIXME: Remove when python-ldap tuple compatibility is dropped - def values(self): - return list(self.itervalues()) + return iter(self._nice) - # FIXME: Remove when python-ldap tuple compatibility is dropped - def items(self): - return list(self.iteritems()) - - # FIXME: Remove when python-ldap tuple compatibility is dropped - def update(self, _obj={}, **kwargs): - _obj = dict(_obj, **kwargs) - super(LDAPEntry, self).update(_obj) - - # FIXME: Remove when python-ldap tuple compatibility is dropped - def popitem(self): - try: - name = self.iterkeys().next() - except StopIteration: - raise KeyError - - return name, self.pop(name) class LDAPEntryView(collections.MutableMapping): __slots__ = ('_entry',) @@ -1577,14 +1520,11 @@ class LDAPClient(object): raise errors.LimitsExceeded() return entries[0] - def add_entry(self, entry, entry_attrs=None): + def add_entry(self, entry): """Create a new entry. This should be called as add_entry(entry). """ - if entry_attrs is not None: - raise_deprecation_error() - # remove all [] values (python-ldap hates 'em) attrs = dict((k, v) for k, v in entry.raw.iteritems() if v) @@ -1610,14 +1550,11 @@ class LDAPClient(object): self.conn.rename_s(dn, new_rdn, delold=int(del_old)) time.sleep(.3) # Give memberOf plugin a chance to work - def update_entry(self, entry, entry_attrs=None): + def update_entry(self, entry): """Update entry's attributes. This should be called as update_entry(entry). """ - if entry_attrs is not None: - raise_deprecation_error() - # generate modlist modlist = entry.generate_modlist() if not modlist: -- 2.1.0
>From ed8885113bc54c75df8979f4da33b49065320ed4 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Wed, 21 Jan 2015 11:14:19 +0000 Subject: [PATCH 02/16] ldap: Remove unused IPAdmin methods --- ipapython/ipaldap.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 5ba2b8b..d4beaee 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -1720,13 +1720,5 @@ class IPAdmin(LDAPClient): # FIXME: for backwards compatibility only return self.conn.modify_s(*args, **kwargs) - def set_option(self, *args, **kwargs): - # FIXME: for backwards compatibility only - return self.conn.set_option(*args, **kwargs) - - def encode(self, *args, **kwargs): - # FIXME: for backwards compatibility only - return self.conn.encode(*args, **kwargs) - def unbind(self, *args, **kwargs): return self.conn.unbind_s(*args, **kwargs) -- 2.1.0
>From cd03fdfdefddaee108db774a261ed917415139ec Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Mon, 26 Jan 2015 15:16:01 +0000 Subject: [PATCH 03/16] ldap: Add connection management to LDAPClient --- ipapython/ipaldap.py | 71 ++++++++++++++++++++++++++++++++++++++++------ ipaserver/plugins/ldap2.py | 5 +++- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index d4beaee..8153fca 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -1083,13 +1083,22 @@ class LDAPClient(object): SCOPE_ONELEVEL = ldap.SCOPE_ONELEVEL SCOPE_SUBTREE = ldap.SCOPE_SUBTREE - def __init__(self, ldap_uri): + def __init__(self, ldap_uri, start_tls=False, force_schema_updates=False, + no_schema=False, decode_attrs=True): self.ldap_uri = ldap_uri + self._start_tls = start_tls + self._force_schema_updates = force_schema_updates + self._no_schema = no_schema + self._decode_attrs = decode_attrs + self.log = log_mgr.get_logger(self) - self._init_connection() + self._conn = None - def _init_connection(self): - self.conn = None + self._connect() + + @property + def conn(self): + return self._conn @contextlib.contextmanager def error_handler(self, arg_desc=None): @@ -1186,6 +1195,44 @@ class LDAPClient(object): reason=_('objectclass %s not found') % oc) return [unicode(a).lower() for a in list(set(allowed_attributes))] + def __del__(self): + self.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def close(self): + """ + Close the connection. + """ + if self._conn is not None: + self._disconnect() + + def _connect(self): + if self._conn is not None: + raise errors.DatabaseError( + desc="Can't connect to server", info="Already connected") + + with self.error_handler(): + object.__setattr__(self, '_conn', + IPASimpleLDAPObject(self.ldap_uri, + self._force_schema_updates, + self._no_schema, + self._decode_attrs)) + + if self._start_tls: + self._conn.start_tls_s() + + def _disconnect(self): + if self._conn is None: + raise errors.DatabaseError( + desc="Can't disconnect from server", info="Not connected") + + object.__setattr__(self, '_conn', None) + def make_dn_from_attr(self, attr, value, parent_dn=None): """ Make distinguished name from attribute. @@ -1626,7 +1673,7 @@ class IPAdmin(LDAPClient): realm=None, protocol=None, force_schema_updates=True, start_tls=False, ldap_uri=None, no_schema=False, decode_attrs=True, sasl_nocanon=False, demand_cert=False): - self.conn = None + self._conn = None log_mgr.get_logger(self, True) if debug and debug.lower() == "on": ldap.set_option(ldap.OPT_DEBUG_LEVEL,255) @@ -1646,10 +1693,10 @@ class IPAdmin(LDAPClient): LDAPClient.__init__(self, ldap_uri) with self.error_handler(): - self.conn = IPASimpleLDAPObject(ldap_uri, - force_schema_updates=True, - no_schema=no_schema, - decode_attrs=decode_attrs) + self._conn = IPASimpleLDAPObject(ldap_uri, + force_schema_updates=True, + no_schema=no_schema, + decode_attrs=decode_attrs) if demand_cert: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, True) @@ -1661,6 +1708,12 @@ class IPAdmin(LDAPClient): if start_tls: self.conn.start_tls_s() + def _connect(self): + pass + + def _disconnect(self): + pass + def __str__(self): return self.host + ":" + str(self.port) diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index fd4ed29..2671571 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -98,11 +98,14 @@ class ldap2(LDAPClient, CrudBackend): except AttributeError: return DN() - def _init_connection(self): + def _connect(self): # Connectible.conn is a proxy to thread-local storage; # do not set it pass + def _disconnect(self): + pass + def __del__(self): if self.isconnected(): self.disconnect() -- 2.1.0
>From 5806b40f07893fe65dce3f93e30471c7b7cab6eb Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Fri, 21 Nov 2014 19:45:34 +0100 Subject: [PATCH 04/16] ldap: Use LDAPClient connection management in IPAdmin --- ipapython/ipaldap.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 8153fca..b800e24 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -1690,14 +1690,11 @@ class IPAdmin(LDAPClient): if not ldap_uri: ldap_uri = self.__get_ldap_uri(protocol or self.__guess_protocol()) - LDAPClient.__init__(self, ldap_uri) + super(IPAdmin, self).__init__( + ldap_uri, force_schema_updates=force_schema_updates, + no_schema=no_schema, decode_attrs=decode_attrs) with self.error_handler(): - self._conn = IPASimpleLDAPObject(ldap_uri, - force_schema_updates=True, - no_schema=no_schema, - decode_attrs=decode_attrs) - if demand_cert: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, True) self.conn.set_option(ldap.OPT_X_TLS_DEMAND, True) @@ -1708,12 +1705,6 @@ class IPAdmin(LDAPClient): if start_tls: self.conn.start_tls_s() - def _connect(self): - pass - - def _disconnect(self): - pass - def __str__(self): return self.host + ":" + str(self.port) -- 2.1.0
>From 4c6bc62b0617d1f2dd506156dce642974acfb097 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Fri, 21 Nov 2014 20:03:29 +0100 Subject: [PATCH 05/16] ldap: Use LDAPClient connection management in ldap2 --- ipaserver/plugins/ldap2.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index 2671571..98b038a 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -145,10 +145,12 @@ class ldap2(LDAPClient, CrudBackend): if debug_level: _ldap.set_option(_ldap.OPT_DEBUG_LEVEL, debug_level) + object.__setattr__(self, '_force_schema_updates', + self.api.env.context in ('installer', 'updates')) + LDAPClient._connect(self) + conn = self._conn + with self.error_handler(): - force_updates = self.api.env.context in ('installer', 'updates') - conn = IPASimpleLDAPObject( - self.ldap_uri, force_schema_updates=force_updates) if self.ldap_uri.startswith('ldapi://') and ccache: conn.set_option(_ldap.OPT_HOST_NAME, self.api.env.host) minssf = conn.get_option(_ldap.OPT_X_SASL_SSF_MIN) @@ -200,6 +202,11 @@ class ldap2(LDAPClient, CrudBackend): # ignore when trying to unbind multiple times pass + try: + LDAPClient._disconnect(self) + except errors.PublicError: + # ignore when trying to unbind multiple times + pass def find_entries(self, filter=None, attrs_list=None, base_dn=None, scope=_ldap.SCOPE_SUBTREE, time_limit=None, -- 2.1.0
>From 7c1395875d4b3ad104e4a83698b89573d7d08b99 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Fri, 21 Nov 2014 20:08:17 +0100 Subject: [PATCH 06/16] ldap: Add bind and unbind methods to LDAPClient --- ipapython/ipaldap.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index b800e24..acc35cf 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -1233,6 +1233,41 @@ class LDAPClient(object): object.__setattr__(self, '_conn', None) + def simple_bind(self, bind_dn, bind_password, server_controls=None, + client_controls=None): + """ + Perform simple bind operation. + """ + with self.error_handler(): + self._conn.simple_bind_s( + bind_dn, bind_password, server_controls, client_controls) + + def external_bind(self, user_name, server_controls=None, + client_controls=None): + """ + Perform SASL bind operation using the SASL EXTERNAL mechanism. + """ + with self.error_handler(): + auth_tokens = ldap.sasl.external(user_name) + self._conn.sasl_interactive_bind_s( + None, auth_tokens, server_controls, client_controls) + + def gssapi_bind(self, server_controls=None, client_controls=None): + """ + Perform SASL bind operation using the SASL GSSAPI mechanism. + """ + with self.error_handler(): + auth_tokens = ldap.sasl.sasl({}, 'GSSAPI') + self._conn.sasl_interactive_bind_s( + None, auth_tokens, server_controls, client_controls) + + def unbind(self): + """ + Perform unbind operation. + """ + with self.error_handler(): + self.conn.unbind_s() + def make_dn_from_attr(self, attr, value, parent_dn=None): """ Make distinguished name from attribute. -- 2.1.0
>From bbd66aabb1dc26029e3965f3df3d625778090019 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Fri, 21 Nov 2014 20:10:23 +0100 Subject: [PATCH 07/16] ldap: Use LDAPClient bind and unbind methods in IPAdmin --- ipapython/ipaldap.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index acc35cf..7d980a6 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -1752,29 +1752,29 @@ class IPAdmin(LDAPClient): wait_for_open_ports(host, int(port), timeout) def __bind_with_wait(self, bind_func, timeout, *args, **kwargs): - with self.error_handler(): - try: - bind_func(*args, **kwargs) - except (ldap.CONNECT_ERROR, ldap.SERVER_DOWN), e: - if not timeout or 'TLS' in e.args[0].get('info', ''): - # No connection to continue on if we have a TLS failure - # https://bugzilla.redhat.com/show_bug.cgi?id=784989 - raise - self.__wait_for_connection(timeout) - bind_func(*args, **kwargs) + try: + bind_func(*args, **kwargs) + except errors.NetworkError as e: + if not timeout and 'TLS' in e.error: + # No connection to continue on if we have a TLS failure + # https://bugzilla.redhat.com/show_bug.cgi?id=784989 + raise + except errors.DatabaseError: + pass + else: + return + self.__wait_for_connection(timeout) + bind_func(*args, **kwargs) def do_simple_bind(self, binddn=DN(('cn', 'directory manager')), bindpw="", timeout=DEFAULT_TIMEOUT): - self.__bind_with_wait(self.conn.simple_bind_s, timeout, binddn, bindpw) + self.__bind_with_wait(self.simple_bind, timeout, binddn, bindpw) def do_sasl_gssapi_bind(self, timeout=DEFAULT_TIMEOUT): - self.__bind_with_wait( - self.conn.sasl_interactive_bind_s, timeout, None, SASL_GSSAPI) + self.__bind_with_wait(self.gssapi_bind, timeout) def do_external_bind(self, user_name=None, timeout=DEFAULT_TIMEOUT): - auth_tokens = ldap.sasl.external(user_name) - self.__bind_with_wait( - self.conn.sasl_interactive_bind_s, timeout, None, auth_tokens) + self.__bind_with_wait(self.external_bind, timeout, user_name) def do_bind(self, dm_password="", autobind=AUTOBIND_AUTO, timeout=DEFAULT_TIMEOUT): if dm_password: @@ -1798,6 +1798,3 @@ class IPAdmin(LDAPClient): def modify_s(self, *args, **kwargs): # FIXME: for backwards compatibility only return self.conn.modify_s(*args, **kwargs) - - def unbind(self, *args, **kwargs): - return self.conn.unbind_s(*args, **kwargs) -- 2.1.0
>From 4013fd501f4ac3fe8da96b97e5694d30d0ef3e91 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Fri, 21 Nov 2014 20:14:12 +0100 Subject: [PATCH 08/16] ldap: Use LDAPClient bind and unbind methods in ldap2 --- ipaserver/plugins/ldap2.py | 62 +++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index 98b038a..1e103dc 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -162,47 +162,41 @@ class ldap2(LDAPClient, CrudBackend): conn.set_option(_ldap.OPT_X_SASL_SSF_MIN, minssf) if maxssf < minssf: conn.set_option(_ldap.OPT_X_SASL_SSF_MAX, minssf) - if ccache is not None: - if isinstance(ccache, krbV.CCache): - principal = ccache.principal().name - # Get a fully qualified CCACHE name (schema+name) - # As we do not use the krbV.CCache object later, - # we can safely overwrite it - ccache = "%(type)s:%(name)s" % dict(type=ccache.type, - name=ccache.name) - else: - principal = krbV.CCache(name=ccache, - context=krbV.default_context()).principal().name - - os.environ['KRB5CCNAME'] = ccache - conn.sasl_interactive_bind_s(None, SASL_GSSAPI, - serverctrls=serverctrls, - clientctrls=clientctrls) - setattr(context, 'principal', principal) + + if ccache is not None: + if isinstance(ccache, krbV.CCache): + principal = ccache.principal().name + # Get a fully qualified CCACHE name (schema+name) + # As we do not use the krbV.CCache object later, + # we can safely overwrite it + ccache = "%(type)s:%(name)s" % dict(type=ccache.type, + name=ccache.name) else: - # no kerberos ccache, use simple bind or external sasl - if autobind: - pent = pwd.getpwuid(os.geteuid()) - auth_tokens = _ldap.sasl.external(pent.pw_name) - conn.sasl_interactive_bind_s(None, auth_tokens, - serverctrls=serverctrls, - clientctrls=clientctrls) - else: - conn.simple_bind_s(bind_dn, bind_pw, - serverctrls=serverctrls, - clientctrls=clientctrls) + principal = krbV.CCache(name=ccache, + context=krbV.default_context()).principal().name + + os.environ['KRB5CCNAME'] = ccache + self.gssapi_bind(server_controls=serverctrls, + client_controls=clientctrls) + setattr(context, 'principal', principal) + else: + # no kerberos ccache, use simple bind or external sasl + if autobind: + pent = pwd.getpwuid(os.geteuid()) + self.external_bind(pent.pw_name, + server_controls=serverctrls, + client_controls=clientctrls) + else: + self.simple_bind(bind_dn, bind_pw, + server_controls=serverctrls, + client_controls=clientctrls) return conn def destroy_connection(self): """Disconnect from LDAP server.""" try: - self.conn.unbind_s() - except _ldap.LDAPError: - # ignore when trying to unbind multiple times - pass - - try: + self.unbind() LDAPClient._disconnect(self) except errors.PublicError: # ignore when trying to unbind multiple times -- 2.1.0
>From 9d7232c05042623633fd350c480e1216bc4de42d Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Wed, 8 Apr 2015 11:31:15 +0000 Subject: [PATCH 09/16] ldap: Use LDAPClient instead of IPASimpleLDAPObject in ldap2.modify_password --- ipaserver/plugins/ldap2.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index 1e103dc..819382a 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -34,7 +34,7 @@ import krbV import ldap as _ldap from ipapython.dn import DN -from ipapython.ipaldap import SASL_GSSAPI, IPASimpleLDAPObject, LDAPClient +from ipapython.ipaldap import SASL_GSSAPI, LDAPClient try: @@ -471,11 +471,10 @@ class ldap2(LDAPClient, CrudBackend): pw = old_pass if (otp): pw = old_pass+otp - with self.error_handler(): - conn = IPASimpleLDAPObject( - self.ldap_uri, force_schema_updates=False) - conn.simple_bind_s(dn, pw) - conn.unbind_s() + + conn = LDAPClient(self.ldap_uri, force_schema_updates=False) + conn.simple_bind(dn, pw) + conn.unbind() with self.error_handler(): self.conn.passwd_s(dn, old_pass, new_pass) -- 2.1.0
>From b26aeba9d57deb0398b5e25c40f8a48bfc5eedd9 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Wed, 8 Apr 2015 11:32:21 +0000 Subject: [PATCH 10/16] cainstance: Use LDAPClient instead of IPASimpleLDAPObject --- ipaserver/install/cainstance.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 8ba6e46..f4d5b2e 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -1643,11 +1643,10 @@ def replica_ca_install_check(config): objectclass = 'ipaObject' root_logger.debug('Checking if IPA schema is present in %s', ca_ldap_url) try: - connection = ldap2.IPASimpleLDAPObject( - ca_ldap_url, force_schema_updates=False) - connection.start_tls_s() - connection.simple_bind_s(DN(('cn', 'Directory Manager')), - config.dirman_password) + connection = ipaldap.LDAPClient( + ca_ldap_url, start_tls=True, force_schema_updates=False) + connection.simple_bind(DN(('cn', 'Directory Manager')), + config.dirman_password) rschema = connection.schema result = rschema.get_obj(ldap.schema.models.ObjectClass, objectclass) except Exception: -- 2.1.0
>From a0eff23f4592f65798af72b109101979c8d45d59 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Wed, 8 Apr 2015 11:32:54 +0000 Subject: [PATCH 11/16] makeaci: Use LDAPClient instead of IPASimpleLDAPObject --- makeaci | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/makeaci b/makeaci index c4c6f8a..ab65a99 100755 --- a/makeaci +++ b/makeaci @@ -30,16 +30,7 @@ from argparse import ArgumentParser from ipalib import api from ipapython.dn import DN -from ipapython.ipaldap import LDAPEntry, IPASimpleLDAPObject, LDAPClient - - -class FakeLDAPClient(LDAPClient): - """A LDAP client that can't do any LDAP operations - - Used to create and manipulate entries without an LDAP connection. - """ - def _init_connection(self): - self.conn = IPASimpleLDAPObject('', False, no_schema=True) +from ipapython.ipaldap import LDAPEntry, LDAPClient def parse_options(): @@ -57,7 +48,7 @@ def generate_aci_lines(api): """Yields ACI lines as they appear in ACI.txt, with trailing newline""" update_plugin = api.Updater['update_managed_permissions'] perm_plugin = api.Object['permission'] - fake_ldap = FakeLDAPClient('') + fake_ldap = LDAPClient('', force_schema_updates=False, no_schema=True) for name, template, obj in update_plugin.get_templates(): dn = perm_plugin.get_dn(name) entry = fake_ldap.make_entry(dn) -- 2.1.0
>From 7cf0887f58f22536227790da6b0a9e87ec5f6e7d Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Wed, 14 Jan 2015 15:51:52 +0000 Subject: [PATCH 12/16] ldap: Move value encoding from IPASimpleLDAPObject to LDAPClient --- ipapython/ipaldap.py | 208 +++++++++++++++++---------------------------- ipaserver/plugins/ldap2.py | 22 +++-- 2 files changed, 94 insertions(+), 136 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 7d980a6..34f42bc 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -462,153 +462,64 @@ class IPASimpleLDAPObject(object): else: raise TypeError("attempt to pass unsupported type from ldap, value=%s type=%s" %(val, type(val))) - def convert_result(self, result): - ''' - result is a python-ldap result tuple of the form (dn, attrs), - where dn is a string containing the dn (distinguished name) of - the entry, and attrs is a dictionary containing the attributes - associated with the entry. The keys of attrs are strings, and - the associated values are lists of strings. - - We convert the tuple to an LDAPEntry object. - ''' - - ipa_result = [] - for dn_tuple in result: - original_dn = dn_tuple[0] - original_attrs = dn_tuple[1] - - # original_dn is None if referral instead of an entry was - # returned from the LDAP server, we need to skip this item - if original_dn is None: - log_msg = 'Referral entry ignored: {ref}'\ - .format(ref=str(original_attrs)) - self.log.debug(log_msg) - - continue - - ipa_entry = LDAPEntry(self, DN(original_dn)) - - for attr, original_values in original_attrs.items(): - ipa_entry.raw[attr] = original_values - ipa_entry.reset_modlist() - - ipa_result.append(ipa_entry) - - if _debug_log_ldap: - self.log.debug('ldap.result: %s', ipa_result) - return ipa_result - #---------- python-ldap emulations ---------- def add(self, dn, modlist): - assert isinstance(dn, DN) - dn = str(dn) - modlist = self.encode(modlist) return self.conn.add(dn, modlist) def add_ext(self, dn, modlist, serverctrls=None, clientctrls=None): - assert isinstance(dn, DN) - dn = str(dn) - modlist = self.encode(modlist) return self.conn.add_ext(dn, modlist, serverctrls, clientctrls) def add_ext_s(self, dn, modlist, serverctrls=None, clientctrls=None): - assert isinstance(dn, DN) - dn = str(dn) - modlist = self.encode(modlist) return self.conn.add_ext_s(dn, modlist, serverctrls, clientctrls) def add_s(self, dn, modlist): - assert isinstance(dn, DN) - dn = str(dn) - modlist = self.encode(modlist) return self.conn.add_s(dn, modlist) def bind(self, who, cred, method=ldap.AUTH_SIMPLE): self.flush_cached_schema() - if who is None: - who = DN() - assert isinstance(who, DN) - who = str(who) - cred = self.encode(cred) return self.conn.bind(who, cred, method) def delete(self, dn): - assert isinstance(dn, DN) - dn = str(dn) return self.conn.delete(dn) def delete_s(self, dn): - assert isinstance(dn, DN) - dn = str(dn) return self.conn.delete_s(dn) def get_option(self, option): return self.conn.get_option(option) def modify_s(self, dn, modlist): - assert isinstance(dn, DN) - dn = str(dn) - modlist = [(x[0], self.encode(x[1]), self.encode(x[2])) for x in modlist] return self.conn.modify_s(dn, modlist) def modrdn_s(self, dn, newrdn, delold=1): - assert isinstance(dn, DN) - dn = str(dn) - assert isinstance(newrdn, (DN, RDN)) - newrdn = str(newrdn) return self.conn.modrdn_s(dn, newrdn, delold) def passwd_s(self, dn, oldpw, newpw, serverctrls=None, clientctrls=None): - assert isinstance(dn, DN) - dn = str(dn) - oldpw = self.encode(oldpw) - newpw = self.encode(newpw) return self.conn.passwd_s(dn, oldpw, newpw, serverctrls, clientctrls) def rename_s(self, dn, newrdn, newsuperior=None, delold=1): # NOTICE: python-ldap of version 2.3.10 and lower does not support # serverctrls and clientctrls for rename_s operation. Thus, these # options are ommited from this command until really needed - assert isinstance(dn, DN) - dn = str(dn) - assert isinstance(newrdn, (DN, RDN)) - newrdn = str(newrdn) return self.conn.rename_s(dn, newrdn, newsuperior, delold) def result(self, msgid=ldap.RES_ANY, all=1, timeout=None): - resp_type, resp_data = self.conn.result(msgid, all, timeout) - resp_data = self.convert_result(resp_data) - return resp_type, resp_data + return self.conn.result(msgid, all, timeout) def result3(self, msgid=ldap.RES_ANY, all=1, timeout=None): - rtype, rdata, rmsgid, rctrls = self.conn.result3(msgid, all, timeout) - rdata = self.convert_result(rdata) - return rtype, rdata, rmsgid, rctrls + return self.conn.result3(msgid, all, timeout) def sasl_interactive_bind_s(self, who, auth, serverctrls=None, clientctrls=None, sasl_flags=ldap.SASL_QUIET): self.flush_cached_schema() - if who is None: - who = DN() - assert isinstance(who, DN) - who = str(who) - return self.conn.sasl_interactive_bind_s(who, auth, serverctrls, clientctrls, sasl_flags) + return self.conn.sasl_interactive_bind_s(who, auth, serverctrls, + clientctrls, sasl_flags) def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): - assert isinstance(base, DN) - base = str(base) - filterstr = self.encode(filterstr) - attrlist = self.encode(attrlist) return self.conn.search(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): - assert isinstance(base, DN) - base = str(base) - filterstr = self.encode(filterstr) - attrlist = self.encode(attrlist) - if _debug_log_ldap: self.log.debug( "ldap.search_ext: dn: %s\nfilter: %s\nattrs_list: %s", @@ -618,42 +529,23 @@ class IPASimpleLDAPObject(object): return self.conn.search_ext(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): - assert isinstance(base, DN) - base = str(base) - filterstr = self.encode(filterstr) - attrlist = self.encode(attrlist) - ldap_result = self.conn.search_ext_s(base, scope, filterstr, attrlist, attrsonly, serverctrls, clientctrls, timeout, sizelimit) - ipa_result = self.convert_result(ldap_result) - return ipa_result + return self.conn.search_ext_s(base, scope, filterstr, attrlist, + attrsonly, serverctrls, clientctrls, + timeout, sizelimit) def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): - assert isinstance(base, DN) - base = str(base) - filterstr = self.encode(filterstr) - attrlist = self.encode(attrlist) - ldap_result = self.conn.search_s(base, scope, filterstr, attrlist, attrsonly) - ipa_result = self.convert_result(ldap_result) - return ipa_result + return self.conn.search_s(base, scope, filterstr, attrlist, + attrsonly) def search_st(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0, timeout=-1): - assert isinstance(base, DN) - base = str(base) - filterstr = self.encode(filterstr) - attrlist = self.encode(attrlist) - ldap_result = self.conn.search_st(base, scope, filterstr, attrlist, attrsonly, timeout) - ipa_result = self.convert_result(ldap_result) - return ipa_result + return self.conn.search_st(base, scope, filterstr, attrlist, attrsonly, + timeout) def set_option(self, option, invalue): return self.conn.set_option(option, invalue) def simple_bind_s(self, who=None, cred='', serverctrls=None, clientctrls=None): self.flush_cached_schema() - if who is None: - who = DN() - assert isinstance(who, DN) - who = str(who) - cred = self.encode(cred) return self.conn.simple_bind_s(who, cred, serverctrls, clientctrls) def start_tls_s(self): @@ -1100,6 +992,49 @@ class LDAPClient(object): def conn(self): return self._conn + def encode(self, val): + return self.conn.encode(val) + + def decode(self, val, attr): + return self.conn.decode(val, attr) + + def _convert_result(self, result): + ''' + result is a python-ldap result tuple of the form (dn, attrs), + where dn is a string containing the dn (distinguished name) of + the entry, and attrs is a dictionary containing the attributes + associated with the entry. The keys of attrs are strings, and + the associated values are lists of strings. + + We convert the tuple to an LDAPEntry object. + ''' + + ipa_result = [] + for dn_tuple in result: + original_dn = dn_tuple[0] + original_attrs = dn_tuple[1] + + # original_dn is None if referral instead of an entry was + # returned from the LDAP server, we need to skip this item + if original_dn is None: + log_msg = 'Referral entry ignored: {ref}'\ + .format(ref=str(original_attrs)) + self.log.debug(log_msg) + + continue + + ipa_entry = LDAPEntry(self.conn, DN(original_dn)) + + for attr, original_values in original_attrs.items(): + ipa_entry.raw[attr] = original_values + ipa_entry.reset_modlist() + + ipa_result.append(ipa_entry) + + if _debug_log_ldap: + self.log.debug('ldap.result: %s', ipa_result) + return ipa_result + @contextlib.contextmanager def error_handler(self, arg_desc=None): """Context manager that handles LDAPErrors @@ -1239,6 +1174,11 @@ class LDAPClient(object): Perform simple bind operation. """ with self.error_handler(): + if bind_dn is None: + bind_dn = DN() + assert isinstance(bind_dn, DN) + bind_dn = str(bind_dn) + bind_password = self.encode(bind_password) self._conn.simple_bind_s( bind_dn, bind_password, server_controls, client_controls) @@ -1250,7 +1190,7 @@ class LDAPClient(object): with self.error_handler(): auth_tokens = ldap.sasl.external(user_name) self._conn.sasl_interactive_bind_s( - None, auth_tokens, server_controls, client_controls) + '', auth_tokens, server_controls, client_controls) def gssapi_bind(self, server_controls=None, client_controls=None): """ @@ -1259,7 +1199,7 @@ class LDAPClient(object): with self.error_handler(): auth_tokens = ldap.sasl.sasl({}, 'GSSAPI') self._conn.sasl_interactive_bind_s( - None, auth_tokens, server_controls, client_controls) + '', auth_tokens, server_controls, client_controls) def unbind(self): """ @@ -1499,19 +1439,23 @@ class LDAPClient(object): # pass arguments to python-ldap with self.error_handler(): + filter = self.encode(filter) + attrs_list = self.encode(attrs_list) + while True: if paged_search: sctrls = [SimplePagedResultsControl(0, page_size, cookie)] try: id = self.conn.search_ext( - base_dn, scope, filter, attrs_list, + str(base_dn), scope, filter, attrs_list, serverctrls=sctrls, timeout=time_limit, sizelimit=size_limit ) while True: result = self.conn.result3(id, 0) objtype, res_list, res_id, res_ctrls = result + res_list = self._convert_result(res_list) if not res_list: break if (objtype == ldap.RES_SEARCH_ENTRY or @@ -1533,7 +1477,7 @@ class LDAPClient(object): sctrls = [SimplePagedResultsControl(0, 0, cookie)] try: self.conn.search_ext_s( - base_dn, scope, filter, attrs_list, + str(base_dn), scope, filter, attrs_list, serverctrls=sctrls, timeout=time_limit, sizelimit=size_limit) except ldap.LDAPError, e: @@ -1611,7 +1555,8 @@ class LDAPClient(object): attrs = dict((k, v) for k, v in entry.raw.iteritems() if v) with self.error_handler(): - self.conn.add_s(entry.dn, attrs.items()) + attrs = self.encode(attrs) + self.conn.add_s(str(entry.dn), attrs.items()) entry.reset_modlist() @@ -1629,7 +1574,7 @@ class LDAPClient(object): if dn[0] == new_rdn: raise errors.EmptyModlist() with self.error_handler(): - self.conn.rename_s(dn, new_rdn, delold=int(del_old)) + self.conn.rename_s(str(dn), str(new_rdn), delold=int(del_old)) time.sleep(.3) # Give memberOf plugin a chance to work def update_entry(self, entry): @@ -1644,7 +1589,9 @@ class LDAPClient(object): # pass arguments to python-ldap with self.error_handler(): - self.conn.modify_s(entry.dn, modlist) + modlist = [(a, self.encode(b), self.encode(c)) + for a, b, c in modlist] + self.conn.modify_s(str(entry.dn), modlist) entry.reset_modlist() @@ -1656,7 +1603,7 @@ class LDAPClient(object): dn = entry_or_dn.dn with self.error_handler(): - self.conn.delete_s(dn) + self.conn.delete_s(str(dn)) def entry_exists(self, dn): """ @@ -1795,6 +1742,9 @@ class IPAdmin(LDAPClient): #fall back self.do_sasl_gssapi_bind(timeout=timeout) - def modify_s(self, *args, **kwargs): + def modify_s(self, dn, modlist): # FIXME: for backwards compatibility only - return self.conn.modify_s(*args, **kwargs) + assert isinstance(dn, DN) + dn = str(dn) + modlist = [(a, self.encode(b), self.encode(c)) for a, b, c in modlist] + return self.conn.modify_s(dn, modlist) diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index 819382a..3bb0369 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -360,9 +360,11 @@ class ldap2(LDAPClient, CrudBackend): ('cn', 'etc'), self.api.env.basedn) try: - upg_entries = self.conn.search_s(upg_dn, _ldap.SCOPE_BASE, - attrlist=['*']) - except _ldap.NO_SUCH_OBJECT: + with self.error_handler(): + upg_entries = self.conn.search_s(str(upg_dn), _ldap.SCOPE_BASE, + attrlist=['*']) + upg_entries = self._convert_result(upg_entries) + except errors.NotFound: upg_entries = None if not upg_entries or 'originfilter' not in upg_entries[0]: raise errors.ACIError(info=_( @@ -477,7 +479,9 @@ class ldap2(LDAPClient, CrudBackend): conn.unbind() with self.error_handler(): - self.conn.passwd_s(dn, old_pass, new_pass) + old_pass = self.encode(old_pass) + new_pass = self.encode(new_pass) + self.conn.passwd_s(str(dn), old_pass, new_pass) def add_entry_to_group(self, dn, group_dn, member_attr='member', allow_same=False): """ @@ -509,7 +513,9 @@ class ldap2(LDAPClient, CrudBackend): # update group entry try: with self.error_handler(): - self.conn.modify_s(group_dn, modlist) + modlist = [(a, self.encode(b), self.encode(c)) + for a, b, c in modlist] + self.conn.modify_s(str(group_dn), modlist) except errors.DatabaseError: raise errors.AlreadyGroupMember() @@ -529,7 +535,9 @@ class ldap2(LDAPClient, CrudBackend): # update group entry try: with self.error_handler(): - self.conn.modify_s(group_dn, modlist) + modlist = [(a, self.encode(b), self.encode(c)) + for a, b, c in modlist] + self.conn.modify_s(str(group_dn), modlist) except errors.MidairCollision: raise errors.NotGroupMember() @@ -583,7 +591,7 @@ class ldap2(LDAPClient, CrudBackend): (_ldap.MOD_REPLACE, 'krblastpwdchange', None)] with self.error_handler(): - self.conn.modify_s(dn, mod) + self.conn.modify_s(str(dn), mod) # CrudBackend methods -- 2.1.0
>From ef3911fa83eb4eb6b60301dcba1c6392dcedcaea Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Wed, 14 Jan 2015 16:49:35 +0000 Subject: [PATCH 13/16] ldap: Use LDAPClient instead of IPASimpleLDAPObject in LDAPEntry --- ipapython/ipaldap.py | 11 +++++++---- ipatests/test_xmlrpc/test_baseldap_plugin.py | 8 +++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 34f42bc..f668aa5 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -571,7 +571,7 @@ class LDAPEntry(collections.MutableMapping): * LDAPEntry(dn, entry) - create a shallow copy of an existing LDAPEntry with a different DN. * LDAPEntry(conn, dn, mapping) - create a new LDAPEntry using the - specified IPASimpleLDAPObject and DN and optionally initialize + specified LDAPClient and DN and optionally initialize attributes from the specified mapping object. Keyword arguments can be used to override values of specific attributes. @@ -582,7 +582,7 @@ class LDAPEntry(collections.MutableMapping): assert _dn is None _dn = _conn _conn = _conn._conn - assert isinstance(_conn, IPASimpleLDAPObject) + assert isinstance(_conn, LDAPClient) if isinstance(_dn, LDAPEntry): assert _obj is None @@ -992,6 +992,9 @@ class LDAPClient(object): def conn(self): return self._conn + def get_single_value(self, attr): + return self.conn.get_single_value(attr) + def encode(self, val): return self.conn.encode(val) @@ -1023,7 +1026,7 @@ class LDAPClient(object): continue - ipa_entry = LDAPEntry(self.conn, DN(original_dn)) + ipa_entry = LDAPEntry(self, DN(original_dn)) for attr, original_values in original_attrs.items(): ipa_entry.raw[attr] = original_values @@ -1238,7 +1241,7 @@ class LDAPClient(object): return DN((primary_key, entry_attrs[primary_key]), parent_dn) def make_entry(self, _dn=None, _obj=None, **kwargs): - return LDAPEntry(self.conn, _dn, _obj, **kwargs) + return LDAPEntry(self, _dn, _obj, **kwargs) # generating filters for find_entry # some examples: diff --git a/ipatests/test_xmlrpc/test_baseldap_plugin.py b/ipatests/test_xmlrpc/test_baseldap_plugin.py index d5b3388..3ffc041 100644 --- a/ipatests/test_xmlrpc/test_baseldap_plugin.py +++ b/ipatests/test_xmlrpc/test_baseldap_plugin.py @@ -187,7 +187,13 @@ def test_entry_to_dict(): self._schema = FakeSchema() self._has_schema = True - conn = FakeIPASimpleLDAPObject() + class FakeLDAPClient(ipaldap.LDAPClient): + def __init__(self): + super(FakeLDAPClient, self).__init__('ldap://test', + force_schema_updates=False) + self._conn = FakeIPASimpleLDAPObject() + + conn = FakeLDAPClient() rights = {'nothing': 'is'} entry = ipaldap.LDAPEntry( -- 2.1.0
>From 97bb39d2001cec46a74440233c32f020d5503fa6 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Mon, 26 Jan 2015 16:33:50 +0000 Subject: [PATCH 14/16] ldap: Move schema handling from IPASimpleLDAPObject to LDAPClient --- ipalib/plugins/baseldap.py | 2 +- ipapython/ipaldap.py | 547 +++++++++++++-------------- ipatests/test_xmlrpc/test_baseldap_plugin.py | 9 +- 3 files changed, 266 insertions(+), 292 deletions(-) diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 4b1c701..85065c6 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -243,7 +243,7 @@ def entry_to_dict(entry, **options): for attr in entry.iterkeys(): if attr.lower() == 'attributelevelrights': value = entry[attr] - elif entry.conn.get_type(attr) is str: + elif entry.conn.get_attribute_type(attr) is str: value = entry.raw[attr] else: value = list(entry.raw[attr]) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index f668aa5..8ea2426 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -50,7 +50,6 @@ from ipapython.dnsutil import DNSName SASL_GSSAPI = ldap.sasl.sasl({}, 'GSSAPI') DEFAULT_TIMEOUT = 10 -DN_SYNTAX_OID = '1.3.6.1.4.1.1466.115.121.1.12' _debug_log_ldap = False _missing = object() @@ -190,277 +189,18 @@ schema_cache = SchemaCache() class IPASimpleLDAPObject(object): ''' - The purpose of this class is to provide a boundary between IPA and - python-ldap. In IPA we use IPA defined types because they are - richer and are designed to meet our needs. We also require that we - consistently use those types without exception. On the other hand - python-ldap uses different types. The goal is to be able to have - IPA code call python-ldap methods using the types native to - IPA. This class accomplishes that goal by exposing python-ldap - methods which take IPA types, convert them to python-ldap types, - call python-ldap, and then convert the results returned by - python-ldap into IPA types. - IPA code should never call python-ldap directly, it should only call python-ldap methods in this class. ''' - # Note: the oid for dn syntax is: 1.3.6.1.4.1.1466.115.121.1.12 - - _SYNTAX_MAPPING = { - '1.3.6.1.4.1.1466.115.121.1.1' : str, # ACI item - '1.3.6.1.4.1.1466.115.121.1.4' : str, # Audio - '1.3.6.1.4.1.1466.115.121.1.5' : str, # Binary - '1.3.6.1.4.1.1466.115.121.1.8' : str, # Certificate - '1.3.6.1.4.1.1466.115.121.1.9' : str, # Certificate List - '1.3.6.1.4.1.1466.115.121.1.10' : str, # Certificate Pair - '1.3.6.1.4.1.1466.115.121.1.23' : str, # Fax - '1.3.6.1.4.1.1466.115.121.1.28' : str, # JPEG - '1.3.6.1.4.1.1466.115.121.1.40' : str, # OctetString (same as Binary) - '1.3.6.1.4.1.1466.115.121.1.49' : str, # Supported Algorithm - '1.3.6.1.4.1.1466.115.121.1.51' : str, # Teletext Terminal Identifier - - DN_SYNTAX_OID : DN, # DN, member, memberof - '2.16.840.1.113730.3.8.3.3' : DN, # enrolledBy - '2.16.840.1.113730.3.8.3.18' : DN, # managedBy - '2.16.840.1.113730.3.8.3.5' : DN, # memberUser - '2.16.840.1.113730.3.8.3.7' : DN, # memberHost - '2.16.840.1.113730.3.8.3.20' : DN, # memberService - '2.16.840.1.113730.3.8.11.4' : DN, # ipaNTFallbackPrimaryGroup - '2.16.840.1.113730.3.8.11.21' : DN, # ipaAllowToImpersonate - '2.16.840.1.113730.3.8.11.22' : DN, # ipaAllowedTarget - '2.16.840.1.113730.3.8.7.1' : DN, # memberAllowCmd - '2.16.840.1.113730.3.8.7.2' : DN, # memberDenyCmd - - '2.16.840.1.113719.1.301.4.14.1' : DN, # krbRealmReferences - '2.16.840.1.113719.1.301.4.17.1' : DN, # krbKdcServers - '2.16.840.1.113719.1.301.4.18.1' : DN, # krbPwdServers - '2.16.840.1.113719.1.301.4.26.1' : DN, # krbPrincipalReferences - '2.16.840.1.113719.1.301.4.29.1' : DN, # krbAdmServers - '2.16.840.1.113719.1.301.4.36.1' : DN, # krbPwdPolicyReference - '2.16.840.1.113719.1.301.4.40.1' : DN, # krbTicketPolicyReference - '2.16.840.1.113719.1.301.4.41.1' : DN, # krbSubTrees - '2.16.840.1.113719.1.301.4.52.1' : DN, # krbObjectReferences - '2.16.840.1.113719.1.301.4.53.1' : DN, # krbPrincContainerRef - '1.3.6.1.4.1.1466.115.121.1.24' : datetime.datetime, - } - - # In most cases we lookup the syntax from the schema returned by - # the server. However, sometimes attributes may not be defined in - # the schema (e.g. extensibleObject which permits undefined - # attributes), or the schema was incorrectly defined (i.e. giving - # an attribute the syntax DirectoryString when in fact it's really - # a DN). This (hopefully sparse) table allows us to trap these - # anomalies and force them to be the syntax we know to be in use. - # - # FWIW, many entries under cn=config are undefined :-( - - _SYNTAX_OVERRIDE = CIDict({ - 'managedtemplate': DN, - 'managedbase': DN, - 'originscope': DN, - 'idnsname': DNSName, - 'idnssoamname': DNSName, - 'idnssoarname': DNSName, - 'dnszoneidnsname': DNSName, - 'nsds5replicalastupdatestart': unicode, - 'nsds5replicalastupdateend': unicode, - 'nsds5replicalastinitstart': unicode, - 'nsds5replicalastinitend': unicode, - }) - _SINGLE_VALUE_OVERRIDE = CIDict({ - 'nsslapd-ssl-check-hostname': True, - 'nsslapd-lookthroughlimit': True, - 'nsslapd-idlistscanlimit': True, - 'nsslapd-anonlimitsdn': True, - 'nsslapd-minssf-exclude-rootdse': True, - }) - - def __init__(self, uri, force_schema_updates, no_schema=False, - decode_attrs=True): + def __init__(self, uri): """An internal LDAP connection object :param uri: The LDAP URI to connect to - :param force_schema_updates: - If true, this object will always request a new schema from the - server. If false, a cached schema will be reused if it exists. - - Generally, it should be true if the API context is 'installer' or - 'updates', but it must be given explicitly since the API object - is not always available - :param no_schema: If true, schema is never requested from the server. - :param decode_attrs: - If true, attributes are decoded to Python types according to their - syntax. """ self.log = log_mgr.get_logger(self) self.uri = uri self.conn = SimpleLDAPObject(uri) - self._no_schema = no_schema - self._has_schema = False - self._schema = None - self._force_schema_updates = force_schema_updates - self._decode_attrs = decode_attrs - - def _get_schema(self): - if self._no_schema: - return None - if not self._has_schema: - try: - self._schema = schema_cache.get_schema( - self.uri, self.conn, - force_update=self._force_schema_updates) - except (errors.ExecutionError, IndexError): - pass - self._has_schema = True - return self._schema - - schema = property(_get_schema, None, None, 'schema associated with this LDAP server') - - - def flush_cached_schema(self): - ''' - Force this instance to forget it's cached schema and reacquire - it from the schema cache. - ''' - - # Currently this is called during bind operations to assure - # we're working with valid schema for a specific - # connection. This causes self._get_schema() to query the - # schema cache for the server's schema passing along a flag - # indicating if we're in a context that requires freshly - # loading the schema vs. returning the last cached version of - # the schema. If we're in a mode that permits use of - # previously cached schema the flush and reacquire is a very - # low cost operation. - # - # The schema is reacquired whenever this object is - # instantiated or when binding occurs. The schema is not - # reacquired for operations during a bound connection, it is - # presumed schema cannot change during this interval. This - # provides for maximum efficiency in contexts which do need - # schema refreshing by only peforming the refresh inbetween - # logical operations that have the potential to cause a schema - # change. - - self._has_schema = False - self._schema = None - - def get_type(self, attr): - if isinstance(attr, unicode): - attr = attr.encode('utf-8') - - # Is this a special case attribute? - if attr in self._SYNTAX_OVERRIDE: - return self._SYNTAX_OVERRIDE[attr] - - if self.schema is None: - return unicode - - # Try to lookup the syntax in the schema returned by the server - obj = self.schema.get_obj(ldap.schema.AttributeType, attr) - if obj is None: - return unicode - - return self._SYNTAX_MAPPING.get(obj.syntax, unicode) - - def has_dn_syntax(self, attr): - """ - Check the schema to see if the attribute uses DN syntax. - - Returns True/False - """ - return self.get_type(attr) is DN - - def get_single_value(self, attr): - """ - Check the schema to see if the attribute is single-valued. - - If the attribute is in the schema then returns True/False - - If there is a problem loading the schema or the attribute is - not in the schema return None - """ - if isinstance(attr, unicode): - attr = attr.encode('utf-8') - - if attr in self._SINGLE_VALUE_OVERRIDE: - return self._SINGLE_VALUE_OVERRIDE[attr] - - if self.schema is None: - return None - - obj = self.schema.get_obj(ldap.schema.AttributeType, attr) - if obj is None: - return None - - return obj.single_value - - - def encode(self, val): - """ - Encode attribute value to LDAP representation (str). - """ - # Booleans are both an instance of bool and int, therefore - # test for bool before int otherwise the int clause will be - # entered for a boolean value instead of the boolean clause. - if isinstance(val, bool): - if val: - return 'TRUE' - else: - return 'FALSE' - elif isinstance(val, (unicode, float, int, long, Decimal, DN)): - return value_to_utf8(val) - elif isinstance(val, DNSName): - return str(val) - elif isinstance(val, str): - return val - elif isinstance(val, list): - return [self.encode(m) for m in val] - elif isinstance(val, tuple): - return tuple(self.encode(m) for m in val) - elif isinstance(val, dict): - dct = dict((self.encode(k), self.encode(v)) for k, v in val.iteritems()) - return dct - elif isinstance(val, datetime.datetime): - return val.strftime(LDAP_GENERALIZED_TIME_FORMAT) - elif val is None: - return None - else: - raise TypeError("attempt to pass unsupported type to ldap, value=%s type=%s" %(val, type(val))) - - def decode(self, val, attr): - """ - Decode attribute value from LDAP representation (str). - """ - if isinstance(val, str): - if not self._decode_attrs: - return val - target_type = self.get_type(attr) - try: - if target_type is str: - return val - elif target_type is unicode: - return val.decode('utf-8') - elif target_type is datetime.datetime: - return datetime.datetime.strptime(val, LDAP_GENERALIZED_TIME_FORMAT) - else: - return target_type(val) - except Exception, e: - msg = 'unable to convert the attribute %r value %r to type %s' % (attr, val, target_type) - self.log.error(msg) - raise ValueError(msg) - elif isinstance(val, list): - return [self.decode(m, attr) for m in val] - elif isinstance(val, tuple): - return tuple(self.decode(m, attr) for m in val) - elif isinstance(val, dict): - dct = dict((unicode_from_utf8(k), self.decode(v, k)) for k, v in val.iteritems()) - return dct - elif val is None: - return None - else: - raise TypeError("attempt to pass unsupported type from ldap, value=%s type=%s" %(val, type(val))) #---------- python-ldap emulations ---------- @@ -477,7 +217,6 @@ class IPASimpleLDAPObject(object): return self.conn.add_s(dn, modlist) def bind(self, who, cred, method=ldap.AUTH_SIMPLE): - self.flush_cached_schema() return self.conn.bind(who, cred, method) def delete(self, dn): @@ -512,7 +251,6 @@ class IPASimpleLDAPObject(object): def sasl_interactive_bind_s(self, who, auth, serverctrls=None, clientctrls=None, sasl_flags=ldap.SASL_QUIET): - self.flush_cached_schema() return self.conn.sasl_interactive_bind_s(who, auth, serverctrls, clientctrls, sasl_flags) @@ -545,14 +283,12 @@ class IPASimpleLDAPObject(object): return self.conn.set_option(option, invalue) def simple_bind_s(self, who=None, cred='', serverctrls=None, clientctrls=None): - self.flush_cached_schema() return self.conn.simple_bind_s(who, cred, serverctrls, clientctrls) def start_tls_s(self): return self.conn.start_tls_s() def unbind_s(self): - self.flush_cached_schema() return self.conn.unbind_s() @@ -880,7 +616,7 @@ class LDAPEntry(collections.MutableMapping): # particularly for schema adds = [value for value in new if value not in old] dels = [value for value in old if value not in new] - if adds and self.conn.get_single_value(name): + if adds and self.conn.get_attribute_single_value(name): if len(adds) > 1: raise errors.OnlyOneValueAllowed(attr=name) modlist.append((ldap.MOD_REPLACE, name, adds)) @@ -961,8 +697,16 @@ class LDAPClient(object): This class abstracts a LDAP connection, providing methods that work with LADPEntries. - This class is not intended to be used directly; instead, use one of its - subclasses, IPAdmin or the ldap2 plugin. + The purpose of this class is to provide a boundary between IPA and + python-ldap. In IPA we use IPA defined types because they are + richer and are designed to meet our needs. We also require that we + consistently use those types without exception. On the other hand + python-ldap uses different types. The goal is to be able to have + IPA code call python-ldap methods using the types native to + IPA. This class accomplishes that goal by exposing python-ldap + methods which take IPA types, convert them to python-ldap types, + call python-ldap, and then convert the results returned by + python-ldap into IPA types. """ # rules for generating filters from entries @@ -975,8 +719,93 @@ class LDAPClient(object): SCOPE_ONELEVEL = ldap.SCOPE_ONELEVEL SCOPE_SUBTREE = ldap.SCOPE_SUBTREE + _SYNTAX_MAPPING = { + '1.3.6.1.4.1.1466.115.121.1.1' : str, # ACI item + '1.3.6.1.4.1.1466.115.121.1.4' : str, # Audio + '1.3.6.1.4.1.1466.115.121.1.5' : str, # Binary + '1.3.6.1.4.1.1466.115.121.1.8' : str, # Certificate + '1.3.6.1.4.1.1466.115.121.1.9' : str, # Certificate List + '1.3.6.1.4.1.1466.115.121.1.10' : str, # Certificate Pair + '1.3.6.1.4.1.1466.115.121.1.12' : DN, # Distinguished Name + '1.3.6.1.4.1.1466.115.121.1.23' : str, # Fax + '1.3.6.1.4.1.1466.115.121.1.24' : datetime.datetime, + '1.3.6.1.4.1.1466.115.121.1.28' : str, # JPEG + '1.3.6.1.4.1.1466.115.121.1.40' : str, # OctetString (same as Binary) + '1.3.6.1.4.1.1466.115.121.1.49' : str, # Supported Algorithm + '1.3.6.1.4.1.1466.115.121.1.51' : str, # Teletext Terminal Identifier + + '2.16.840.1.113730.3.8.3.3' : DN, # enrolledBy + '2.16.840.1.113730.3.8.3.18' : DN, # managedBy + '2.16.840.1.113730.3.8.3.5' : DN, # memberUser + '2.16.840.1.113730.3.8.3.7' : DN, # memberHost + '2.16.840.1.113730.3.8.3.20' : DN, # memberService + '2.16.840.1.113730.3.8.11.4' : DN, # ipaNTFallbackPrimaryGroup + '2.16.840.1.113730.3.8.11.21' : DN, # ipaAllowToImpersonate + '2.16.840.1.113730.3.8.11.22' : DN, # ipaAllowedTarget + '2.16.840.1.113730.3.8.7.1' : DN, # memberAllowCmd + '2.16.840.1.113730.3.8.7.2' : DN, # memberDenyCmd + + '2.16.840.1.113719.1.301.4.14.1' : DN, # krbRealmReferences + '2.16.840.1.113719.1.301.4.17.1' : DN, # krbKdcServers + '2.16.840.1.113719.1.301.4.18.1' : DN, # krbPwdServers + '2.16.840.1.113719.1.301.4.26.1' : DN, # krbPrincipalReferences + '2.16.840.1.113719.1.301.4.29.1' : DN, # krbAdmServers + '2.16.840.1.113719.1.301.4.36.1' : DN, # krbPwdPolicyReference + '2.16.840.1.113719.1.301.4.40.1' : DN, # krbTicketPolicyReference + '2.16.840.1.113719.1.301.4.41.1' : DN, # krbSubTrees + '2.16.840.1.113719.1.301.4.52.1' : DN, # krbObjectReferences + '2.16.840.1.113719.1.301.4.53.1' : DN, # krbPrincContainerRef + } + + # In most cases we lookup the syntax from the schema returned by + # the server. However, sometimes attributes may not be defined in + # the schema (e.g. extensibleObject which permits undefined + # attributes), or the schema was incorrectly defined (i.e. giving + # an attribute the syntax DirectoryString when in fact it's really + # a DN). This (hopefully sparse) table allows us to trap these + # anomalies and force them to be the syntax we know to be in use. + # + # FWIW, many entries under cn=config are undefined :-( + + _SYNTAX_OVERRIDE = CIDict({ + 'managedtemplate': DN, + 'managedbase': DN, + 'originscope': DN, + 'idnsname': DNSName, + 'idnssoamname': DNSName, + 'idnssoarname': DNSName, + 'dnszoneidnsname': DNSName, + 'nsds5replicalastupdatestart': unicode, + 'nsds5replicalastupdateend': unicode, + 'nsds5replicalastinitstart': unicode, + 'nsds5replicalastinitend': unicode, + }) + _SINGLE_VALUE_OVERRIDE = CIDict({ + 'nsslapd-ssl-check-hostname': True, + 'nsslapd-lookthroughlimit': True, + 'nsslapd-idlistscanlimit': True, + 'nsslapd-anonlimitsdn': True, + 'nsslapd-minssf-exclude-rootdse': True, + }) + def __init__(self, ldap_uri, start_tls=False, force_schema_updates=False, no_schema=False, decode_attrs=True): + """Create LDAPClient object. + + :param ldap_uri: The LDAP URI to connect to + :param start_tls: Use STARTTLS + :param force_schema_updates: + If true, this object will always request a new schema from the + server. If false, a cached schema will be reused if it exists. + + Generally, it should be true if the API context is 'installer' or + 'updates', but it must be given explicitly since the API object + is not always available + :param no_schema: If true, schema is never requested from the server. + :param decode_attrs: + If true, attributes are decoded to Python types according to their + syntax. + """ self.ldap_uri = ldap_uri self._start_tls = start_tls self._force_schema_updates = force_schema_updates @@ -985,6 +814,8 @@ class LDAPClient(object): self.log = log_mgr.get_logger(self) self._conn = None + self._has_schema = False + self._schema = None self._connect() @@ -992,14 +823,164 @@ class LDAPClient(object): def conn(self): return self._conn - def get_single_value(self, attr): - return self.conn.get_single_value(attr) + def _get_schema(self): + if self._no_schema: + return None + + if not self._has_schema: + try: + schema = schema_cache.get_schema( + self.conn.uri, self.conn.conn, + force_update=self._force_schema_updates) + except (errors.ExecutionError, IndexError): + schema = None + + object.__setattr__(self, '_schema', schema) + object.__setattr__(self, '_has_schema', True) + + return self._schema + + def _flush_schema(self): + ''' + Force this instance to forget it's cached schema and reacquire + it from the schema cache. + ''' + + # Currently this is called during bind operations to assure + # we're working with valid schema for a specific + # connection. This causes self._get_schema() to query the + # schema cache for the server's schema passing along a flag + # indicating if we're in a context that requires freshly + # loading the schema vs. returning the last cached version of + # the schema. If we're in a mode that permits use of + # previously cached schema the flush and reacquire is a very + # low cost operation. + # + # The schema is reacquired whenever this object is + # instantiated or when binding occurs. The schema is not + # reacquired for operations during a bound connection, it is + # presumed schema cannot change during this interval. This + # provides for maximum efficiency in contexts which do need + # schema refreshing by only peforming the refresh inbetween + # logical operations that have the potential to cause a schema + # change. + + object.__setattr__(self, '_has_schema', False) + object.__setattr__(self, '_schema', None) + + def get_attribute_type(self, name_or_oid): + if not self._decode_attrs: + return str + + if isinstance(name_or_oid, unicode): + name_or_oid = name_or_oid.encode('utf-8') + + # Is this a special case attribute? + if name_or_oid in self._SYNTAX_OVERRIDE: + return self._SYNTAX_OVERRIDE[name_or_oid] + + schema = self._get_schema() + if schema is not None: + # Try to lookup the syntax in the schema returned by the server + obj = schema.get_obj(ldap.schema.AttributeType, name_or_oid) + if obj is not None and obj.syntax in self._SYNTAX_MAPPING: + return self._SYNTAX_MAPPING[obj.syntax] + + return unicode + + def has_dn_syntax(self, name_or_oid): + """ + Check the schema to see if the attribute uses DN syntax. + + Returns True/False + """ + return self.get_attribute_type(name_or_oid) is DN + + def get_attribute_single_value(self, name_or_oid): + """ + Check the schema to see if the attribute is single-valued. + + If the attribute is in the schema then returns True/False + + If there is a problem loading the schema or the attribute is + not in the schema return None + """ + if isinstance(name_or_oid, unicode): + name_or_oid = name_or_oid.encode('utf-8') + + if name_or_oid in self._SINGLE_VALUE_OVERRIDE: + return self._SINGLE_VALUE_OVERRIDE[name_or_oid] + + schema = self._get_schema() + if schema is not None: + obj = schema.get_obj(ldap.schema.AttributeType, name_or_oid) + if obj is not None: + return obj.single_value + + return None def encode(self, val): - return self.conn.encode(val) + """ + Encode attribute value to LDAP representation (str). + """ + # Booleans are both an instance of bool and int, therefore + # test for bool before int otherwise the int clause will be + # entered for a boolean value instead of the boolean clause. + if isinstance(val, bool): + if val: + return 'TRUE' + else: + return 'FALSE' + elif isinstance(val, (unicode, float, int, long, Decimal, DN)): + return value_to_utf8(val) + elif isinstance(val, DNSName): + return str(val) + elif isinstance(val, str): + return val + elif isinstance(val, list): + return [self.encode(m) for m in val] + elif isinstance(val, tuple): + return tuple(self.encode(m) for m in val) + elif isinstance(val, dict): + dct = dict((self.encode(k), self.encode(v)) for k, v in val.iteritems()) + return dct + elif isinstance(val, datetime.datetime): + return val.strftime(LDAP_GENERALIZED_TIME_FORMAT) + elif val is None: + return None + else: + raise TypeError("attempt to pass unsupported type to ldap, value=%s type=%s" %(val, type(val))) def decode(self, val, attr): - return self.conn.decode(val, attr) + """ + Decode attribute value from LDAP representation (str). + """ + if isinstance(val, str): + target_type = self.get_attribute_type(attr) + try: + if target_type is str: + return val + elif target_type is unicode: + return val.decode('utf-8') + elif target_type is datetime.datetime: + return datetime.datetime.strptime(val, LDAP_GENERALIZED_TIME_FORMAT) + else: + return target_type(val) + except Exception, e: + msg = 'unable to convert the attribute %r value %r to type %s' % (attr, val, target_type) + self.log.error(msg) + raise ValueError(msg) + elif isinstance(val, list): + return [self.decode(m, attr) for m in val] + elif isinstance(val, tuple): + return tuple(self.decode(m, attr) for m in val) + elif isinstance(val, dict): + dct = dict((unicode_from_utf8(k), self.decode(v, k)) for k, v in val.iteritems()) + return dct + elif val is None: + return None + else: + raise TypeError("attempt to pass unsupported type from ldap, value=%s type=%s" %(val, type(val))) def _convert_result(self, result): ''' @@ -1112,14 +1093,6 @@ class LDAPClient(object): 'Unhandled LDAPError: %s: %s' % (type(e).__name__, str(e))) raise errors.DatabaseError(desc=desc, info=info) - @property - def schema(self): - """schema associated with this LDAP server""" - return self.conn.schema - - def has_dn_syntax(self, attr): - return self.conn.has_dn_syntax(attr) - def get_allowed_attributes(self, objectclasses, raise_on_unknown=False): if self.schema is None: return None @@ -1133,6 +1106,11 @@ class LDAPClient(object): reason=_('objectclass %s not found') % oc) return [unicode(a).lower() for a in list(set(allowed_attributes))] + @property + def schema(self): + """schema associated with this LDAP server""" + return self._get_schema() + def __del__(self): self.close() @@ -1156,10 +1134,7 @@ class LDAPClient(object): with self.error_handler(): object.__setattr__(self, '_conn', - IPASimpleLDAPObject(self.ldap_uri, - self._force_schema_updates, - self._no_schema, - self._decode_attrs)) + IPASimpleLDAPObject(self.ldap_uri)) if self._start_tls: self._conn.start_tls_s() @@ -1177,6 +1152,7 @@ class LDAPClient(object): Perform simple bind operation. """ with self.error_handler(): + self._flush_schema() if bind_dn is None: bind_dn = DN() assert isinstance(bind_dn, DN) @@ -1192,6 +1168,7 @@ class LDAPClient(object): """ with self.error_handler(): auth_tokens = ldap.sasl.external(user_name) + self._flush_schema() self._conn.sasl_interactive_bind_s( '', auth_tokens, server_controls, client_controls) @@ -1201,6 +1178,7 @@ class LDAPClient(object): """ with self.error_handler(): auth_tokens = ldap.sasl.sasl({}, 'GSSAPI') + self._flush_schema() self._conn.sasl_interactive_bind_s( '', auth_tokens, server_controls, client_controls) @@ -1209,6 +1187,7 @@ class LDAPClient(object): Perform unbind operation. """ with self.error_handler(): + self._flush_schema() self.conn.unbind_s() def make_dn_from_attr(self, attr, value, parent_dn=None): diff --git a/ipatests/test_xmlrpc/test_baseldap_plugin.py b/ipatests/test_xmlrpc/test_baseldap_plugin.py index 3ffc041..6b19e57 100644 --- a/ipatests/test_xmlrpc/test_baseldap_plugin.py +++ b/ipatests/test_xmlrpc/test_baseldap_plugin.py @@ -181,17 +181,12 @@ def test_entry_to_dict(): elif name == 'dnattr': return FakeAttributeType(name, '1.3.6.1.4.1.1466.115.121.1.12') - class FakeIPASimpleLDAPObject(ipaldap.IPASimpleLDAPObject): - def __init__(self): - super(FakeIPASimpleLDAPObject, self).__init__('ldap://test', False) - self._schema = FakeSchema() - self._has_schema = True - class FakeLDAPClient(ipaldap.LDAPClient): def __init__(self): super(FakeLDAPClient, self).__init__('ldap://test', force_schema_updates=False) - self._conn = FakeIPASimpleLDAPObject() + self._has_schema = True + self._schema = FakeSchema() conn = FakeLDAPClient() rights = {'nothing': 'is'} -- 2.1.0
>From a339641e957e8512dbd98097ccac5e9adb8ae6f0 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Tue, 20 Jan 2015 12:36:14 +0000 Subject: [PATCH 15/16] ldap: Use SimpleLDAPObject instead of IPASimpleLDAPObject in LDAPClient --- ipapython/ipaldap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 8ea2426..8493d73 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -830,7 +830,7 @@ class LDAPClient(object): if not self._has_schema: try: schema = schema_cache.get_schema( - self.conn.uri, self.conn.conn, + self.ldap_uri, self.conn, force_update=self._force_schema_updates) except (errors.ExecutionError, IndexError): schema = None @@ -1134,7 +1134,7 @@ class LDAPClient(object): with self.error_handler(): object.__setattr__(self, '_conn', - IPASimpleLDAPObject(self.ldap_uri)) + ldap.initialize(self.ldap_uri)) if self._start_tls: self._conn.start_tls_s() -- 2.1.0
>From 3e230eb93d1dfb26c208cf4b4ee42d7976003818 Mon Sep 17 00:00:00 2001 From: Jan Cholasta <jchol...@redhat.com> Date: Wed, 14 Jan 2015 17:09:58 +0000 Subject: [PATCH 16/16] ldap: Remove IPASimpleLDAPObject --- ipapython/ipaldap.py | 105 --------------------------------------------------- 1 file changed, 105 deletions(-) diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 8493d73..21f2890 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -187,111 +187,6 @@ class SchemaCache(object): schema_cache = SchemaCache() -class IPASimpleLDAPObject(object): - ''' - IPA code should never call python-ldap directly, it should only - call python-ldap methods in this class. - ''' - - def __init__(self, uri): - """An internal LDAP connection object - - :param uri: The LDAP URI to connect to - """ - self.log = log_mgr.get_logger(self) - self.uri = uri - self.conn = SimpleLDAPObject(uri) - - #---------- python-ldap emulations ---------- - - def add(self, dn, modlist): - return self.conn.add(dn, modlist) - - def add_ext(self, dn, modlist, serverctrls=None, clientctrls=None): - return self.conn.add_ext(dn, modlist, serverctrls, clientctrls) - - def add_ext_s(self, dn, modlist, serverctrls=None, clientctrls=None): - return self.conn.add_ext_s(dn, modlist, serverctrls, clientctrls) - - def add_s(self, dn, modlist): - return self.conn.add_s(dn, modlist) - - def bind(self, who, cred, method=ldap.AUTH_SIMPLE): - return self.conn.bind(who, cred, method) - - def delete(self, dn): - return self.conn.delete(dn) - - def delete_s(self, dn): - return self.conn.delete_s(dn) - - def get_option(self, option): - return self.conn.get_option(option) - - def modify_s(self, dn, modlist): - return self.conn.modify_s(dn, modlist) - - def modrdn_s(self, dn, newrdn, delold=1): - return self.conn.modrdn_s(dn, newrdn, delold) - - def passwd_s(self, dn, oldpw, newpw, serverctrls=None, clientctrls=None): - return self.conn.passwd_s(dn, oldpw, newpw, serverctrls, clientctrls) - - def rename_s(self, dn, newrdn, newsuperior=None, delold=1): - # NOTICE: python-ldap of version 2.3.10 and lower does not support - # serverctrls and clientctrls for rename_s operation. Thus, these - # options are ommited from this command until really needed - return self.conn.rename_s(dn, newrdn, newsuperior, delold) - - def result(self, msgid=ldap.RES_ANY, all=1, timeout=None): - return self.conn.result(msgid, all, timeout) - - def result3(self, msgid=ldap.RES_ANY, all=1, timeout=None): - return self.conn.result3(msgid, all, timeout) - - def sasl_interactive_bind_s(self, who, auth, serverctrls=None, - clientctrls=None, sasl_flags=ldap.SASL_QUIET): - return self.conn.sasl_interactive_bind_s(who, auth, serverctrls, - clientctrls, sasl_flags) - - def search(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): - return self.conn.search(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): - if _debug_log_ldap: - self.log.debug( - "ldap.search_ext: dn: %s\nfilter: %s\nattrs_list: %s", - base, filterstr, attrlist) - - - return self.conn.search_ext(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 self.conn.search_ext_s(base, scope, filterstr, attrlist, - attrsonly, serverctrls, clientctrls, - timeout, sizelimit) - - def search_s(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0): - return self.conn.search_s(base, scope, filterstr, attrlist, - attrsonly) - - def search_st(self, base, scope, filterstr='(objectClass=*)', attrlist=None, attrsonly=0, timeout=-1): - return self.conn.search_st(base, scope, filterstr, attrlist, attrsonly, - timeout) - - def set_option(self, option, invalue): - return self.conn.set_option(option, invalue) - - def simple_bind_s(self, who=None, cred='', serverctrls=None, clientctrls=None): - return self.conn.simple_bind_s(who, cred, serverctrls, clientctrls) - - def start_tls_s(self): - return self.conn.start_tls_s() - - def unbind_s(self): - return self.conn.unbind_s() - - class LDAPEntry(collections.MutableMapping): __slots__ = ('_conn', '_dn', '_names', '_nice', '_raw', '_sync', '_not_list', '_orig', '_raw_view', '_single_value_view') -- 2.1.0
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code