summaryrefslogtreecommitdiffstats
path: root/src/tests/intg/ds_openldap.py
diff options
context:
space:
mode:
authorNikolai Kondrashov <Nikolai.Kondrashov@redhat.com>2014-11-24 19:13:16 +0200
committerJakub Hrozek <jhrozek@redhat.com>2015-05-28 13:55:52 +0200
commit9d453f1e8b28983b363b44c49b7cd701a994fd97 (patch)
treef681e8183b68cfcca3e7b618b119238489b46cce /src/tests/intg/ds_openldap.py
parent9c5e4ae08ea41f9b1cdb3b3d0e9c35056baeab86 (diff)
downloadsssd-9d453f1e8b28983b363b44c49b7cd701a994fd97.tar.gz
sssd-9d453f1e8b28983b363b44c49b7cd701a994fd97.tar.xz
sssd-9d453f1e8b28983b363b44c49b7cd701a994fd97.zip
Add integration tests
Add "intgcheck" make target. Update CI to use it. The "intgcheck" target configures and builds sssd in a sub-directory, installs it into a prefix in another sub-directory, and then makes the "intgcheck-installed" target from within src/tests/intg in that separate build. The "intgcheck-installed" target in src/tests/intg runs py.test for all tests it can find in that directory, under fakeroot and nss_wrapper/uid_wrapper environments emulating running under root. It also adds the value of INTGCHECK_PYTEST_ARGS environment/make variable to the py.test command line. You can use it to pass additional py.test options, such as specifying a subset of tests to run. See "py.test --help" output. There are only two test suites in src/tests/intg at the moment: ent_test.py and ldap_test.py. The ent_test.py runs tests on ent.py - a module of assertion functions for checking entries in NSS database (passwd and group), for use in actual tests. The ent_test.py suite can be used as ent.py usage reference. The ldap_test.py suite sets up and starts a slapd instance, adds a few user and group entries, configures and starts sssd and verifies that those users and groups are retrieved correctly using various NSS functions. The tests are very basic at the moment. Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com> Reviewed-by: Michal Židek <mzidek@redhat.com>
Diffstat (limited to 'src/tests/intg/ds_openldap.py')
-rw-r--r--src/tests/intg/ds_openldap.py279
1 files changed, 279 insertions, 0 deletions
diff --git a/src/tests/intg/ds_openldap.py b/src/tests/intg/ds_openldap.py
new file mode 100644
index 000000000..c58e53a2a
--- /dev/null
+++ b/src/tests/intg/ds_openldap.py
@@ -0,0 +1,279 @@
+#
+# OpenLDAP directory server instance class
+#
+# Copyright (c) 2015 Red Hat, Inc.
+# Author: Nikolai Kondrashov <Nikolai.Kondrashov@redhat.com>
+#
+# This is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import hashlib
+import base64
+import urllib
+import time
+import ldap
+import os
+import errno
+import signal
+import shutil
+import sys
+from util import *
+from ds import DS
+
+def hash_password(password):
+ """Generate userPassword value for a password."""
+ salt = os.urandom(4)
+ hash = hashlib.sha1(password)
+ hash.update(salt)
+ return "{SSHA}" + base64.standard_b64encode(hash.digest() + salt)
+
+class DSOpenLDAP(DS):
+ """OpenLDAP directory server instance."""
+
+ def __init__(self, dir, port, base_dn, admin_rdn, admin_pw):
+ """
+ Initialize the instance.
+
+ Arguments:
+ dir Path to the root of the filesystem hierarchy to create
+ the instance under.
+ port TCP port on localhost to bind the server to.
+ base_dn Base DN.
+ admin_rdn Administrator DN, relative to BASE_DN.
+ admin_pw Administrator password.
+ """
+ DS.__init__(self, dir, port, base_dn, admin_rdn, admin_pw)
+ self.run_dir = self.dir + "/var/run/ldap"
+ self.pid_path = self.run_dir + "/slapd.pid"
+ self.conf_dir = self.dir + "/etc/ldap"
+ self.conf_slapd_d_dir = self.conf_dir + "/slapd.d"
+ self.data_dir = self.dir + "/var/lib/ldap"
+
+ def _setup_config(self):
+ """Setup the instance initial configuration."""
+ dist_lib_dir = first_dir("/usr/lib/openldap",
+ "/usr/lib64/openldap",
+ "/usr/lib/ldap")
+ dist_conf_dir = first_dir("/etc/ldap",
+ "/etc/openldap")
+ args_file = self.run_dir + "/slapd.args"
+ admin_pw_hash = hash_password(self.admin_pw)
+ uid = os.geteuid()
+ gid = os.getegid()
+
+ #
+ # Add configuration
+ #
+ config = unindent("""
+ dn: cn=config
+ objectClass: olcGlobal
+ cn: config
+ olcPidFile: {self.pid_path}
+ olcArgsFile: {args_file}
+ # Read slapd.conf(5) for possible values
+ olcLogLevel: none
+
+ # Frontend settings
+ dn: olcDatabase={{-1}}frontend,cn=config
+ objectClass: olcDatabaseConfig
+ objectClass: olcFrontendConfig
+ olcDatabase: {{-1}}frontend
+ # The maximum number of entries that is returned for
+ # a search operation
+ olcSizeLimit: 500
+ # Allow unlimited access to local connection from the local root
+ olcAccess: {{0}}to * by dn.exact=gidNumber={gid}+uidNumber={uid},
+ cn=peercred,cn=external,cn=auth manage by * break
+ # Allow unauthenticated read access for schema and
+ # base DN autodiscovery
+ olcAccess: {{1}}to dn.exact="" by * read
+ olcAccess: {{2}}to dn.base="cn=Subschema" by * read
+
+ # Config db settings
+ dn: olcDatabase=config,cn=config
+ objectClass: olcDatabaseConfig
+ olcDatabase: config
+ # Allow unlimited access to local connection from the local root
+ olcAccess: to * by dn.exact=gidNumber={gid}+uidNumber={uid},
+ cn=peercred,cn=external,cn=auth manage by * break
+ olcRootDN: {self.admin_rdn},cn=config
+ olcRootPW: {admin_pw_hash}
+
+ # Load schemas
+ dn: cn=schema,cn=config
+ objectClass: olcSchemaConfig
+ cn: schema
+
+ include: file://{dist_conf_dir}/schema/core.ldif
+ include: file://{dist_conf_dir}/schema/cosine.ldif
+ include: file://{dist_conf_dir}/schema/nis.ldif
+ include: file://{dist_conf_dir}/schema/inetorgperson.ldif
+
+ # Load module
+ dn: cn=module{{0}},cn=config
+ objectClass: olcModuleList
+ cn: module{{0}}
+ olcModulePath: {dist_lib_dir}
+ olcModuleLoad: back_hdb
+
+ # Set defaults for the backend
+ dn: olcBackend=hdb,cn=config
+ objectClass: olcBackendConfig
+ olcBackend: hdb
+
+ # The database definition.
+ dn: olcDatabase=hdb,cn=config
+ objectClass: olcDatabaseConfig
+ objectClass: olcHdbConfig
+ olcDatabase: hdb
+ olcDbCheckpoint: 512 30
+ olcLastMod: TRUE
+ olcSuffix: {self.base_dn}
+ olcDbDirectory: {self.data_dir}
+ olcRootDN: {self.admin_dn}
+ olcRootPW: {admin_pw_hash}
+ olcDbIndex: objectClass eq
+ olcDbIndex: cn,uid eq
+ olcDbIndex: uidNumber,gidNumber eq
+ olcDbIndex: member,memberUid eq
+ olcAccess: to attrs=userPassword,shadowLastChange
+ by self write
+ by anonymous auth
+ by * none
+ olcAccess: to dn.base="" by * read
+ olcAccess: to *
+ by * read
+ """).format(**locals())
+
+ slapadd = subprocess.Popen(
+ ["slapadd", "-F", self.conf_slapd_d_dir, "-b", "cn=config"],
+ stdin = subprocess.PIPE, close_fds = True
+ )
+ slapadd.communicate(config)
+ if slapadd.returncode != 0:
+ raise Exception("Failed to add configuration with slapadd")
+
+ #
+ # Add database config (example from distribution)
+ #
+ db_config = unindent("""
+ # One 0.25 GB cache
+ set_cachesize 0 268435456 1
+
+ # Transaction Log settings
+ set_lg_regionmax 262144
+ set_lg_bsize 2097152
+ """)
+ db_config_file = open(self.data_dir + "/DB_CONFIG", "w")
+ db_config_file.write(db_config)
+ db_config_file.close()
+
+ def setup(self):
+ """Setup the instance."""
+ ldapi_socket = self.run_dir + "/ldapi"
+ ldapi_url = "ldapi://" + urllib.quote(ldapi_socket, "")
+ url_list = ldapi_url + " " + self.ldap_url
+
+ os.makedirs(self.conf_slapd_d_dir)
+ os.makedirs(self.run_dir)
+ os.makedirs(self.data_dir)
+
+ #
+ # Setup initial configuration
+ #
+ self._setup_config()
+
+ #
+ # Start the daemon
+ #
+ if subprocess.call(["slapd", "-F", self.conf_slapd_d_dir,
+ "-h", url_list]) != 0:
+ raise Exception("Failed to start slapd")
+
+ #
+ # Wait until it is available
+ #
+ attempt = 0
+ while True:
+ try:
+ ldap_conn = ldap.initialize(ldapi_url)
+ ldap_conn.simple_bind_s(self.admin_rdn + ",cn=config", self.admin_pw)
+ ldap_conn.unbind_s()
+ ldap_conn = ldap.initialize(self.ldap_url)
+ ldap_conn.simple_bind_s(self.admin_dn, self.admin_pw)
+ ldap_conn.unbind_s()
+ break
+ except ldap.SERVER_DOWN:
+ pass
+ if ++attempt > 30:
+ raise Exception("Failed to start slapd")
+ time.sleep(1)
+
+ #
+ # Relax requirement of member attribute presence in groupOfNames
+ #
+ modlist = [
+ (ldap.MOD_DELETE, "olcObjectClasses",
+ "{7}( 2.5.6.9 NAME 'groupOfNames' "
+ "DESC 'RFC2256: a group of names (DNs)' SUP top "
+ "STRUCTURAL MUST ( member $ cn ) MAY ( businessCategory $ "
+ "seeAlso $ owner $ ou $ o $ description ) )"),
+ (ldap.MOD_ADD, "olcObjectClasses",
+ "{7}( 2.5.6.9 NAME 'groupOfNames' "
+ "DESC 'RFC2256: a group of names (DNs)' SUP top "
+ "STRUCTURAL MUST ( cn ) MAY ( member $ businessCategory $ "
+ "seeAlso $ owner $ ou $ o $ description ) )"),
+ ]
+ ldap_conn = ldap.initialize(ldapi_url)
+ ldap_conn.simple_bind_s(self.admin_rdn + ",cn=config", self.admin_pw)
+ ldap_conn.modify_s("cn={0}core,cn=schema,cn=config", modlist)
+ ldap_conn.unbind_s()
+
+ #
+ # Add data
+ #
+ ldap_conn = ldap.initialize(self.ldap_url)
+ ldap_conn.simple_bind_s(self.admin_dn, self.admin_pw)
+ ldap_conn.add_s(self.base_dn, [
+ ("objectClass", ["dcObject", "organization"]),
+ ("o", "Example Company"),
+ ])
+ ldap_conn.add_s("cn=Manager," + self.base_dn, [
+ ("objectClass", "organizationalRole"),
+ ])
+ for ou in ("Users", "Groups", "Netgroups", "Services", "Policies"):
+ ldap_conn.add_s("ou=" + ou + "," + self.base_dn, [
+ ("objectClass", ["top", "organizationalUnit"]),
+ ])
+ ldap_conn.unbind_s()
+
+ def teardown(self):
+ """Teardown the instance."""
+ # Wait for slapd to stop
+ try:
+ pid_file = open(self.pid_path, "r")
+ try:
+ os.kill(int(pid_file.read()), signal.SIGTERM)
+ finally:
+ pid_file.close()
+ attempt = 0
+ while os.path.isfile(self.pid_path):
+ if ++attempt > 30:
+ raise Exception("Failed to stop slapd")
+ time.sleep(1)
+ except IOError, e:
+ if e.errno != errno.ENOENT:
+ raise
+
+ for path in (self.conf_slapd_d_dir, self.run_dir, self.data_dir):
+ shutil.rmtree(path, True)