From 6a0b8143150a04762ca0ba041bbdd0680c111a91 Mon Sep 17 00:00:00 2001
From: Anuj Borah <aborah@redhat.com>
Date: Thu, 23 Jul 2020 11:48:24 +0530
Subject: [PATCH] libdirsrv should be modified to be compatible with new DS

Directory Server 1.4.x

As DS is going to get rid of setup-ds.pl file completely. We need to change the same on our side too.
---
 src/tests/multihost/basic/conftest.py         |   4 +-
 .../python/sssd/testlib/common/libdirsrv.py   | 143 ++++++++++++------
 src/tests/python/sssd/testlib/common/utils.py |  21 ++-
 3 files changed, 110 insertions(+), 58 deletions(-)

diff --git a/src/tests/multihost/basic/conftest.py b/src/tests/multihost/basic/conftest.py
index b30d25aa84..413d1d7b18 100644
--- a/src/tests/multihost/basic/conftest.py
+++ b/src/tests/multihost/basic/conftest.py
@@ -38,7 +38,7 @@ def package_install(session_multihost):
     distro = session_multihost.master[0].distro
     pkg_list = 'authselect nss-tools 389-ds-base krb5-server '\
                'openldap-clients krb5-workstation '\
-               '389-ds-base-legacy-tools sssd sssd-dbus sssd-kcm ' \
+               'sssd sssd-dbus sssd-kcm ' \
                'expect ldb-tools sssd-tools'
     if 'Fedora' in distro:
         cmd = 'dnf install -y %s' % (pkg_list)
@@ -281,7 +281,7 @@ def enable_files_domain(session_multihost):
     """
     session_multihost.master[0].transport.get_file('/etc/sssd/sssd.conf',
                                                    '/tmp/sssd.conf')
-    sssdconfig = ConfigParser.SafeConfigParser()
+    sssdconfig = ConfigParser.RawConfigParser(delimiters=('='))
     sssdconfig.read('/tmp/sssd.conf')
     sssd_section = 'sssd'
     if sssd_section in sssdconfig.sections():
diff --git a/src/tests/python/sssd/testlib/common/libdirsrv.py b/src/tests/python/sssd/testlib/common/libdirsrv.py
index 43d73d7c18..7729e2426e 100644
--- a/src/tests/python/sssd/testlib/common/libdirsrv.py
+++ b/src/tests/python/sssd/testlib/common/libdirsrv.py
@@ -14,10 +14,10 @@
 from sssd.testlib.common.exceptions import LdapException
 from sssd.testlib.common.utils import LdapOperations
 
-DS_USER = 'nobody'
-DS_GROUP = 'nobody'
+DS_USER = 'dirsrv'
+DS_GROUP = 'dirsrv'
 DS_ADMIN = 'admin'
-DS_ROOTDN = 'CN=Directory Manager'
+DS_ROOTDN = 'cn=Directory Manager'
 
 
 class DirSrv(object):
@@ -51,7 +51,7 @@ def __repr__(self):
                                self.__dict__)
 
     def create_config(self):
-        """create config file for setup-ds.pl to setup DS instances.
+        """create inf file for dscreate to setup DS instances.
 
         Args:
             param1 (None):
@@ -64,17 +64,21 @@ def create_config(self):
         """
         config = ConfigParser.RawConfigParser()
         config.optionxform = str
-        config.add_section('General')
-        config.set('General', 'FullMachineName', self.dsinstance_host)
-        config.set('General', 'SuiteSpotUserID', DS_USER)
-        config.set('General', 'SuiteSpotGroup', DS_GROUP)
-        config.set('General', 'ConfigDirectoryAdminID', DS_ADMIN)
+        config.add_section('general')
+        config.set('general', 'full_machine_name', self.dsinstance_host)
+        config.set('general', 'user', DS_USER)
+        config.set('general', 'group', DS_GROUP)
         config.add_section('slapd')
-        config.set('slapd', 'ServerIdentifier', self.instance_name)
-        config.set('slapd', 'ServerPort', self.dsldap_port)
-        config.set('slapd', 'Suffix', self.dsinstance_suffix)
-        config.set('slapd', 'RootDN', self.dsrootdn)
-        config.set('slapd', 'RootDNPwd', self.dsrootdn_pwd)
+        config.set('slapd', 'instance_name', self.instance_name)
+        config.set('slapd', 'port', self.dsldap_port)
+        config.set('slapd', 'suffix', self.dsinstance_suffix)
+        config.set('slapd', 'root_dn', self.dsrootdn)
+        config.set('slapd', 'root_password', self.dsrootdn_pwd)
+        config.set('slapd', 'self_sign_cert', False)
+        config.add_section('backend-userRoot')
+        config.set('backend-userRoot', 'create_suffix_entry', 'True')
+        config.set('backend-userRoot', 'sample_entries', '001003006')
+        config.set('backend-userRoot', 'suffix', self.dsinstance_suffix)
 
         (ds_config, ds_config_file_path) = tempfile.mkstemp(suffix='cfg')
         os.close(ds_config)
@@ -83,23 +87,23 @@ def create_config(self):
         return ds_config_file_path
 
     def setup_ds(self, ds_cfg_file):
-        """create DS instance by running setup-ds.pl.
+        """create DS instance by running dscreate
 
         Args:
              ds_config_file (str): ds_config_file: Configuration File path
 
         Returns:
-             bool: True if setup-ds.pl ran successfully else False
+             bool: True if dscreate ran successfully else False
 
         Exceptions:
              subprocess.CalledProcessError:
         """
         self.multihost.transport.put_file(ds_cfg_file, '/tmp/test.cfg')
-        setup_args = ['setup-ds.pl', '--silent',
-                      '--file=/tmp/test.cfg', '--debug']
+        setup_cmd = 'dscreate -v from-file %s' % '/tmp/test.cfg'
         try:
-            self.multihost.run_command(setup_args)
+            self.multihost.run_command(setup_cmd)
         except subprocess.CalledProcessError:
+            self.multihost.log.info("Failed to setup Directory Server")
             raise
         else:
             os.remove(ds_cfg_file)
@@ -119,17 +123,18 @@ def remove_ds(self, inst_name=None):
         """
         if inst_name is None:
             inst_name = self.ds_inst_name
-        remove_args = ['remove-ds.pl', '-i', inst_name, '-d']
+        remove_cmd = 'dsctl %s remove --do-it' % (inst_name)
         try:
-            self.multihost.run_command(remove_args)
+            self.multihost.run_command(remove_cmd)
         except subprocess.CalledProcessError:
+            self.multihost.log.info("Failed to remove %s instance" % inst_name)
             raise
 
     def _copy_pkcs12(self, ssl_dir):
         """ Copy the pkcs12 files from ssl_dir to
         DS instance directory """
-
-        nss_db_files = ['ca.p12', 'server.p12', 'pin.txt', 'pwfile']
+        server_p12 = '%s-server.p12' % self.multihost.sys_hostname
+        nss_db_files = ['ca.p12', 'pin.txt', 'pwfile', server_p12]
         for db_file in nss_db_files:
             source = os.path.join(ssl_dir, db_file)
             destination = os.path.join(self.dsinst_path, db_file)
@@ -158,18 +163,18 @@ def _set_dsperms(self, file_path):
             self.multihost.run_command(change_ownership)
         except subprocess.CalledProcessError:
             raise DirSrvException(
-                'fail to user change ownership of pin.txt fail')
+                'Failed to change ownership of pin.txt')
         try:
             self.multihost.run_command(change_group)
         except subprocess.CalledProcessError:
             raise DirSrvException(
-                'fail to change group ownership of pin.txt file')
+                'Failed to change group ownership of pin.txt')
         try:
             self.multihost.run_command(chmod_file)
         except subprocess.CalledProcessError:
-            raise DirSrvException('fail to change permissions of pin.txt file')
+            raise DirSrvException('Failed to change permissions of pin.txt')
 
