From aa0a07bf443f673c3a3d3d57e8e9df7f9af30e51 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 18 Dec 2017 08:50:35 +0100 Subject: [PATCH 1/4] Make LDAPI optional LDAPI is LDAP over Unix Domain Socket, also referred as LDAP IPC. AF_UNIX is only supported on POSIX compatible operating systems. Don't bind to LDAPI socket and skip LDAPI specific tests, e.g. SASL EXTERNAL with SO_PEERCRED. Signed-off-by: Christian Heimes --- Lib/slapdtest/__init__.py | 3 ++- Lib/slapdtest/_slapdtest.py | 47 ++++++++++++++++++++++++++------- Tests/t_ldap_sasl.py | 5 ++-- Tests/t_ldap_schema_subentry.py | 3 ++- Tests/t_ldapobject.py | 14 +++++----- tox.ini | 4 +-- 6 files changed, 55 insertions(+), 21 deletions(-) diff --git a/Lib/slapdtest/__init__.py b/Lib/slapdtest/__init__.py index a2a875f9..306eaf7f 100644 --- a/Lib/slapdtest/__init__.py +++ b/Lib/slapdtest/__init__.py @@ -8,4 +8,5 @@ __version__ = '3.0.0b2' from slapdtest._slapdtest import SlapdObject, SlapdTestCase, SysLogHandler -from slapdtest._slapdtest import skip_unless_ci, requires_sasl, requires_tls +from slapdtest._slapdtest import requires_ldapi, requires_sasl, requires_tls +from slapdtest._slapdtest import skip_unless_ci diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index 4c7a9e45..09ec1546 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -56,6 +56,12 @@ LOCALHOST = '127.0.0.1' +CI_DISABLED = set(os.environ.get('CI_DISABLED', '').split(':')) +if 'LDAPI' in CI_DISABLED: + HAVE_LDAPI = False +else: + HAVE_LDAPI = hasattr(socket, 'AF_UNIX') + def identity(test_item): """Identity decorator @@ -69,7 +75,7 @@ def skip_unless_ci(reason, feature=None): """ if not os.environ.get('CI', False): return unittest.skip(reason) - elif feature in os.environ.get('CI_DISABLED', '').split(':'): + elif feature in CI_DISABLED: return unittest.skip(reason) else: # Don't skip on Travis @@ -95,6 +101,14 @@ def requires_sasl(): return identity +def requires_ldapi(): + if not HAVE_LDAPI: + return skip_unless_ci( + "test needs ldapi support (AF_UNIX)", feature='LDAPI') + else: + return identity + + def combined_logger( log_name, log_level=logging.WARN, @@ -149,8 +163,6 @@ class SlapdObject(object): root_dn = 'cn=%s,%s' % (root_cn, suffix) root_pw = 'password' slapd_loglevel = 'stats stats2' - # use SASL/EXTERNAL via LDAPI when invoking OpenLDAP CLI tools - cli_sasl_external = True local_host = '127.0.0.1' testrunsubdirs = ( 'schema', @@ -192,8 +204,17 @@ def __init__(self): self._slapd_conf = os.path.join(self.testrundir, 'slapd.conf') self._db_directory = os.path.join(self.testrundir, "openldap-data") self.ldap_uri = "ldap://%s:%d/" % (LOCALHOST, self._port) - ldapi_path = os.path.join(self.testrundir, 'ldapi') - self.ldapi_uri = "ldapi://%s" % quote_plus(ldapi_path) + if HAVE_LDAPI: + ldapi_path = os.path.join(self.testrundir, 'ldapi') + self.ldapi_uri = "ldapi://%s" % quote_plus(ldapi_path) + self.default_ldap_uri = self.ldapi_uri + # use SASL/EXTERNAL via LDAPI when invoking OpenLDAP CLI tools + self.cli_sasl_external = True + else: + self.ldapi_uri = None + self.default_ldap_uri = self.ldap_uri + # Use simple bind via LDAP uri + self.cli_sasl_external = False # TLS certs self.cafile = os.path.join(HERE, 'certs/ca.pem') self.servercert = os.path.join(HERE, 'certs/server.pem') @@ -331,11 +352,14 @@ def _start_slapd(self): """ Spawns/forks the slapd process """ + urls = [self.ldap_uri] + if self.ldapi_uri: + urls.append(self.ldapi_uri) slapd_args = [ self.PATH_SLAPD, '-f', self._slapd_conf, '-F', self.testrundir, - '-h', '%s' % ' '.join((self.ldap_uri, self.ldapi_uri)), + '-h', '%s' % ' '.join(urls), ] if self._log.isEnabledFor(logging.DEBUG): slapd_args.extend(['-d', '-1']) @@ -346,18 +370,21 @@ def _start_slapd(self): # Waits until the LDAP server socket is open, or slapd crashed # no cover to avoid spurious coverage changes, see # https://github.com/python-ldap/python-ldap/issues/127 - while 1: # pragma: no cover + for _ in range(10): # pragma: no cover if self._proc.poll() is not None: self._stopped() raise RuntimeError("slapd exited before opening port") time.sleep(self._start_sleep) try: - self._log.debug("slapd connection check to %s", self.ldapi_uri) + self._log.debug( + "slapd connection check to %s", self.default_ldap_uri + ) self.ldapwhoami() except RuntimeError: pass else: return + raise RuntimeError("slapd did not start properly") def start(self): """ @@ -435,9 +462,11 @@ def _cli_auth_args(self): # no cover to avoid spurious coverage changes def _cli_popen(self, ldapcommand, extra_args=None, ldap_uri=None, stdin_data=None): # pragma: no cover + if ldap_uri is None: + ldap_uri = self.default_ldap_uri args = [ ldapcommand, - '-H', ldap_uri or self.ldapi_uri, + '-H', ldap_uri, ] + self._cli_auth_args() + (extra_args or []) self._log.debug('Run command: %r', ' '.join(args)) proc = subprocess.Popen( diff --git a/Tests/t_ldap_sasl.py b/Tests/t_ldap_sasl.py index af6ed51a..828fe776 100644 --- a/Tests/t_ldap_sasl.py +++ b/Tests/t_ldap_sasl.py @@ -14,7 +14,8 @@ from ldap.ldapobject import SimpleLDAPObject import ldap.sasl -from slapdtest import SlapdTestCase, requires_sasl, requires_tls +from slapdtest import SlapdTestCase +from slapdtest import requires_ldapi, requires_sasl, requires_tls LDIF = """ @@ -60,7 +61,7 @@ def setUpClass(cls): ) cls.server.ldapadd(ldif) - @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), "needs Unix socket") + @requires_ldapi() def test_external_ldapi(self): # EXTERNAL authentication with LDAPI (AF_UNIX) ldap_conn = self.ldap_object_class(self.server.ldapi_uri) diff --git a/Tests/t_ldap_schema_subentry.py b/Tests/t_ldap_schema_subentry.py index 3c07d35b..4e1e09b2 100644 --- a/Tests/t_ldap_schema_subentry.py +++ b/Tests/t_ldap_schema_subentry.py @@ -16,7 +16,7 @@ from ldap.ldapobject import SimpleLDAPObject import ldap.schema from ldap.schema.models import ObjectClass -from slapdtest import SlapdTestCase +from slapdtest import SlapdTestCase, requires_ldapi HERE = os.path.abspath(os.path.dirname(__file__)) @@ -88,6 +88,7 @@ def test_urlfetch_ldap(self): dn, schema = ldap.schema.urlfetch(self.server.ldap_uri) self.assertSlapdSchema(dn, schema) + @requires_ldapi() def test_urlfetch_ldapi(self): dn, schema = ldap.schema.urlfetch(self.server.ldapi_uri) self.assertSlapdSchema(dn, schema) diff --git a/Tests/t_ldapobject.py b/Tests/t_ldapobject.py index 62591d77..50754687 100644 --- a/Tests/t_ldapobject.py +++ b/Tests/t_ldapobject.py @@ -22,8 +22,8 @@ import unittest import warnings import pickle -import warnings -from slapdtest import SlapdTestCase, requires_sasl, requires_tls +from slapdtest import SlapdTestCase +from slapdtest import requires_ldapi, requires_sasl, requires_tls # Switch off processing .ldaprc or ldap.conf before importing _ldap os.environ['LDAPNOINIT'] = '1' @@ -303,6 +303,7 @@ def test005_invalid_credentials(self): self.fail("expected INVALID_CREDENTIALS, got %r" % r) @requires_sasl() + @requires_ldapi() def test006_sasl_extenal_bind_s(self): l = self.ldap_object_class(self.server.ldapi_uri) l.sasl_external_bind_s() @@ -441,6 +442,7 @@ class Test01_ReconnectLDAPObject(Test00_SimpleLDAPObject): ldap_object_class = ReconnectLDAPObject @requires_sasl() + @requires_ldapi() def test101_reconnect_sasl_external(self): l = self.ldap_object_class(self.server.ldapi_uri) l.sasl_external_bind_s() @@ -450,7 +452,7 @@ def test101_reconnect_sasl_external(self): self.assertEqual(l.whoami_s(), authz_id) def test102_reconnect_simple_bind(self): - l = self.ldap_object_class(self.server.ldapi_uri) + l = self.ldap_object_class(self.server.ldap_uri) bind_dn = 'cn=user1,'+self.server.suffix l.simple_bind_s(bind_dn, 'user1_pw') self.assertEqual(l.whoami_s(), 'dn:'+bind_dn) @@ -458,7 +460,7 @@ def test102_reconnect_simple_bind(self): self.assertEqual(l.whoami_s(), 'dn:'+bind_dn) def test103_reconnect_get_state(self): - l1 = self.ldap_object_class(self.server.ldapi_uri) + l1 = self.ldap_object_class(self.server.ldap_uri) bind_dn = 'cn=user1,'+self.server.suffix l1.simple_bind_s(bind_dn, 'user1_pw') self.assertEqual(l1.whoami_s(), 'dn:'+bind_dn) @@ -477,7 +479,7 @@ def test103_reconnect_get_state(self): str('_start_tls'): 0, str('_trace_level'): 0, str('_trace_stack_limit'): 5, - str('_uri'): self.server.ldapi_uri, + str('_uri'): self.server.ldap_uri, str('bytes_mode'): l1.bytes_mode, str('bytes_mode_hardfail'): l1.bytes_mode_hardfail, str('timeout'): -1, @@ -485,7 +487,7 @@ def test103_reconnect_get_state(self): ) def test104_reconnect_restore(self): - l1 = self.ldap_object_class(self.server.ldapi_uri) + l1 = self.ldap_object_class(self.server.ldap_uri) bind_dn = 'cn=user1,'+self.server.suffix l1.simple_bind_s(bind_dn, 'user1_pw') self.assertEqual(l1.whoami_s(), 'dn:'+bind_dn) diff --git a/tox.ini b/tox.ini index fcfc628b..58e3cf68 100644 --- a/tox.ini +++ b/tox.ini @@ -33,8 +33,8 @@ basepython = python2 deps = {[testenv]deps} passenv = {[testenv]passenv} setenv = - CI_DISABLED=TLS:SASL -# rebuild without SASL and TLS + CI_DISABLED=LDAPI:SASL:TLS +# rebuild without SASL and TLS, run without LDAPI commands = {envpython} \ -m coverage run --parallel setup.py \ clean --all \ From 50e9c2e320c61d39202c8e23a14bc0b4eed13346 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 18 Dec 2017 08:51:06 +0100 Subject: [PATCH 2/4] Remove unused import of pwd module Signed-off-by: Christian Heimes --- Tests/t_ldap_sasl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/t_ldap_sasl.py b/Tests/t_ldap_sasl.py index 828fe776..d1044681 100644 --- a/Tests/t_ldap_sasl.py +++ b/Tests/t_ldap_sasl.py @@ -5,7 +5,6 @@ See https://www.python-ldap.org/ for details. """ import os -import pwd import socket import unittest From f01b66e5ed26b2a80c6c1010f249241100354d50 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 18 Dec 2017 08:57:59 +0100 Subject: [PATCH 3/4] Use PATH to lookup test binaries Instead of hard-coded paths /usr/bin and /usr/sbin, the SlapdObject test helper now uses PATH env var to find test binaries. For slapd server binary, sbin paths are automatically added to lookup PATH in case they are missing. Signed-off-by: Christian Heimes --- Lib/slapdtest/_slapdtest.py | 77 +++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index 09ec1546..ebc54412 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -9,6 +9,7 @@ import os import socket +import sys import time import subprocess import logging @@ -109,6 +110,46 @@ def requires_ldapi(): return identity +def _which(cmd): + """Specialized which command based on shutil.which() from Python 3.6. + + * simplified + * always adds /sbin directories to path + """ + + def _access_check(fn): + return (os.path.exists(fn) and os.access(fn, os.F_OK | os.X_OK) + and not os.path.isdir(fn)) + + # Path with directory part skips PATH lookup. + if os.path.dirname(cmd): + if _access_check(cmd): + return cmd + return None + + path = os.environ.get("PATH", os.defpath).split(os.pathsep) + + if sys.platform == 'win32': + if os.curdir not in path: + path.insert(0, os.curdir) + # include path extension (.exe) + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + files = [cmd + ext for ext in pathext] + else: + # always include sbin for slapd binary + for sbin in ['/usr/local/sbin', '/sbin', '/usr/sbin']: + if sbin not in path: + path.append(sbin) + files = [cmd] + + for directory in path: + for name in files: + name = os.path.join(directory, name) + if _access_check(name): + return name + return None + + def combined_logger( log_name, log_level=logging.WARN, @@ -172,8 +213,6 @@ class SlapdObject(object): ) TMPDIR = os.environ.get('TMP', os.getcwd()) - SBINDIR = os.environ.get('SBIN', '/usr/sbin') - BINDIR = os.environ.get('BIN', '/usr/bin') if 'SCHEMA' in os.environ: SCHEMADIR = os.environ['SCHEMA'] elif os.path.isdir("/etc/openldap/schema"): @@ -182,12 +221,14 @@ class SlapdObject(object): SCHEMADIR = "/etc/ldap/schema" else: SCHEMADIR = None - PATH_LDAPADD = os.path.join(BINDIR, 'ldapadd') - PATH_LDAPDELETE = os.path.join(BINDIR, 'ldapdelete') - PATH_LDAPMODIFY = os.path.join(BINDIR, 'ldapmodify') - PATH_LDAPWHOAMI = os.path.join(BINDIR, 'ldapwhoami') - PATH_SLAPD = os.environ.get('SLAPD', os.path.join(SBINDIR, 'slapd')) - PATH_SLAPTEST = os.path.join(SBINDIR, 'slaptest') + # _check_requirements turns paths into absolute paths + PATH_LDAPADD = 'ldapadd' + PATH_LDAPDELETE = 'ldapdelete' + PATH_LDAPMODIFY = 'ldapmodify' + PATH_LDAPWHOAMI = 'ldapwhoami' + # The following two binaries are usually in /usr/sbin. + PATH_SLAPD = os.environ.get('SLAPD', 'slapd') + PATH_SLAPTEST = 'slaptest' # time in secs to wait before trying to access slapd via LDAP (again) _start_sleep = 1.5 @@ -223,13 +264,19 @@ def __init__(self): self.clientkey = os.path.join(HERE, 'certs/client.key') def _check_requirements(self): - binaries = [ - self.PATH_LDAPADD, self.PATH_LDAPMODIFY, self.PATH_LDAPWHOAMI, - self.PATH_SLAPD, self.PATH_SLAPTEST + names = [ + "PATH_LDAPADD", "PATH_LDAPMODIFY", "PATH_LDAPDELETE", + "PATH_LDAPWHOAMI", "PATH_SLAPD", "PATH_SLAPTEST", ] - for binary in binaries: - if not os.path.isfile(binary): - raise ValueError('Binary {} is missing.'.format(binary)) + for name in names: + value = getattr(self, name) + binary = _which(value) + if binary is None: + raise ValueError( + "Command '{}' not found in PATH".format(value) + ) + else: + setattr(self, name, binary) if self.SCHEMADIR is None: raise ValueError('SCHEMADIR is None, ldap schemas are missing.') @@ -359,7 +406,7 @@ def _start_slapd(self): self.PATH_SLAPD, '-f', self._slapd_conf, '-F', self.testrundir, - '-h', '%s' % ' '.join(urls), + '-h', ' '.join(urls), ] if self._log.isEnabledFor(logging.DEBUG): slapd_args.extend(['-d', '-1']) From 02915b3f5bfe0f93d92cdeb26778d8c3efa56d25 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 19 Dec 2017 14:26:39 +0100 Subject: [PATCH 4/4] slapdtest: Set and check command paths in __init__ Make SlapdObject.PATH_* instance attributes, rather than class ones. Set them in __init__. Move checking them from start() to __init__(), so the check becomes simply error handling. Put a straight-up copy of Python's shutil.which() in ldap.compat -- it is a temporary backport, not a modified fork. --- Lib/ldap/compat.py | 70 +++++++++++++++++++++++ Lib/slapdtest/_slapdtest.py | 109 ++++++++++++++---------------------- 2 files changed, 113 insertions(+), 66 deletions(-) diff --git a/Lib/ldap/compat.py b/Lib/ldap/compat.py index de0e110c..cbfeef57 100644 --- a/Lib/ldap/compat.py +++ b/Lib/ldap/compat.py @@ -1,6 +1,7 @@ """Compatibility wrappers for Py2/Py3.""" import sys +import os if sys.version_info[0] < 3: from UserDict import UserDict, IterableUserDict @@ -41,3 +42,72 @@ def reraise(exc_type, exc_value, exc_traceback): """ # In Python 3, all exception info is contained in one object. raise exc_value + +try: + from shutil import which +except ImportError: + # shutil.which() from Python 3.6 + # "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + # 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software Foundation; + # All Rights Reserved" + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index ebc54412..484eb54b 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -21,7 +21,7 @@ os.environ['LDAPNOINIT'] = '1' import ldap -from ldap.compat import quote_plus +from ldap.compat import quote_plus, which HERE = os.path.abspath(os.path.dirname(__file__)) @@ -109,46 +109,14 @@ def requires_ldapi(): else: return identity - -def _which(cmd): - """Specialized which command based on shutil.which() from Python 3.6. - - * simplified - * always adds /sbin directories to path - """ - - def _access_check(fn): - return (os.path.exists(fn) and os.access(fn, os.F_OK | os.X_OK) - and not os.path.isdir(fn)) - - # Path with directory part skips PATH lookup. - if os.path.dirname(cmd): - if _access_check(cmd): - return cmd - return None - - path = os.environ.get("PATH", os.defpath).split(os.pathsep) - - if sys.platform == 'win32': - if os.curdir not in path: - path.insert(0, os.curdir) - # include path extension (.exe) - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) - files = [cmd + ext for ext in pathext] - else: - # always include sbin for slapd binary - for sbin in ['/usr/local/sbin', '/sbin', '/usr/sbin']: - if sbin not in path: - path.append(sbin) - files = [cmd] - - for directory in path: - for name in files: - name = os.path.join(directory, name) - if _access_check(name): - return name - return None - +def _add_sbin(path): + """Add /sbin and related directories to a command search path""" + directories = path.split(os.pathsep) + if sys.platform != 'win32': + for sbin in '/usr/local/sbin', '/sbin', '/usr/sbin': + if sbin not in directories: + directories.append(sbin) + return os.pathsep.join(directories) def combined_logger( log_name, @@ -221,14 +189,9 @@ class SlapdObject(object): SCHEMADIR = "/etc/ldap/schema" else: SCHEMADIR = None - # _check_requirements turns paths into absolute paths - PATH_LDAPADD = 'ldapadd' - PATH_LDAPDELETE = 'ldapdelete' - PATH_LDAPMODIFY = 'ldapmodify' - PATH_LDAPWHOAMI = 'ldapwhoami' - # The following two binaries are usually in /usr/sbin. - PATH_SLAPD = os.environ.get('SLAPD', 'slapd') - PATH_SLAPTEST = 'slaptest' + + BIN_PATH = os.environ.get('BIN', os.environ.get('PATH', os.defpath)) + SBIN_PATH = os.environ.get('SBIN', _add_sbin(BIN_PATH)) # time in secs to wait before trying to access slapd via LDAP (again) _start_sleep = 1.5 @@ -256,6 +219,12 @@ def __init__(self): self.default_ldap_uri = self.ldap_uri # Use simple bind via LDAP uri self.cli_sasl_external = False + + self._find_commands() + + if self.SCHEMADIR is None: + raise ValueError('SCHEMADIR is None, ldap schemas are missing.') + # TLS certs self.cafile = os.path.join(HERE, 'certs/ca.pem') self.servercert = os.path.join(HERE, 'certs/server.pem') @@ -263,22 +232,31 @@ def __init__(self): self.clientcert = os.path.join(HERE, 'certs/client.pem') self.clientkey = os.path.join(HERE, 'certs/client.key') - def _check_requirements(self): - names = [ - "PATH_LDAPADD", "PATH_LDAPMODIFY", "PATH_LDAPDELETE", - "PATH_LDAPWHOAMI", "PATH_SLAPD", "PATH_SLAPTEST", - ] - for name in names: - value = getattr(self, name) - binary = _which(value) - if binary is None: - raise ValueError( - "Command '{}' not found in PATH".format(value) - ) - else: - setattr(self, name, binary) - if self.SCHEMADIR is None: - raise ValueError('SCHEMADIR is None, ldap schemas are missing.') + def _find_commands(self): + self.PATH_LDAPADD = self._find_command('ldapadd') + self.PATH_LDAPDELETE = self._find_command('ldapdelete') + self.PATH_LDAPMODIFY = self._find_command('ldapmodify') + self.PATH_LDAPWHOAMI = self._find_command('ldapwhoami') + + self.PATH_SLAPD = os.environ.get('SLAPD', None) + if not self.PATH_SLAPD: + self.PATH_SLAPD = self._find_command('slapd', in_sbin=True) + self.PATH_SLAPTEST = self._find_command('slaptest', in_sbin=True) + + def _find_command(self, cmd, in_sbin=False): + if in_sbin: + path = self.SBIN_PATH + var_name = 'SBIN' + else: + path = self.BIN_PATH + var_name = 'BIN' + command = which(cmd, path=path) + if command is None: + raise ValueError( + "Command '{}' not found. Set the {} environment variable to " + "override slapdtest's search path.".format(value, var_name) + ) + return command def setup_rundir(self): """ @@ -439,7 +417,6 @@ def start(self): """ if self._proc is None: - self._check_requirements() # prepare directory structure atexit.register(self.stop) self._cleanup_rundir() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy