From 57493b1808502a877bf14ac8a88ca770c0b7e44b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 8 Mar 2022 11:25:50 -0800 Subject: [PATCH 1/2] Look for slapadd in /sbin as well Debian puts the slap* utilities under /usr/sbin https://packages.debian.org/file:slapadd --- Lib/slapdtest/_slapdtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index 36841110..105816be 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -259,7 +259,7 @@ def _find_commands(self): self.PATH_LDAPDELETE = self._find_command('ldapdelete') self.PATH_LDAPMODIFY = self._find_command('ldapmodify') self.PATH_LDAPWHOAMI = self._find_command('ldapwhoami') - self.PATH_SLAPADD = self._find_command('slapadd') + self.PATH_SLAPADD = self._find_command('slapadd', in_sbin=True) self.PATH_SLAPD = os.environ.get('SLAPD', None) if not self.PATH_SLAPD: From d32007e8d921bf13549dd40a0f11ac99507bc9d0 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 10 Apr 2022 09:35:56 -0700 Subject: [PATCH 2/2] slapdtest: changes to CLI argument handling Use 'slapd -Tadd' instead of 'slapadd'. Separate logic for ldap* vs. slapd wrt. authentication and allow override of user/password. All CLI methods return captured output. --- Doc/spelling_wordlist.txt | 1 + Lib/slapdtest/_slapdtest.py | 94 +++++++++++++++++++++++-------------- 2 files changed, 60 insertions(+), 35 deletions(-) diff --git a/Doc/spelling_wordlist.txt b/Doc/spelling_wordlist.txt index e6c2aedd..a9ca5dd5 100644 --- a/Doc/spelling_wordlist.txt +++ b/Doc/spelling_wordlist.txt @@ -76,6 +76,7 @@ ldapControls ldapControlTuples ldapdelete ldapi +ldapmodify LDAPObject ldaps ldapurl diff --git a/Lib/slapdtest/_slapdtest.py b/Lib/slapdtest/_slapdtest.py index 105816be..2e27b09d 100644 --- a/Lib/slapdtest/_slapdtest.py +++ b/Lib/slapdtest/_slapdtest.py @@ -259,7 +259,6 @@ def _find_commands(self): self.PATH_LDAPDELETE = self._find_command('ldapdelete') self.PATH_LDAPMODIFY = self._find_command('ldapmodify') self.PATH_LDAPWHOAMI = self._find_command('ldapwhoami') - self.PATH_SLAPADD = self._find_command('slapadd', in_sbin=True) self.PATH_SLAPD = os.environ.get('SLAPD', None) if not self.PATH_SLAPD: @@ -483,34 +482,57 @@ def _stopped(self): self._log.info('slapd[%d] terminated', self._proc.pid) self._proc = None - def _cli_auth_args(self): - if self.cli_sasl_external: - authc_args = [ - '-Y', 'EXTERNAL', - ] - if not self._log.isEnabledFor(logging.DEBUG): - authc_args.append('-Q') - else: - authc_args = [ - '-x', - '-D', self.root_dn, - '-w', self.root_pw, - ] - return authc_args + def _cli_slap(self, cmd=None, extra_args=None, stdin_data=None): # pragma: no cover + """Entry point for slap* commands directly modifying DB files.""" + if self._proc is not None: + self._log.warning('Directly modifying DB files while slapd is running') + + args = [self.PATH_SLAPD] + + if cmd: + args.append('-T'+cmd) + + args += ['-F', self._slapd_conf] + (extra_args or []) + + return self._cli_exec(args, stdin_data=stdin_data) + + def _cli_ldap(self, ldapcommand, extra_args=None, ldap_uri=None, + bind_dn=None, bind_pw=None, + stdin_data=None): # pragma: no cover + """ + Entry point for ldap* commands, interacting with running slapd + """ + if self._proc is None: + raise RuntimeError('Must start daemon before LDAP access is possible') - # 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 - if ldapcommand.split("/")[-1].startswith("ldap"): - args = [ldapcommand, '-H', ldap_uri] + self._cli_auth_args() + args = [ldapcommand, '-H', ldap_uri or self.default_ldap_uri] + + if ldap_uri.startswith('ldapi://'): + args += ['-Y', 'EXTERNAL'] + + if not self._log.isEnabledFor(logging.DEBUG): + args.append('-Q') + + if bind_dn or bind_pw: + raise RuntimeError('-Y EXTERNAL ignores -D and -w') + else: - args = [ldapcommand, '-F', self._slapd_conf] + args += [ + '-x', + '-D', bind_dn or self.root_dn, + '-w', bind_pw or self.root_pw, + ] - args += (extra_args or []) + if extra_args: + args += extra_args + return self._cli_exec(args, stdin_data=stdin_data) + + # no cover to avoid spurious coverage changes + def _cli_exec(self, args, stdin_data=None): # pragma: no cover self._log.debug('Run command: %r', ' '.join(args)) proc = subprocess.Popen( args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, @@ -530,27 +552,28 @@ def _cli_popen(self, ldapcommand, extra_args=None, ldap_uri=None, ) return stdout_data, stderr_data - def ldapwhoami(self, extra_args=None): + def ldapwhoami(self, extra_args=None, **kws): """ Runs ldapwhoami on this slapd instance """ - self._cli_popen(self.PATH_LDAPWHOAMI, extra_args=extra_args) + return self._cli_ldap(self.PATH_LDAPWHOAMI, extra_args=extra_args, + **kws) - def ldapadd(self, ldif, extra_args=None): + def ldapadd(self, ldif, extra_args=None, **kws): """ Runs ldapadd on this slapd instance, passing it the ldif content """ - self._cli_popen(self.PATH_LDAPADD, extra_args=extra_args, - stdin_data=ldif.encode('utf-8')) + return self._cli_ldap(self.PATH_LDAPADD, extra_args=extra_args, + stdin_data=ldif.encode('utf-8'), **kws) - def ldapmodify(self, ldif, extra_args=None): + def ldapmodify(self, ldif, extra_args=None, **kws): """ - Runs ldapadd on this slapd instance, passing it the ldif content + Runs ldapmodify on this slapd instance, passing it the ldif content """ - self._cli_popen(self.PATH_LDAPMODIFY, extra_args=extra_args, - stdin_data=ldif.encode('utf-8')) + return self._cli_ldap(self.PATH_LDAPMODIFY, extra_args=extra_args, + stdin_data=ldif.encode('utf-8'), **kws) - def ldapdelete(self, dn, recursive=False, extra_args=None): + def ldapdelete(self, dn, recursive=False, extra_args=None, **kws): """ Runs ldapdelete on this slapd instance, deleting 'dn' """ @@ -559,14 +582,15 @@ def ldapdelete(self, dn, recursive=False, extra_args=None): if recursive: extra_args.append('-r') extra_args.append(dn) - self._cli_popen(self.PATH_LDAPDELETE, extra_args=extra_args) + return self._cli_ldap(self.PATH_LDAPDELETE, extra_args=extra_args, + **kws) def slapadd(self, ldif, extra_args=None): """ Runs slapadd on this slapd instance, passing it the ldif content """ - self._cli_popen( - self.PATH_SLAPADD, + return self._cli_slap( + 'add', stdin_data=ldif.encode("utf-8") if ldif else None, extra_args=extra_args, )