-    def setup_certs(self, ssl_dir):
+    def setup_certs(self, ssl_dir, client_host=None, canick=None):
         """copy CA and Server certs to all DS instances.
 
         Args:
@@ -189,7 +194,7 @@ def setup_certs(self, ssl_dir):
         try:
             self.multihost.run_command(stop_ds)
         except subprocess.CalledProcessError:
-            raise DirSrvException("Unable to stop Directory Server instance")
+            raise DirSrvException("Failed to stop Directory Server instance")
         else:
             self.multihost.log.info('DS instance stopped successfully')
             self._copy_pkcs12(ssl_dir)
@@ -197,23 +202,30 @@ def setup_certs(self, ssl_dir):
         target_pin_file = os.path.join(self.dsinst_path, 'pin.txt')
         pwfile = os.path.join(self.dsinst_path, 'pwfile')
         ca_p12 = os.path.join(self.dsinst_path, 'ca.p12')
-        server_p12 = os.path.join(self.dsinst_path, 'server.p12')
-        # recreate the database
-        certutil_cmd = 'certutil -N -d %s -f %s' % (self.dsinst_path, pwfile)
+        server_p12_name = '%s-%s' % (self.multihost.sys_hostname, 'server.p12')
+        server_p12 = os.path.join(self.dsinst_path, server_p12_name)
+        # create directory to copy ca cert
+        certutil_cmd = 'certutil -T -d %s -f %s' % (self.dsinst_path, pwfile)
         self.multihost.run_command(certutil_cmd)
         create_cert_dir = 'mkdir -p /etc/openldap/cacerts'
         # recreate the database
         self.multihost.run_command(create_cert_dir)
+        if not canick:
+            canick = "ExampleCA"
         pkcs12_file = [ca_p12, server_p12]
         for pkcs_file in pkcs12_file:
             if not self._import_certs(pkcs_file, pwfile):
                 raise DirSrvException("importing certificates failed")
-        set_trust_cmd = 'certutil -M -d %s -n "ExampleCA"'\
-                        ' -t "CTu,u,u" -f %s' % (self.dsinst_path, pwfile)
+        set_trust_cmd = 'certutil -M -d %s -n %s -t ' \
+                        '"CTu,u,u" -f %s' % (self.dsinst_path, canick, pwfile)
         self.multihost.run_command(create_cert_dir)
         self.multihost.run_command(set_trust_cmd)
         self.multihost.transport.put_file(os.path.join(
             ssl_dir, 'cacert.pem'), cacert_file_path)
+        if client_host:
+            client_host.run_command(create_cert_dir)
+            client_host.transport.put_file(
+                os.path.join(ssl_dir, 'cacert.pem'), cacert_file_path)
         try:
             self._set_dsperms(target_pin_file)
         except DirSrvException:
@@ -222,7 +234,7 @@ def setup_certs(self, ssl_dir):
         try:
             self.multihost.run_command(start_ds)
         except subprocess.CalledProcessError:
-            raise DirSrvException('Could not Start DS Instance')
+            raise DirSrvException('Failed to start DS Instance')
         else:
             self.multihost.log.info('DS instance started successfully')
 
@@ -246,7 +258,7 @@ def enable_ssl(self, binduri, tls_port):
         add_tls = [(ldap.MOD_ADD, 'nsTLS1', [b'on'])]
         (ret, return_value) = ldap_obj.modify_ldap(mod_dn1, add_tls)
         if not return_value:
-            raise LdapException('fail to enable TLS, Error:%s' % (ret))
+            raise LdapException('Failed to enable TLS, Error:%s' % (ret))
         else:
             print('Enabled nsTLS1=on')
         mod_dn2 = 'cn=RSA,cn=encryption,cn=config'
@@ -255,7 +267,7 @@ def enable_ssl(self, binduri, tls_port):
                           ((self.dsinstance_host.encode()))])]
         (ret, return_value) = ldap_obj.modify_ldap(mod_dn2, mod_security)
         if not return_value:
-            raise LdapException('fail to set Server-Cert nick:%s' % (ret))
+            raise LdapException('Failed to set Server-Cert nick:%s' % (ret))
         else:
             print('Enabled Server-Cert nick')
 
@@ -265,7 +277,7 @@ def enable_ssl(self, binduri, tls_port):
         (ret, return_value) = ldap_obj.modify_ldap(mod_dn3, enable_security)
         if not return_value:
             raise LdapException(
-                'fail to enable nsslapd-security, Error:%s' % (ret))
+                'Failed to enable nsslapd-security, Error:%s' % (ret))
         else:
             print('Enabled nsslapd-security')
 
@@ -276,10 +288,35 @@ def enable_ssl(self, binduri, tls_port):
         (ret, return_value) = ldap_obj.modify_ldap(mod_dn4, enable_ssl_port)
         if not return_value:
             raise LdapException(
-                'fail to set nsslapd-securePort, Error:%s' % (ret))
+                'Failed to set nsslapd-securePort, Error:%s' % (ret))
         else:
             print('Enabled nsslapd-securePort=%r' % tls_port)
 
+    def enable_anonymous_search(self, binduri):
+        """Enable anonymous search access to basedn
+        Args:
+            binduri (str): LDAP uri to bind with
+        Returns:
+            boold: True if ACI is added
+        Exceptions:
+            LdapException
+        """
+        ldap_obj = LdapOperations(
+            uri=binduri, binddn=self.dsrootdn, bindpw=self.dsrootdn_pwd)
+        # Enable Anonymous access aci
+        allow_anonymous = "(targetattr!=\"userPassword || aci\")" \
+                          "(version 3.0; acl \"Enable anonymous " \
+                          "access\"; allow " \
+                          "(read, search, compare) userdn=\"ldap:///anyone\";)"
+        add_aci = [(ldap.MOD_ADD, 'aci', [allow_anonymous.encode('utf-8')])]
+        (ret, return_value) = ldap_obj.modify_ldap(
+            self.dsinstance_suffix, add_aci)
+        if not return_value:
+            raise LdapException("Failed to enable anonymous access aci")
+        else:
+            print("Enabled Anonymous access "
+                  "aci to %s" % self.dsinstance_suffix)
+
 
 class DirSrvWrap(object):
     """This is a wrapper class for DirSrv.
