From 2a9cf26b9f70fea11839013a48c5f28c4d5af197 Mon Sep 17 00:00:00 2001 From: William Brown Date: Sun, 12 Jun 2016 16:22:58 +1000 Subject: [PATCH] Ticket 48884 - Bugfixes for mapped object and new connections Bug Description: This patch corrects a number of behaviours around ldap connections, and the mapped object type behaviour in certain conditions. (Rdn set through properties, rather than argument) Fix Description: Fix handling of ldapurl, start_tls, and mapped_objects when the rdn is part of properties. https://fedorahosted.org/389/ticket/48884 Author: wibrown Review by: ??? --- lib389/__init__.py | 34 ++++++++++++++++++++++++--------- lib389/_mapped_object.py | 49 +++++++++++++++++++++++++++++------------------- lib389/properties.py | 1 + 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/lib389/__init__.py b/lib389/__init__.py index 30baa2f..2031daa 100644 --- a/lib389/__init__.py +++ b/lib389/__init__.py @@ -392,7 +392,12 @@ class DirSrv(SimpleLDAPObject): self.state = DIRSRV_STATE_INIT self.verbose = verbose + if self.verbose: + log.setLevel(logging.DEBUG) + else: + log.setLevel(logging.INFO) self.log = log + self.timeout = timeout self.confdir = None @@ -457,22 +462,31 @@ class DirSrv(SimpleLDAPObject): self.state) if SER_SERVERID_PROP not in args: - self.log.info('SER_SERVERID_PROP not provided') + self.log.debug('SER_SERVERID_PROP not provided') + # The lack of this value basically rules it out in most cases + self.isLocal = False # Do we have ldapi settings? # Do we really need .strip() on this? self.ldapi_enabled = args.get(SER_LDAPI_ENABLED, 'off') self.ldapi_socket = args.get(SER_LDAPI_SOCKET, None) + self.host = None + self.ldapuri = None + self.sslport = None + self.port = None # Or do we have tcp / ip settings? if self.ldapi_enabled == 'on' and self.ldapi_socket is not None: self.ldapi_autobind = args.get(SER_LDAPI_AUTOBIND, 'off') self.isLocal = True if self.verbose: self.log.info("Allocate %s with %s" % (self.__class__, self.ldapi_socket)) + elif args.get(SER_LDAP_URL, None) is not None: + self.ldapuri = args.get(SER_LDAP_URL) + if self.verbose: + self.log.info("Allocate %s with %s" % (self.__class__, self.ldapuri)) else: # Settings from args of server attributes self.strict_hostname = args.get(SER_STRICT_HOSTNAME_CHECKING, False) - self.host = None if self.strict_hostname is True: self.host = args.get(SER_HOST, LOCALHOST) if self.host == LOCALHOST: @@ -486,10 +500,8 @@ class DirSrv(SimpleLDAPObject): self.sslport = args.get(SER_SECURE_PORT) self.isLocal = isLocalHost(self.host) if self.verbose: - self.log.info("Allocate %s with %s:%s" % (self.__class__, - self.host, - (self.sslport or - self.port))) + self.log.info("Allocate %s with %s:%s" % (self.__class__, self.host, (self.sslport or self.port))) + self.binddn = args.get(SER_ROOT_DN, DN_DM) self.bindpw = args.get(SER_ROOT_PW, PW_DM) self.creation_suffix = args.get(SER_CREATION_SUFFIX, DEFAULT_SUFFIX) @@ -957,7 +969,7 @@ class DirSrv(SimpleLDAPObject): self.state = DIRSRV_STATE_ALLOCATED - def open(self, saslmethod=None, certdir=None): + def open(self, saslmethod=None, certdir=None, starttls=False): ''' It opens a ldap bound connection to dirsrv so that online administrative tasks are possible. It binds with the binddn @@ -986,8 +998,10 @@ class DirSrv(SimpleLDAPObject): """ We have a certificate directory, so lets start up TLS negotiations """ + self.set_option(ldap.OPT_X_TLS_CACERTFILE, certdir) + + if certdir or starttls: try: - self.set_option(ldap.OPT_X_TLS_CACERTFILE, certdir) self.start_tls_s() except ldap.LDAPError as e: log.fatal('TLS negotiation failed: %s' % str(e)) @@ -1039,7 +1053,7 @@ class DirSrv(SimpleLDAPObject): Authenticated, now finish the initialization """ if self.verbose: - log.info("open(): bound as %s" % self.whoami_s()) + log.info("open(): bound as %s" % self.binddn) self.__initPart2() self.state = DIRSRV_STATE_ONLINE @@ -1397,6 +1411,8 @@ class DirSrv(SimpleLDAPObject): host = self.host if self.ldapi_enabled == 'on' and self.ldapi_socket is not None: return "ldapi://%s" % (ldapurl.ldapUrlEscape(ensure_str(ldapi_socket))) + elif self.ldapuri: + return self.ldapuri elif self.sslport: return "ldaps://%s:%d/" % (ensure_str(host), self.sslport) else: diff --git a/lib389/_mapped_object.py b/lib389/_mapped_object.py index 46fb5f9..33a569e 100644 --- a/lib389/_mapped_object.py +++ b/lib389/_mapped_object.py @@ -68,6 +68,8 @@ class DSLogging(object): self._log = logging.getLogger(type(self).__name__) if verbose: self._log.setLevel(logging.DEBUG) + else: + self._log.setLevel(logging.INFO) class DSLdapObject(DSLogging): @@ -92,7 +94,8 @@ class DSLdapObject(DSLogging): def __unicode__(self): val = self._dn if self._rdn_attribute: - val = self.get(self._rdn_attribute) + # What if the rdn is multi value and we don't get the primary .... ARGHHH + val = self.get(self._rdn_attribute)[0] return ensure_str(val) def __str__(self): @@ -234,6 +237,7 @@ class DSLdapObject(DSLogging): self._instance.add_s(e) # If it worked, we need to fix our instance dn self._dn = dn + return self # A challenge of this, is how do we manage indexes? They have two naming attribunes.... @@ -253,19 +257,23 @@ class DSLdapObjects(DSLogging): def list(self): # Filter based on the objectclasses and the basedn - results = self._instance.search_s( - base=self._basedn, - scope=self._scope, - # This will yield and & filter for objectClass with as many terms as needed. - filterstr=_gen_and( - _gen_filter(_term_gen('objectclass'), self._objectclasses) - ), - attrlist=self._list_attrlist, - ) - # def __init__(self, instance, dn=None, batch=False): - # insts = map(lambda r: self._childobject(instance=self._instance, dn=r.dn, batch=self._batch), results) - insts = [self._childobject(instance=self._instance, dn=r.dn, batch=self._batch) for r in results] - print(insts) + insts = None + try: + results = self._instance.search_s( + base=self._basedn, + scope=self._scope, + # This will yield and & filter for objectClass with as many terms as needed. + filterstr=_gen_and( + _gen_filter(_term_gen('objectclass'), self._objectclasses) + ), + attrlist=self._list_attrlist, + ) + # def __init__(self, instance, dn=None, batch=False): + # insts = map(lambda r: self._childobject(instance=self._instance, dn=r.dn, batch=self._batch), results) + insts = [self._childobject(instance=self._instance, dn=r.dn, batch=self._batch) for r in results] + except ldap.NO_SUCH_OBJECT: + # There are no objects to select from, se we return an empty array + insts = [] return insts def get(self, selector=[], dn=None): @@ -322,12 +330,15 @@ class DSLdapObjects(DSLogging): # Get the rdn out of the properties if it's unset??? if rdn is None and self._rdn_attribute in properties: # First see if we can get it from the properties. - trdn = str_properties.get(self._rdn_attribute) - if type(trdn) != list: - raise ldap.UNWILLING_TO_PERFORM("rdn %s from properties is not in a list" % self._rdn_attribute) - if len(trdn) != 1: + trdn = properties.get(self._rdn_attribute) + if type(trdn) == str: + rdn = trdn + elif type(trdn) == list and len(trdn) != 1: raise ldap.UNWILLING_TO_PERFORM("Cannot determine rdn %s from properties. Too many choices" % (self._rdn_attribute)) - rdn = trdn[0] + elif type(trdn) == list: + rdn = trdn[0] + else: + raise ldap.UNWILLING_TO_PERFORM("Cannot determine rdn %s from properties, Invalid type" % type(trdn)) return (rdn, properties) diff --git a/lib389/properties.py b/lib389/properties.py index 49f1592..2293b2b 100644 --- a/lib389/properties.py +++ b/lib389/properties.py @@ -19,6 +19,7 @@ from lib389._constants import * # SER_HOST = 'hostname' SER_PORT = 'ldap-port' +SER_LDAP_URL = 'ldapurl' SER_SECURE_PORT = 'ldap-secureport' SER_ROOT_DN = 'root-dn' SER_ROOT_PW = 'root-pw' -- 2.5.5