From 06548403fee6826cdd1e10b040c9ad8ba1a5c0c0 Mon Sep 17 00:00:00 2001 From: William Brown Date: Wed, 10 Jan 2018 09:59:23 +1000 Subject: [PATCH 1/2] Ticket 49527 - Improve ds* cli tool testing Bug Description: As we get closer to release it's important our tests work correctly and are comprehensive. Fix Description: Improve the directory manager test, backend test user test and correct some incompatability with memberof test. https://pagure.io/389-ds-base/issue/49527 Author: wibrown Review by: ??? --- src/lib389/cli/dsconf | 4 + src/lib389/cli/dsctl | 2 + src/lib389/cli/dsidm | 2 + src/lib389/lib389/__init__.py | 3 +- src/lib389/lib389/_mapped_object.py | 38 ++++++++- src/lib389/lib389/backend.py | 5 +- src/lib389/lib389/cli_base/__init__.py | 17 +++- src/lib389/lib389/cli_conf/backend.py | 7 +- src/lib389/lib389/cli_conf/directory_manager.py | 35 +++++++++ src/lib389/lib389/cli_idm/user.py | 41 +++++++++- src/lib389/lib389/idm/directorymanager.py | 3 + src/lib389/lib389/idm/nscontainer.py | 90 ++++++++++++++++++++++ src/lib389/lib389/plugins.py | 16 ++-- src/lib389/lib389/tests/cli/adm_instance_test.py | 1 - src/lib389/lib389/tests/cli/conf_backend_test.py | 6 +- .../tests/cli/conf_directory_manager_test.py | 22 ++++++ .../lib389/tests/cli/conf_plugins/memberof_test.py | 56 +++++++------- src/lib389/lib389/tests/cli/ctl_dbtasks_test.py | 3 + src/lib389/lib389/tests/cli/idm_user_test.py | 90 ++++++++++++++++++++++ .../tests/configurations/config_001003006_test.py | 57 +------------- 20 files changed, 391 insertions(+), 107 deletions(-) create mode 100644 src/lib389/lib389/cli_conf/directory_manager.py create mode 100644 src/lib389/lib389/idm/nscontainer.py create mode 100644 src/lib389/lib389/tests/cli/conf_directory_manager_test.py create mode 100644 src/lib389/lib389/tests/cli/idm_user_test.py diff --git a/src/lib389/cli/dsconf b/src/lib389/cli/dsconf index ea1d056..b13183c 100755 --- a/src/lib389/cli/dsconf +++ b/src/lib389/cli/dsconf @@ -19,6 +19,7 @@ logging.basicConfig(format='%(message)s') from lib389 import DirSrv from lib389._constants import DN_CONFIG, DN_DM from lib389.cli_conf import backend as cli_backend +from lib389.cli_conf import directory_manager as cli_directory_manager from lib389.cli_conf import plugin as cli_plugin from lib389.cli_conf import schema as cli_schema from lib389.cli_conf import health as cli_health @@ -65,6 +66,7 @@ if __name__ == '__main__': subparsers = parser.add_subparsers(help="resources to act upon") cli_backend.create_parser(subparsers) + cli_directory_manager.create_parsers(subparsers) cli_schema.create_parser(subparsers) cli_health.create_parser(subparsers) cli_plugin.create_parser(subparsers) @@ -109,10 +111,12 @@ if __name__ == '__main__': if args.verbose: inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose) args.func(inst, None, log, args) + log.info("Command successful.") else: try: inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose) args.func(inst, None, log, args) + log.info("Command successful.") except Exception as e: log.debug(e, exc_info=True) log.error("Error: %s" % e) diff --git a/src/lib389/cli/dsctl b/src/lib389/cli/dsctl index 36ecf99..02a1cff 100755 --- a/src/lib389/cli/dsctl +++ b/src/lib389/cli/dsctl @@ -80,9 +80,11 @@ if __name__ == '__main__': if args.verbose: result = args.func(inst, log, args) + log.info("Command successful.") else: try: result = args.func(inst, log, args) + log.info("Command successful.") except Exception as e: log.debug(e, exc_info=True) log.error("Error: %s" % str(e)) diff --git a/src/lib389/cli/dsidm b/src/lib389/cli/dsidm index 1cfc415..d6a43ab 100755 --- a/src/lib389/cli/dsidm +++ b/src/lib389/cli/dsidm @@ -101,10 +101,12 @@ if __name__ == '__main__': if args.verbose: inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose) args.func(inst, dsrc_inst['basedn'], log, args) + log.info("Command successful.") else: try: inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose) args.func(inst, dsrc_inst['basedn'], log, args) + log.info("Command successful.") except Exception as e: log.debug(e, exc_info=True) log.error("Error: %s" % str(e)) diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py index fab1ed1..b636ebb 100644 --- a/src/lib389/lib389/__init__.py +++ b/src/lib389/lib389/__init__.py @@ -3323,6 +3323,7 @@ class DirSrv(SimpleLDAPObject, object): # This could be made to delete by filter .... def delete_branch_s(self, basedn, scope): ents = self.search_s(basedn, scope) - for ent in ents: + + for ent in sorted(ents, key=lambda e: len(e.dn), reverse=True): self.log.debug("Delete entry children %s" % (ent.dn)) self.delete_s(ent.dn) diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py index 6161500..64ccd56 100644 --- a/src/lib389/lib389/_mapped_object.py +++ b/src/lib389/lib389/_mapped_object.py @@ -225,7 +225,12 @@ class DSLdapObject(DSLogging): values = self.get_attr_vals_bytes(attr) self._log.debug("%s contains %s" % (self._dn, values)) - return ensure_bytes(value).lower() in [x.lower() for x in values] + if value is None: + # We are just checking if SOMETHING is present .... + return len(values) > 0 + else: + # Check if a value really does exist. + return ensure_bytes(value).lower() in [x.lower() for x in values] def add(self, key, value): """Add an attribute with a value @@ -287,7 +292,10 @@ class DSLdapObject(DSLogging): :type key: str """ - self.set(key, None, action=ldap.MOD_DELETE) + try: + self.set(key, None, action=ldap.MOD_DELETE) + except ldap.NO_SUCH_ATTRIBUTE: + pass # maybe this could be renamed? def set(self, key, value, action=ldap.MOD_REPLACE): @@ -491,6 +499,21 @@ class DSLdapObject(DSLogging): return ensure_str(self.get_attr_val(key)) + def get_attr_val_utf8_l(self, key): + """Get a single attribute value from the entry in utf8 type + + :param key: An attribute name + :type key: str + :returns: A single bytes value + :raises: ValueError - if instance is offline + """ + + x = self.get_attr_val(key) + if x is not None: + return ensure_str(x).lower() + else: + return None + def get_attr_vals_utf8(self, key): """Get attribute values from the entry in utf8 type @@ -502,6 +525,17 @@ class DSLdapObject(DSLogging): return ensure_list_str(self.get_attr_vals(key)) + def get_attr_vals_utf8_l(self, key): + """Get attribute values from the entry in utf8 type and lowercase + + :param key: An attribute name + :type key: str + :returns: A single bytes value + :raises: ValueError - if instance is offline + """ + + return [x.lower() for x in ensure_list_str(self.get_attr_vals(key))] + def get_attr_val_int(self, key): """Get a single attribute value from the entry in int type diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py index 25a244c..3fcaf07 100644 --- a/src/lib389/lib389/backend.py +++ b/src/lib389/lib389/backend.py @@ -516,7 +516,10 @@ class Backend(DSLdapObject): self._instance.index.delete_all(bename) # Now remove our children, this is all ldbm config - self._instance.delete_branch_s(self._dn, ldap.SCOPE_ONELEVEL) + + configs = self._instance.search_s(self._dn, ldap.SCOPE_ONELEVEL) + for c in configs: + self._instance.delete_branch_s(c.dn, ldap.SCOPE_SUBTREE) # The super will actually delete ourselves. super(Backend, self).delete() diff --git a/src/lib389/lib389/cli_base/__init__.py b/src/lib389/lib389/cli_base/__init__.py index 21bf3cc..4f5e7ad 100644 --- a/src/lib389/lib389/cli_base/__init__.py +++ b/src/lib389/lib389/cli_base/__init__.py @@ -23,14 +23,17 @@ def _input(msg): return raw_input(msg) -def _get_arg(args, msg=None): +def _get_arg(args, msg=None, hidden=False): if args is not None and len(args) > 0: if type(args) is list: return args[0] else: return args else: - return _input("%s : " % msg) + if hidden: + return getpass("%s : " % msg) + else: + return _input("%s : " % msg) def _get_args(args, kws): kwargs = {} @@ -50,8 +53,11 @@ def _get_args(args, kws): def _get_attributes(args, attrs): kwargs = {} for attr in attrs: - if args is not None and hasattr(args, attr) and getattr(args, attr) is not None: - kwargs[attr] = getattr(args, attr) + # Python can't represent a -, so it replaces it to _ + # in many places, so we have to normalise this. + attr_normal = attr.replace('-', '_') + if args is not None and hasattr(args, attr_normal) and getattr(args, attr_normal) is not None: + kwargs[attr] = getattr(args, attr_normal) else: if attr.lower() == 'userpassword': kwargs[attr] = getpass("Enter value for %s : " % attr) @@ -168,3 +174,6 @@ class FakeArgs(object): def __init__(self): pass + def __len__(self): + return len(self.__dict__.keys()) + diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py index e99cc82..4437725 100644 --- a/src/lib389/lib389/cli_conf/backend.py +++ b/src/lib389/lib389/cli_conf/backend.py @@ -10,6 +10,7 @@ from lib389.backend import Backend, Backends import argparse from lib389.cli_base import ( + populate_attr_arguments, _generic_list, _generic_get, _generic_get_dn, @@ -37,7 +38,7 @@ def backend_get_dn(inst, basedn, log, args): _generic_get_dn(inst, basedn, log.getChild('backend_get_dn'), MANY, dn) def backend_create(inst, basedn, log, args): - kwargs = _get_attributes(args.extra, SINGULAR._must_attributes) + kwargs = _get_attributes(args, Backend._must_attributes) _generic_create(inst, basedn, log.getChild('backend_create'), MANY, kwargs) def backend_delete(inst, basedn, log, args, warn=True): @@ -65,9 +66,7 @@ def create_parser(subparsers): create_parser = subcommands.add_parser('create', help='create') create_parser.set_defaults(func=backend_create) - create_parser.add_argument('extra', nargs=argparse.REMAINDER, - help='A create may take one or more extra arguments. This parameter provides them' - ) + populate_attr_arguments(create_parser, Backend._must_attributes) delete_parser = subcommands.add_parser('delete', help='deletes the object') delete_parser.set_defaults(func=backend_delete) diff --git a/src/lib389/lib389/cli_conf/directory_manager.py b/src/lib389/lib389/cli_conf/directory_manager.py new file mode 100644 index 0000000..56c3e73 --- /dev/null +++ b/src/lib389/lib389/cli_conf/directory_manager.py @@ -0,0 +1,35 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2017 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- + +from lib389.idm.directorymanager import DirectoryManager + +from lib389.cli_base import _get_arg + +import argparse + +def password_change(inst, basedn, log, args): + # Due to an issue, we can't use extended op, so we have to + # submit the password directly to the field. + + password = _get_arg(args.password, msg="Enter new directory manager password", hidden=True) + dm = DirectoryManager(inst) + dm.change_password(password) + +def create_parsers(subparsers): + directory_manager_parser = subparsers.add_parser('directory_manager', help="Manage the directory manager account") + + subcommands = directory_manager_parser.add_subparsers(help='action') + + password_change_parser = subcommands.add_parser('password_change', help="Change the directory manager password") + password_change_parser.set_defaults(func=password_change) + # This is to put in a dummy attr that args can work with. We do this + # because the actual test case will over-ride it, but it prevents + # a user putting the pw on the cli. + password_change_parser.set_defaults(password=None) + + diff --git a/src/lib389/lib389/cli_idm/user.py b/src/lib389/lib389/cli_idm/user.py index 5b57862..70e7148 100644 --- a/src/lib389/lib389/cli_idm/user.py +++ b/src/lib389/lib389/cli_idm/user.py @@ -43,11 +43,34 @@ def create(inst, basedn, log, args): kwargs = _get_attributes(args, MUST_ATTRIBUTES) _generic_create(inst, basedn, log.getChild('_generic_create'), MANY, kwargs) -def delete(inst, basedn, log, args): - dn = _get_arg( args, msg="Enter dn to delete") - _warn(dn, msg="Deleting %s %s" % (SINGULAR.__name__, dn)) +def delete(inst, basedn, log, args, warn=True): + dn = _get_arg( args.dn, msg="Enter dn to delete") + if warn: + _warn(dn, msg="Deleting %s %s" % (SINGULAR.__name__, dn)) _generic_delete(inst, basedn, log.getChild('_generic_delete'), SINGULAR, dn) +def status(inst, basedn, log, args): + uid = _get_arg( args.uid, msg="Enter %s to check" % RDN) + uas = UserAccounts(inst, basedn) + acct = uas.get(uid) + acct_str = "locked: %s" % acct.is_locked() + log.info('uid: %s' % uid) + log.info(acct_str) + +def lock(inst, basedn, log, args): + uid = _get_arg( args.uid, msg="Enter %s to check" % RDN) + accounts = UserAccounts(inst, basedn) + acct = accounts.get(uid) + acct.lock() + log.info('locked %s' % uid) + +def unlock(inst, basedn, log, args): + uid = _get_arg( args.uid, msg="Enter %s to check" % RDN) + accounts = UserAccounts(inst, basedn) + acct = accounts.get(uid) + acct.unlock() + log.info('unlocked %s' % uid) + def create_parser(subparsers): user_parser = subparsers.add_parser('user', help='Manage posix users') @@ -72,5 +95,17 @@ def create_parser(subparsers): delete_parser.set_defaults(func=delete) delete_parser.add_argument('dn', nargs='?', help='The dn to delete') + lock_parser = subcommands.add_parser('lock', help='lock') + lock_parser.set_defaults(func=lock) + lock_parser.add_argument('uid', nargs='?', help='The uid to lock') + + status_parser = subcommands.add_parser('status', help='status') + status_parser.set_defaults(func=status) + status_parser.add_argument('uid', nargs='?', help='The uid to check') + + unlock_parser = subcommands.add_parser('unlock', help='unlock') + unlock_parser.set_defaults(func=unlock) + unlock_parser.add_argument('uid', nargs='?', help='The uid to unlock') + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/src/lib389/lib389/idm/directorymanager.py b/src/lib389/lib389/idm/directorymanager.py index 4028fc6..150a549 100644 --- a/src/lib389/lib389/idm/directorymanager.py +++ b/src/lib389/lib389/idm/directorymanager.py @@ -30,6 +30,9 @@ class DirectoryManager(Account): self._create_objectclasses = None self._protected = True + def change_password(self, new_password): + self._instance.config.set('nsslapd-rootpw', new_password) + def bind(self, password=PW_DM, *args, **kwargs): """Bind as the Directory Manager. We have a default test password that can be overriden. diff --git a/src/lib389/lib389/idm/nscontainer.py b/src/lib389/lib389/idm/nscontainer.py new file mode 100644 index 0000000..5ea2aff --- /dev/null +++ b/src/lib389/lib389/idm/nscontainer.py @@ -0,0 +1,90 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2017, Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- + +from lib389._mapped_object import DSLdapObject, DSLdapObjects + +class nsContainer(DSLdapObject): + """A single instance of a nsContainer. This is similar to OU + for organisation of a directory tree. + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn=None): + super(nsContainer, self).__init__(instance, dn) + self._rdn_attribute = 'cn' + self._must_attributes = ['cn'] + self._create_objectclasses = [ + 'top', + 'nscontainer', + ] + self._protected = False + +class nsContainers(DSLdapObjects): + """The set of nsContainers on the server. + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all group entries below + :type basedn: str + """ + + def __init__(self, instance, basedn): + super(nsContainers, self).__init__(instance) + self._objectclasses = [ + 'nscontainer', + ] + self._filterattrs = ['cn'] + self._childobject = nsContainer + self._basedn = basedn + +class nsHiddenContainer(DSLdapObject): + """A single instance of a hidden container. This is a combination + of nsContainer and ldapsubentry. + + :param instance: An instance + :type instance: lib389.DirSrv + :param dn: Entry DN + :type dn: str + """ + + def __init__(self, instance, dn=None): + super(nsHiddenContainer, self).__init__(instance, dn) + self._rdn_attribute = 'cn' + self._must_attributes = ['cn'] + self._create_objectclasses = [ + 'top', + 'nscontainer', + 'ldapsubentry', + ] + self._protected = False + +class nsHiddenContainers(DSLdapObjects): + """The set of nsHiddenContainers on the server. + + :param instance: An instance + :type instance: lib389.DirSrv + :param basedn: Base DN for all group entries below + :type basedn: str + """ + + def __init__(self, instance, basedn): + super(nsHiddenContainers, self).__init__(instance) + self._objectclasses = [ + 'nscontainer', + 'ldapsubentry', + ] + self._filterattrs = ['cn'] + self._childobject = nsHiddenContainer + self._basedn = basedn + + + diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py index e0080d6..8180e78 100644 --- a/src/lib389/lib389/plugins.py +++ b/src/lib389/lib389/plugins.py @@ -271,7 +271,7 @@ class MemberOfPlugin(Plugin): self._must_attributes.extend(['memberOfGroupAttr', 'memberOfAttr']) def get_attr(self): - return self.get_attr_val('memberofattr') + return self.get_attr_val_utf8_l('memberofattr') def get_attr_formatted(self): return self.display_attr('memberofattr') @@ -280,7 +280,7 @@ class MemberOfPlugin(Plugin): self.set('memberofattr', attr) def get_groupattr(self): - return self.get_attr_vals('memberofgroupattr') + return self.get_attr_vals_utf8_l('memberofgroupattr') def get_groupattr_formatted(self): return self.display_attr('memberofgroupattr') @@ -292,7 +292,7 @@ class MemberOfPlugin(Plugin): self.remove('memberofgroupattr', attr) def get_allbackends(self): - return self.get_attr_val('memberofallbackends') + return self.get_attr_val_utf8_l('memberofallbackends') def get_allbackends_formatted(self): return self.display_attr('memberofallbackends') @@ -304,7 +304,7 @@ class MemberOfPlugin(Plugin): self.set('memberofallbackends', 'off') def get_skipnested(self): - return self.get_attr_val('memberofskipnested') + return self.get_attr_val_utf8_l('memberofskipnested') def get_skipnested_formatted(self): return self.display_attr('memberofskipnested') @@ -316,7 +316,7 @@ class MemberOfPlugin(Plugin): self.set('memberofskipnested', 'off') def get_autoaddoc(self): - return self.get_attr_val('memberofautoaddoc') + return self.get_attr_val_utf8_l('memberofautoaddoc') def get_autoaddoc_formatted(self): return self.display_attr('memberofautoaddoc') @@ -328,7 +328,7 @@ class MemberOfPlugin(Plugin): self.remove_all('memberofautoaddoc') def get_entryscope(self, formatted=False): - return self.get_attr_vals('memberofentryscope') + return self.get_attr_vals_utf8_l('memberofentryscope') def get_entryscope_formatted(self): return self.display_attr('memberofentryscope') @@ -343,7 +343,7 @@ class MemberOfPlugin(Plugin): self.remove_all('memberofentryscope') def get_excludescope(self): - return self.get_attr_vals('memberofentryscopeexcludesubtree') + return self.get_attr_vals_utf8_l('memberofentryscopeexcludesubtree') def get_excludescope_formatted(self): return self.display_attr('memberofentryscopeexcludesubtree') @@ -768,7 +768,7 @@ class Plugins(DSLdapObjects): # This is a map of plugin to type, so when we # do a get / list / create etc, we can map to the correct # instance. - def __init__(self, instance): + def __init__(self, instance, basedn=None): super(Plugins, self).__init__(instance=instance) self._objectclasses = ['top', 'nsslapdplugin'] self._filterattrs = ['cn', 'nsslapd-pluginPath'] diff --git a/src/lib389/lib389/tests/cli/adm_instance_test.py b/src/lib389/lib389/tests/cli/adm_instance_test.py index 1c3d829..d8d2f41 100644 --- a/src/lib389/lib389/tests/cli/adm_instance_test.py +++ b/src/lib389/lib389/tests/cli/adm_instance_test.py @@ -14,7 +14,6 @@ from lib389 import DirSrv from lib389.cli_base import LogCapture - def test_instance_list(): lc = LogCapture() inst = DirSrv() diff --git a/src/lib389/lib389/tests/cli/conf_backend_test.py b/src/lib389/lib389/tests/cli/conf_backend_test.py index da8ec73..3ed55aa 100644 --- a/src/lib389/lib389/tests/cli/conf_backend_test.py +++ b/src/lib389/lib389/tests/cli/conf_backend_test.py @@ -13,6 +13,9 @@ from lib389.cli_conf.backend import backend_list, backend_get, backend_get_dn, b from lib389.cli_base import LogCapture, FakeArgs from lib389.tests.cli import topology +from lib389.utils import ds_is_older +pytestmark = pytest.mark.skipif(ds_is_older('1.4.0'), reason="Not implemented") + # Topology is pulled from __init__.py def test_backend_cli(topology): # @@ -23,7 +26,8 @@ def test_backend_cli(topology): topology.logcap.flush() # Add a backend # We need to fake the args - args.extra = ['dc=example,dc=com', 'userRoot'] + args.cn = 'userRoot' + args.nsslapd_suffix = 'dc=example,dc=com' backend_create(topology.standalone, None, topology.logcap.log, args) # Assert one. backend_list(topology.standalone, None, topology.logcap.log, None) diff --git a/src/lib389/lib389/tests/cli/conf_directory_manager_test.py b/src/lib389/lib389/tests/cli/conf_directory_manager_test.py new file mode 100644 index 0000000..96b0b17 --- /dev/null +++ b/src/lib389/lib389/tests/cli/conf_directory_manager_test.py @@ -0,0 +1,22 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2017 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- + +import pytest + +from lib389.cli_conf.directory_manager import password_change + +from lib389.cli_base import LogCapture, FakeArgs +from lib389.tests.cli import topology + +# Topology is pulled from __init__.py +def test_directory_manager(topology): + # + args = FakeArgs() + args.password = 'password' + password_change(topology.standalone, None, topology.logcap.log, args) + diff --git a/src/lib389/lib389/tests/cli/conf_plugins/memberof_test.py b/src/lib389/lib389/tests/cli/conf_plugins/memberof_test.py index 339138f..903956d 100644 --- a/src/lib389/lib389/tests/cli/conf_plugins/memberof_test.py +++ b/src/lib389/lib389/tests/cli/conf_plugins/memberof_test.py @@ -56,24 +56,24 @@ def test_set_attr_with_illegal_value(topology): def test_set_groupattr_with_legal_value(topology): args = FakeArgs() - args.value = "uniqueMember" + args.value = "uniquemember" memberof_cli.add_groupattr(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains("successfully added memberOfGroupAttr value") topology.logcap.flush() memberof_cli.display_groupattr(topology.standalone, None, topology.logcap.log, args) - assert topology.logcap.contains(": uniqueMember") + assert topology.logcap.contains(": uniquemember") topology.logcap.flush() def test_set_groupattr_with_value_that_already_exists(topology): plugin = MemberOfPlugin(topology.standalone) # setup test - if not "uniqueMember" in plugin.get_groupattr(): - plugin.add_groupattr("uniqueMember") + if not "uniquemember" in plugin.get_groupattr(): + plugin.add_groupattr("uniquemember") args = FakeArgs() - args.value = "uniqueMember" + args.value = "uniquemember" memberof_cli.add_groupattr(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains("already exists") topology.logcap.flush() @@ -89,18 +89,18 @@ def test_set_groupattr_with_illegal_value(topology): def test_remove_groupattr_with_value_that_exists(topology): plugin = MemberOfPlugin(topology.standalone) # setup test - if not "uniqueMember" in plugin.get_groupattr(): - plugin.add_groupattr("uniqueMember") + if not "uniquemember" in plugin.get_groupattr(): + plugin.add_groupattr("uniquemember") args = FakeArgs() - args.value = "uniqueMember" + args.value = "uniquemember" memberof_cli.remove_groupattr(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains("successfully removed memberOfGroupAttr value") topology.logcap.flush() memberof_cli.display_groupattr(topology.standalone, None, topology.logcap.log, args) - assert not topology.logcap.contains(": uniqueMember") + assert not topology.logcap.contains(": uniquemember") topology.logcap.flush() def test_remove_groupattr_with_value_that_doesnt_exist(topology): @@ -361,14 +361,14 @@ def test_get_excludescope_when_not_set(topology): def test_add_excludescope_with_legal_value(topology): args = FakeArgs() - args.value = "ou=People,dc=example,dc=com" + args.value = "ou=people,dc=example,dc=com" memberof_cli.add_excludescope(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains("successfully added memberOfEntryScopeExcludeSubtree value") topology.logcap.flush() args.value = None memberof_cli.display_excludescope(topology.standalone, None, topology.logcap.log, args) - assert topology.logcap.contains(": ou=People,dc=example,dc=com") + assert topology.logcap.contains(": ou=people,dc=example,dc=com") topology.logcap.flush() args.value = "a=b" @@ -378,7 +378,7 @@ def test_add_excludescope_with_legal_value(topology): args.value = None memberof_cli.display_excludescope(topology.standalone, None, topology.logcap.log, args) - assert topology.logcap.contains(": ou=People,dc=example,dc=com") + assert topology.logcap.contains(": ou=people,dc=example,dc=com") assert topology.logcap.contains(": a=b") topology.logcap.flush() @@ -393,12 +393,12 @@ def test_add_excludescope_with_illegal_value(topology): def test_add_excludescope_with_existing_value(topology): plugin = MemberOfPlugin(topology.standalone) # setup test - if not "ou=People,dc=example,dc=com" in plugin.get_excludescope(): - plugin.add_excludescope("ou=People,dc=example,dc=com") + if not "ou=people,dc=example,dc=com" in plugin.get_excludescope(): + plugin.add_excludescope("ou=people,dc=example,dc=com") args = FakeArgs() - args.value = "ou=People,dc=example,dc=com" + args.value = "ou=people,dc=example,dc=com" memberof_cli.add_excludescope(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains('Value "{}" already exists'.format(args.value)) topology.logcap.flush() @@ -408,8 +408,8 @@ def test_remove_excludescope_with_existing_value(topology): # setup test if not "a=b" in plugin.get_excludescope(): plugin.add_excludescope("a=b") - if not "ou=People,dc=example,dc=com" in plugin.get_excludescope(): - plugin.add_excludescope("ou=People,dc=example,dc=com") + if not "ou=people,dc=example,dc=com" in plugin.get_excludescope(): + plugin.add_excludescope("ou=people,dc=example,dc=com") args = FakeArgs() @@ -420,7 +420,7 @@ def test_remove_excludescope_with_existing_value(topology): args.value = None memberof_cli.display_excludescope(topology.standalone, None, topology.logcap.log, args) - assert topology.logcap.contains(": ou=People,dc=example,dc=com") + assert topology.logcap.contains(": ou=people,dc=example,dc=com") assert not topology.logcap.contains(": a=b") topology.logcap.flush() @@ -437,15 +437,15 @@ def test_remove_all_excludescope(topology): # setup test if not "a=b" in plugin.get_excludescope(): plugin.add_excludescope("a=b") - if not "ou=People,dc=example,dc=com" in plugin.get_excludescope(): - plugin.add_excludescope("ou=People,dc=example,dc=com") + if not "ou=people,dc=example,dc=com" in plugin.get_excludescope(): + plugin.add_excludescope("ou=people,dc=example,dc=com") args = FakeArgs() args.value = None memberof_cli.display_excludescope(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains(": a=b") - assert topology.logcap.contains(": ou=People,dc=example,dc=com") + assert topology.logcap.contains(": ou=people,dc=example,dc=com") topology.logcap.flush() args.value = None @@ -456,7 +456,7 @@ def test_remove_all_excludescope(topology): args.value = None memberof_cli.display_excludescope(topology.standalone, None, topology.logcap.log, args) assert not topology.logcap.contains(": a=b") - assert not topology.logcap.contains(": ou=People,dc=example,dc=com") + assert not topology.logcap.contains(": ou=people,dc=example,dc=com") topology.logcap.flush() def test_add_entryscope_with_value_that_exists_in_excludescope(topology): @@ -464,12 +464,12 @@ def test_add_entryscope_with_value_that_exists_in_excludescope(topology): # setup test if not "dc=example,dc=com" in plugin.get_entryscope(): plugin.add_entryscope("dc=example,dc=com") - if not "ou=People,dc=example,dc=com" in plugin.get_excludescope(): - plugin.add_excludescope("ou=People,dc=example,dc=com") + if not "ou=people,dc=example,dc=com" in plugin.get_excludescope(): + plugin.add_excludescope("ou=people,dc=example,dc=com") args = FakeArgs() - args.value = "ou=People,dc=example,dc=com" + args.value = "ou=people,dc=example,dc=com" memberof_cli.add_scope(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains("is also listed as an exclude suffix") topology.logcap.flush() @@ -479,12 +479,12 @@ def test_add_excludescope_with_value_that_exists_in_entryscope(topology): # setup test if not "dc=example,dc=com" in plugin.get_entryscope(): plugin.add_entryscope("dc=example,dc=com") - if not "ou=People,dc=example,dc=com" in plugin.get_excludescope(): - plugin.add_excludescope("ou=People,dc=example,dc=com") + if not "ou=people,dc=example,dc=com" in plugin.get_excludescope(): + plugin.add_excludescope("ou=people,dc=example,dc=com") args = FakeArgs() - args.value = "ou=People,dc=example,dc=com" + args.value = "ou=people,dc=example,dc=com" memberof_cli.add_scope(topology.standalone, None, topology.logcap.log, args) assert topology.logcap.contains("is also listed as an exclude suffix") topology.logcap.flush() diff --git a/src/lib389/lib389/tests/cli/ctl_dbtasks_test.py b/src/lib389/lib389/tests/cli/ctl_dbtasks_test.py index f2c31e5..d77b72e 100644 --- a/src/lib389/lib389/tests/cli/ctl_dbtasks_test.py +++ b/src/lib389/lib389/tests/cli/ctl_dbtasks_test.py @@ -15,6 +15,9 @@ from lib389.cli_ctl.dbtasks import dbtasks_db2index, dbtasks_db2bak, dbtasks_db2 from lib389.cli_base import LogCapture, FakeArgs from lib389.tests.cli import topology, topology_be_latest +from lib389.utils import ds_is_older +pytestmark = pytest.mark.skipif(ds_is_older('1.4.0'), reason="Not implemented") + def test_db2index(topology): pass diff --git a/src/lib389/lib389/tests/cli/idm_user_test.py b/src/lib389/lib389/tests/cli/idm_user_test.py new file mode 100644 index 0000000..da579b4 --- /dev/null +++ b/src/lib389/lib389/tests/cli/idm_user_test.py @@ -0,0 +1,90 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2017 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- + +import pytest +import ldap + +from lib389._constants import DEFAULT_SUFFIX, INSTALL_LATEST_CONFIG + +from lib389.cli_conf.backend import backend_create +from lib389.cli_idm.initialise import initialise +from lib389.cli_idm.user import get, create, delete, status, lock, unlock + +from lib389.cli_base import LogCapture, FakeArgs +from lib389.tests.cli import topology + +from lib389.utils import ds_is_older +pytestmark = pytest.mark.skipif(ds_is_older('1.4.0'), reason="Not implemented") + +# Topology is pulled from __init__.py +def test_user_tasks(topology): + # + be_args = FakeArgs() + + be_args.cn = 'userRoot' + be_args.nsslapd_suffix = DEFAULT_SUFFIX + backend_create(topology.standalone, None, topology.logcap.log, be_args) + + # And add the skeleton objects. + init_args = FakeArgs() + init_args.version = INSTALL_LATEST_CONFIG + initialise(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, init_args) + + # First check that our test user isn't there: + topology.logcap.flush() + u_args = FakeArgs() + u_args.selector = 'testuser' + with pytest.raises(ldap.NO_SUCH_OBJECT): + get(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) + + # Create the user + topology.logcap.flush() + u_args.cn = 'testuser' + u_args.uid = 'testuser' + u_args.sn = 'testuser' + u_args.homeDirectory = '/home/testuser' + u_args.uidNumber = '5000' + u_args.gidNumber = '5000' + create(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) + + assert(topology.logcap.contains("Sucessfully created testuser")) + # Assert they exist + topology.logcap.flush() + get(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) + assert(topology.logcap.contains('dn: uid=testuser,ou=people,dc=example,dc=com')) + + # Reset the password + + # Lock the account, check status + topology.logcap.flush() + lock(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) + assert(topology.logcap.contains('locked')) + + topology.logcap.flush() + status(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) + assert(topology.logcap.contains('locked: True')) + + # Unlock check status + topology.logcap.flush() + unlock(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) + assert(topology.logcap.contains('unlocked')) + + topology.logcap.flush() + status(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args) + assert(topology.logcap.contains('locked: False')) + + # Enroll a dummy cert + + # Enroll a dummy sshkey + + # Delete it + topology.logcap.flush() + u_args.dn = 'uid=testuser,ou=people,dc=example,dc=com' + delete(topology.standalone, DEFAULT_SUFFIX, topology.logcap.log, u_args, warn=False) + assert(topology.logcap.contains('Sucessfully deleted uid=testuser,ou=people,dc=example,dc=com')) + diff --git a/src/lib389/lib389/tests/configurations/config_001003006_test.py b/src/lib389/lib389/tests/configurations/config_001003006_test.py index 300122a..dfc8e81 100644 --- a/src/lib389/lib389/tests/configurations/config_001003006_test.py +++ b/src/lib389/lib389/tests/configurations/config_001003006_test.py @@ -18,15 +18,10 @@ from lib389 import DirSrv from lib389._constants import * from lib389.properties import * -DEBUGGING = os.getenv('DEBUGGING', default=False) -if DEBUGGING: - logging.getLogger(__name__).setLevel(logging.DEBUG) -else: - logging.getLogger(__name__).setLevel(logging.INFO) -log = logging.getLogger(__name__) +from lib389.topologies import topology_st -### WARNING: -# We can't use topology here, as we need to force python install! +from lib389.utils import ds_is_older +pytestmark = pytest.mark.skipif(ds_is_older('1.4.0'), reason="Not implemented") REQUIRED_DNS = [ 'dc=example,dc=com', @@ -40,52 +35,6 @@ REQUIRED_DNS = [ 'cn=Directory Administrators,dc=example,dc=com', ] -class TopologyMain(object): - def __init__(self, standalones=None, masters=None, - consumers=None, hubs=None): - if standalones: - if isinstance(standalones, dict): - self.ins = standalones - else: - self.standalone = standalones - if masters: - self.ms = masters - if consumers: - self.cs = consumers - if hubs: - self.hs = hubs - -@pytest.fixture(scope="module") -def topology_st(request): - """Create DS standalone instance""" - - if DEBUGGING: - standalone = DirSrv(verbose=True) - else: - standalone = DirSrv(verbose=False) - args_instance[SER_HOST] = HOST_STANDALONE1 - args_instance[SER_PORT] = PORT_STANDALONE1 - # args_instance[SER_SECURE_PORT] = SECUREPORT_STANDALONE1 - args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE1 - args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX - args_standalone = args_instance.copy() - standalone.allocate(args_standalone) - instance_standalone = standalone.exists() - if instance_standalone: - standalone.delete() - standalone.create(pyinstall=True, version='001003006') - standalone.open() - - def fin(): - if DEBUGGING: - standalone.stop() - else: - standalone.delete() - - request.addfinalizer(fin) - - return TopologyMain(standalones=standalone) - def test_install_sample_entries(topology_st): # Assert that our entries match. -- 1.8.3.1