@@ -289,7 +326,8 @@ class DirSrvWrap(object):
     LDAP and TLS ports, specifies default suffix.
     """
     # pylint: disable=too-many-instance-attributes
-    def __init__(self, multihost_obj, ssl=None, ssldb=None):
+    def __init__(self, multihost_obj,
+                 client_obj=None, ssl=None, ssldb=None, canick=None):
         """
         Create a DirSrv object for a specific Host. Specify the ports,
         instance details to the Dirsrv object
@@ -305,6 +343,7 @@ def __init__(self, multihost_obj, ssl=None, ssldb=None):
         self.ds_instance_name = None
         self.multihost = multihost_obj
         self.ds_instance_host = self.multihost.sys_hostname
+        self.client_host = client_obj
         self.ds_instance_suffix = None
         self.ds_rootdn_pwd = None
         self.ds_ldap_port = None
@@ -312,6 +351,7 @@ def __init__(self, multihost_obj, ssl=None, ssldb=None):
         self.ssl = ssl
         if self.ssl:
             self.ssl_dir = ssldb
+        self.canick = canick
 
     def __iter__(self):
         """ iter values of each instance """
@@ -467,7 +507,7 @@ def create_ds_instance(self,
                            root_dn_pwd=None,
                            ldap_port=None,
                            tls_port=None):
-        """create Directory server instance.
+        """Create Directory server instance.
 
         Args:
             inst_name (str): Instance Name
@@ -500,18 +540,25 @@ def create_ds_instance(self,
             try:
                 self.dirsrv_obj.setup_ds(cfg_file)
             except subprocess.CalledProcessError:
-                raise DirSrvException('fail to DS config file to setup')
+                raise DirSrvException('Failed to setup Directory server')
             self.dirsrv_info[self.ds_instance_name] = self.dirsrv_obj.__dict__
+            ldap_uri = 'ldap://%s:%r' % (self.ds_instance_host,
+                                         self.ds_ldap_port)
+            try:
+                self.dirsrv_obj.enable_anonymous_search(ldap_uri)
+            except LdapException:
+                raise DirSrvException("Failed to enable anonymous search")
             if self.ssl:
                 try:
-                    self.dirsrv_obj.setup_certs(self.ssl_dir)
+                    self.dirsrv_obj.setup_certs(self.ssl_dir,
+                                                self.client_host, self.canick)
                 except DirSrvException as err:
                     return err.msg, err.rval
                 else:
                     (result, return_code) = self.enablessl()
             return result, return_code
         else:
-            raise DirSrvException('fail to setup Directory Server instance')
+            raise DirSrvException('Failed to setup Directory Server instance')
 
     def enablessl(self):
         """Enable SSL/TLS on instance.
@@ -539,7 +586,7 @@ def enablessl(self):
             try:
                 self.multihost.run_command(add_tls_port)
             except subprocess.CalledProcessError:
-                return "Unable to set tls_port as ldap_port_t", 1
+                return "Failed to set tls_port as ldap_port_t", 1
             else:
                 self.multihost.log.info('Added %s port to ldap_port_t' %
                                         self.ds_tls_port)
@@ -581,12 +628,12 @@ def remove_ds_instance(self, instance_name):
         """
         ret = self.dirsrv_info[instance_name]
         if ret['instance_name'] == instance_name:
-            ds_inst_name = ret['ds_inst_name']
+            inst_name = ret['ds_inst_name']
             try:
-                self.dirsrv_obj.remove_ds(ds_inst_name)
+                self.dirsrv_obj.remove_ds(inst_name)
             except subprocess.CalledProcessError:
-                raise DirSrvException('Could not remove DS Instance',
-                                      ds_inst_name)
+                raise DirSrvException('Failed to '
+                                      'remove %s instance', inst_name)
             else:
                 del self.ds_used_ports[instance_name]
                 return True
diff --git a/src/tests/python/sssd/testlib/common/utils.py b/src/tests/python/sssd/testlib/common/utils.py
index b15d230583..4dabb787c6 100644
--- a/src/tests/python/sssd/testlib/common/utils.py
+++ b/src/tests/python/sssd/testlib/common/utils.py
@@ -692,7 +692,7 @@ def __init__(self, nssdir=None, nssdir_pwd=None):
         else:
             self.nssdb = nssdir
         if nssdir_pwd is None:
-            self.nssdb_pwd = 'Secret123'
+            self.nssdb_pwd = 'Secret12@38/-\245550'
         else:
             self.nssdb_pwd = nssdir_pwd
         self.pwdfilename = 'pwfile'
@@ -764,7 +764,7 @@ def execute(self,
     def createselfsignedcerts(self,
                               serverlist,
                               ca_dn=None,
-                              passphrase='Secret123',
+                              passphrase='Secret12@38/-\245550',
                               canickname='ExampleCA'):
         """
         Creates a NSS DB in /tmp/nssDirxxxx where self signed Root CA
@@ -782,13 +782,17 @@ def createselfsignedcerts(self,
         ca_pempath = os.path.join(nss_dir, 'cacert.pem')
         server_pempath = os.path.join(nss_dir, 'server.pem')
         ca_p12_path = os.path.join(nss_dir, 'ca.p12')
-        server_p12_path = os.path.join(nss_dir, 'server.p12')
+        # server_p12_path = os.path.join(nss_dir, 'server.p12')
         with open(self.noisefilepath, 'w') as outfile:
             outfile.write(str(self.noise))
+        keyUsage = 'digitalSignature,certSigning,crlSigning,critical'
         ca_args = 'certutil -d %s -f %s -S -n "%s" -s %s' \
-                  ' -t "CT,," -x -z %s' % (nss_dir, self.pwdfilepath,
-                                           canickname, ca_dn,
-                                           self.noisefilepath)
+                  ' -t "CT,," -x --keyUsage %s -z %s' % (nss_dir,
+                                                         self.pwdfilepath,
+                                                         canickname,
+                                                         ca_dn,
+                                                         keyUsage,
+                                                         self.noisefilepath)
 
         ca_pem = 'certutil -d %s -f %s -L -n "%s"' \
                  ' -a -o %s' % (nss_dir, self.pwdfilepath,
@@ -829,9 +833,10 @@ def createselfsignedcerts(self,
                                 ' -k %s -w %s' % (nss_dir, ca_p12_path,
                                                   canickname, self.pwdfilepath,
                                                   self.pwdfilepath)
-                _, _, return_code = self.execute(shlex.split(export_ca_p12))
+                server_pkcs12_file = '%s-%s' % (server, 'server.p12')
+                server_p12 = os.path.join(nss_dir, server_pkcs12_file)
                 export_svr_p12 = 'pk12util -d %s -o %s -n %s'\
-                                 ' -k %s -w %s' % (nss_dir, server_p12_path,
+                                 ' -k %s -w %s' % (nss_dir, server_p12,
                                                    server_nickname,
                                                    self.pwdfilepath,
                                                    self.pwdfilepath)
