From 223846dff976de076fdddc52f7adc55b432b0582 Mon Sep 17 00:00:00 2001 From: William Brown Date: Apr 01 2019 23:27:17 +0000 Subject: Ticket 49390 - improve compare and cn=config compare tests Bug Description: We had a number of tests for the dsldapobject compare cases, but they were in the lib389 tests. Move and update these to work as part of the dirsrvtests suite. Fix Description: Update lib389 to properly handle attribute casing and update compare tests to work with newer lib389 ideas https://pagure.io/389-ds-base/issue/49390 Author: William Brown Review by: spichugi (Thanks) --- diff --git a/dirsrvtests/tests/suites/lib389/config_compare_test.py b/dirsrvtests/tests/suites/lib389/config_compare_test.py new file mode 100644 index 0000000..6379555 --- /dev/null +++ b/dirsrvtests/tests/suites/lib389/config_compare_test.py @@ -0,0 +1,38 @@ +import os +import pytest + +from lib389.topologies import topology_i2 +from lib389.config import Config + +def test_config_compare(topology_i2): + """ + Compare test between cn=config of two different Directory Server intance. + + :id: 7b3e17d6-41ca-4926-bc3b-8173dd912a61 + + :setup: two isolated directory servers + + :steps: 1. Compare if cn=config is the same + + :expectedresults: 1. It should be the same (excluding unique id attrs) + """ + + st1_config = topology_i2.ins.get('standalone1').config + st2_config = topology_i2.ins.get('standalone2').config + # 'nsslapd-port' attribute is expected to be same in cn=config comparison, + # but they are different in our testing environment + # as we are using 2 DS instances running, both running simultaneuosly. + # Hence explicitly adding 'nsslapd-port' to compare_exclude. + st1_config._compare_exclude.append('nsslapd-port') + st2_config._compare_exclude.append('nsslapd-port') + st1_config._compare_exclude.append('nsslapd-secureport') + st2_config._compare_exclude.append('nsslapd-secureport') + + assert Config.compare(st1_config, st2_config) + + +if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s %s" % CURRENT_FILE) diff --git a/dirsrvtests/tests/suites/lib389/idm/__init__.py b/dirsrvtests/tests/suites/lib389/idm/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirsrvtests/tests/suites/lib389/idm/__init__.py diff --git a/dirsrvtests/tests/suites/lib389/idm/user_compare_i2_test.py b/dirsrvtests/tests/suites/lib389/idm/user_compare_i2_test.py new file mode 100644 index 0000000..994ced0 --- /dev/null +++ b/dirsrvtests/tests/suites/lib389/idm/user_compare_i2_test.py @@ -0,0 +1,47 @@ +import os +import pytest +from lib389._constants import DEFAULT_SUFFIX +from lib389.idm.user import UserAccounts, UserAccount +from lib389.topologies import topology_i2 + +def test_user_compare_i2(topology_i2): + """ + Compare test between users of two different Directory Server intances. + + :id: f0ffaf59-e2c2-41ec-9f26-e9b1ef287463 + + :setup: two isolated directory servers + + :steps: 1. Add an identical user to each server + 2. Compare if the users are "the same" + + :expectedresults: 1. Users are added + 2. The users are reported as the same + """ + st1_users = UserAccounts(topology_i2.ins.get('standalone1'), DEFAULT_SUFFIX) + st2_users = UserAccounts(topology_i2.ins.get('standalone2'), DEFAULT_SUFFIX) + + # Create user + user_properties = { + 'uid': 'testuser', + 'cn': 'testuser', + 'sn': 'user', + 'uidNumber': '1000', + 'gidNumber': '2000', + 'homeDirectory': '/home/testuser' + } + + st1_users.create(properties=user_properties) + st1_testuser = st1_users.get('testuser') + + st2_users.create(properties=user_properties) + st2_testuser = st2_users.get('testuser') + + assert UserAccount.compare(st1_testuser, st2_testuser) + + +if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s %s" % CURRENT_FILE) diff --git a/dirsrvtests/tests/suites/lib389/idm/user_compare_m2Repl_test.py b/dirsrvtests/tests/suites/lib389/idm/user_compare_m2Repl_test.py new file mode 100644 index 0000000..68c317d --- /dev/null +++ b/dirsrvtests/tests/suites/lib389/idm/user_compare_m2Repl_test.py @@ -0,0 +1,56 @@ +import os +import pytest +from lib389._constants import DEFAULT_SUFFIX +from lib389.replica import ReplicationManager +from lib389.idm.user import UserAccounts, UserAccount +from lib389.topologies import topology_m2 + + +def test_user_compare_m2Repl(topology_m2): + """ + User compare test between users of master to master replicaton topology. + + :id: 7c243bea-4075-4304-864d-5b789d364871 + + :setup: 2 master MMR + + :steps: 1. Add a user to m1 + 2. Wait for replication + 3. Compare if the user is the same + + :expectedresults: 1. User is added + 2. Replication success + 3. The user is the same + """ + rm = ReplicationManager(DEFAULT_SUFFIX) + m1 = topology_m2.ms.get('master1') + m2 = topology_m2.ms.get('master2') + + m1_users = UserAccounts(m1, DEFAULT_SUFFIX) + m2_users = UserAccounts(m2, DEFAULT_SUFFIX) + + # Create 1st user + user1_properties = { + 'uid': 'testuser', + 'cn': 'testuser', + 'sn': 'user', + 'uidNumber': '1000', + 'gidNumber': '2000', + 'homeDirectory': '/home/testuser' + } + + m1_users.create(properties=user1_properties) + m1_testuser = m1_users.get('testuser') + + rm.wait_for_replication(m1, m2) + + m2_testuser = m2_users.get('testuser') + + assert UserAccount.compare(m1_testuser, m2_testuser) + + +if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s %s" % CURRENT_FILE) diff --git a/dirsrvtests/tests/suites/lib389/idm/user_compare_st_test.py b/dirsrvtests/tests/suites/lib389/idm/user_compare_st_test.py new file mode 100644 index 0000000..70a8233 --- /dev/null +++ b/dirsrvtests/tests/suites/lib389/idm/user_compare_st_test.py @@ -0,0 +1,77 @@ +import os +import pytest +from lib389._constants import DEFAULT_SUFFIX +from lib389.idm.group import Groups +from lib389.idm.user import UserAccounts, UserAccount +from lib389.topologies import topology_st as topology + + +def test_user_compare(topology): + """ + Testing compare function + + :id: 26f2dea9-be1e-48ca-bcea-79592823390c + + :setup: Standalone instance + + :steps: + 1. Testing comparison of two different users. + 2. Testing comparison of 'str' object with itself. + 3. Testing comparison of user with similar user (different object id). + 4. Testing comparison of user with group. + + :expectedresults: + 1. Should fail to compare + 2. Should raise value error + 3. Should be the same despite uuid difference + 4. Should fail to compare + """ + users = UserAccounts(topology.standalone, DEFAULT_SUFFIX) + groups = Groups(topology.standalone, DEFAULT_SUFFIX) + # Create 1st user + user1_properties = { + 'uid': 'testuser1', + 'cn': 'testuser1', + 'sn': 'user', + 'uidNumber': '1000', + 'gidNumber': '2000', + 'homeDirectory': '/home/testuser1' + } + + users.create(properties=user1_properties) + testuser1 = users.get('testuser1') + # Create 2nd user + user2_properties = { + 'uid': 'testuser2', + 'cn': 'testuser2', + 'sn': 'user', + 'uidNumber': '1001', + 'gidNumber': '2002', + 'homeDirectory': '/home/testuser2' + } + + users.create(properties=user2_properties) + testuser2 = users.get('testuser2') + # create group + group_properties = { + 'cn' : 'group1', + 'description' : 'testgroup' + } + + testuser1_copy = users.get("testuser1") + group = groups.create(properties=group_properties) + + assert UserAccount.compare(testuser1, testuser2) is False + + with pytest.raises(ValueError): + UserAccount.compare("test_str_object","test_str_object") + + assert UserAccount.compare(testuser1, testuser1_copy) + assert UserAccount.compare(testuser1, group) is False + + +if __name__ == '__main__': + # Run isolated + # -s for DEBUG mode + CURRENT_FILE = os.path.realpath(__file__) + pytest.main("-s %s" % CURRENT_FILE) diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py index b1bd38b..4513ed9 100644 --- a/src/lib389/lib389/_mapped_object.py +++ b/src/lib389/lib389/_mapped_object.py @@ -107,7 +107,7 @@ class DSLdapObject(DSLogging): self._rdn_attribute = None self._must_attributes = None # attributes, we don't want to compare - self._compare_exclude = ['entryid'] + self._compare_exclude = ['entryid', 'modifytimestamp', 'nsuniqueid'] self._lint_functions = None self._server_controls = None self._client_controls = None @@ -415,6 +415,23 @@ class DSLdapObject(DSLogging): raise ValueError('Too many arguments in the mod op') return self._instance.modify_ext_s(self._dn, mod_list, serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure') + def _unsafe_compare_attribute(self, other): + """Compare two attributes from two objects. This is currently marked unsafe as it's + not complete yet. + + The idea is to use the native ldap compare operation, rather than simple comparison of + values due to schema awareness. LDAP doesn't normalise values, so two objects with: + + cn: value + cn: VaLuE + + This will fail to compare in python, but would succeed in LDAP beacuse CN is case + insensitive. + + To allow schema aware checking, we need to call ldap compare extop here. + """ + pass + @classmethod def compare(cls, obj1, obj2): """Compare if two RDN objects have same attributes and values. @@ -444,16 +461,20 @@ class DSLdapObject(DSLogging): raise ValueError("Invalid arguments: Expecting object types that inherits 'DSLdapObject' class") # check if RDN of objects is same if obj1.rdn != obj2.rdn: + obj1._log.debug("%s != %s" % (obj1.rdn, obj2.rdn)) return False obj1_attrs = obj1.get_compare_attrs() obj2_attrs = obj2.get_compare_attrs() # Bail fast if the keys don't match if set(obj1_attrs.keys()) != set(obj2_attrs.keys()): + obj1._log.debug("%s != %s" % (obj1_attrs.keys(), obj2_attrs.keys())) return False # Check the values of each key # using obj1_attrs.keys() because obj1_attrs.iterkleys() is not supported in python3 for key in obj1_attrs.keys(): if set(obj1_attrs[key]) != set(obj2_attrs[key]): + obj1._log.debug(" v-- %s != %s" % (key, key)) + obj1._log.debug("%s != %s" % (obj1_attrs[key], obj2_attrs[key])) return False return True @@ -463,10 +484,18 @@ class DSLdapObject(DSLogging): """ self._log.debug("%s get_compare_attrs" % (self._dn)) + all_attrs_dict = self.get_all_attrs() + all_attrs_lower = {} + for k in all_attrs_dict: + all_attrs_lower[k.lower()] = all_attrs_dict[k] + # removing _compate_exclude attrs from all attrs - compare_attrs = set(all_attrs_dict.keys()) - set(self._compare_exclude) - compare_attrs_dict = {attr:all_attrs_dict[attr] for attr in compare_attrs} + cx = [x.lower() for x in self._compare_exclude] + compare_attrs = set(all_attrs_lower.keys()) - set(cx) + + compare_attrs_dict = {attr.lower():all_attrs_lower[attr] for attr in compare_attrs} + return compare_attrs_dict def get_all_attrs(self, use_json=False): @@ -483,6 +512,8 @@ class DSLdapObject(DSLogging): attrs_entry = self._instance.search_ext_s(self._dn, ldap.SCOPE_BASE, self._object_filter, attrlist=["*", "+"], serverctrls=self._server_controls, clientctrls=self._client_controls, escapehatch='i am sure')[0] # getting dict from 'entry' object attrs_dict = attrs_entry.data + # Should we normalise the attr names here to lower()? + # This could have unforseen consequences ... return attrs_dict def get_attrs_vals(self, keys, use_json=False): diff --git a/src/lib389/lib389/passwd.py b/src/lib389/lib389/passwd.py index d0bd1d7..b0ce730 100644 --- a/src/lib389/lib389/passwd.py +++ b/src/lib389/lib389/passwd.py @@ -59,7 +59,11 @@ def password_generate(length=64): # The number of possible values for a byte is 256 which is a multiple of 64 # Maybe it is an overkill for our case but it can come handy one day # (especially consider the fact we can use it for CLI tools) - chars = string.ascii_letters + string.digits + '-.' + # + # So it turns out we don't escape the - properly, which means that in certain + # cases the "chars" yield a string like "-ntoauhtnonhtunothu", which of course + # means that the pwdhash binary says "no such option -n". + chars = string.ascii_letters + string.digits + '.' # Get the minimal requirements # Don't use characters that prevent easy highlight for copy paste ... diff --git a/src/lib389/lib389/tests/cli/conf_plugin_test.py b/src/lib389/lib389/tests/cli/conf_plugin_test.py index 3f1955f..6cd630b 100644 --- a/src/lib389/lib389/tests/cli/conf_plugin_test.py +++ b/src/lib389/lib389/tests/cli/conf_plugin_test.py @@ -8,7 +8,7 @@ import pytest -from lib389.cli_conf.plugin import plugin_list, plugin_get, plugin_get_dn, plugin_enable, plugin_disable +from lib389.cli_conf.plugin import plugin_list, plugin_get from lib389.cli_base import LogCapture, FakeArgs diff --git a/src/lib389/lib389/tests/config_compare_test.py b/src/lib389/lib389/tests/config_compare_test.py deleted file mode 100644 index f0c45cd..0000000 --- a/src/lib389/lib389/tests/config_compare_test.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import sys -import time -import ldap -import logging -import pytest -from lib389._constants import * -from lib389.properties import * -from lib389.tasks import * -from lib389.utils import * - -from lib389.idm.group import Groups -from lib389.idm.user import UserAccounts, UserAccount - -from lib389.topologies import topology_st, topology_i2 -from lib389.config import Config - -DEBUGGING = os.getenv('DEBUGGING', False) - -if DEBUGGING is not False: - DEBUGGING = True - -if DEBUGGING: - logging.getLogger(__name__).setLevel(logging.DEBUG) -else: - logging.getLogger(__name__).setLevel(logging.INFO) - -log = logging.getLogger(__name__) - - -def test_config_compare(topology_i2): - """ - Compare test between cn=config of two different Directory Server intance. - """ - if DEBUGGING: - # Add debugging steps(if any)... - pass - - st1_config = topology_i2.ins.get('standalone1').config - st2_config = topology_i2.ins.get('standalone2').config - # 'nsslapd-port' attribute is expected to be same in cn=config comparison, - # but they are different in our testing environment - # as we are using 2 DS instances running, both running simultaneuosly. - # Hence explicitly adding 'nsslapd-port' to compare_exclude. - st1_config._compare_exclude.append('nsslapd-port') - st2_config._compare_exclude.append('nsslapd-port') - - assert(Config.compare(st1_config, st2_config) == True) - log.info("Test PASSED") - - -if __name__ == '__main__': - # Run isolated - # -s for DEBUG mode - CURRENT_FILE = os.path.realpath(__file__) - pytest.main("-s %s" % CURRENT_FILE) diff --git a/src/lib389/lib389/tests/dsadmin_test.py b/src/lib389/lib389/tests/dsadmin_test.py index 114cb9d..edc2805 100644 --- a/src/lib389/lib389/tests/dsadmin_test.py +++ b/src/lib389/lib389/tests/dsadmin_test.py @@ -6,13 +6,6 @@ # See LICENSE for details. # --- END COPYRIGHT BLOCK --- -# from nose import * -# from nose.tools import * - -from . import config -from .config import log -from .config import * - import ldap import time import lib389 diff --git a/src/lib389/lib389/tests/idm/user_compare_i2_test.py b/src/lib389/lib389/tests/idm/user_compare_i2_test.py deleted file mode 100644 index c0c2004..0000000 --- a/src/lib389/lib389/tests/idm/user_compare_i2_test.py +++ /dev/null @@ -1,67 +0,0 @@ -import os -import sys -import time -import ldap -import logging -import pytest -from lib389._constants import * -from lib389.properties import * -from lib389.tasks import * -from lib389.utils import * - -from lib389.idm.group import Groups -from lib389.idm.user import UserAccounts, UserAccount - -from lib389.topologies import topology_i2 - -DEBUGGING = os.getenv('DEBUGGING', False) - -if DEBUGGING is not False: - DEBUGGING = True - -if DEBUGGING: - logging.getLogger(__name__).setLevel(logging.DEBUG) -else: - logging.getLogger(__name__).setLevel(logging.INFO) - -log = logging.getLogger(__name__) - - -def test_user_compare_i2(topology_i2): - """ - Compare test between users of two different Directory Server intances. - - """ - if DEBUGGING: - # Add debugging steps(if any)... - pass - - st1_users = UserAccounts(topology_i2.ins.get('standalone1'), DEFAULT_SUFFIX) - st2_users = UserAccounts(topology_i2.ins.get('standalone2'), DEFAULT_SUFFIX) - - # Create user - user_properties = { - 'uid': 'testuser', - 'cn': 'testuser', - 'sn': 'user', - 'uidNumber': '1000', - 'gidNumber': '2000', - 'homeDirectory': '/home/testuser' - } - - st1_users.create(properties=user_properties) - st1_testuser = st1_users.get('testuser') - - st2_users.create(properties=user_properties) - st2_testuser = st2_users.get('testuser') - - assert(UserAccount.compare(st1_testuser, st2_testuser) == True) - - log.info("Test PASSED") - - -if __name__ == '__main__': - # Run isolated - # -s for DEBUG mode - CURRENT_FILE = os.path.realpath(__file__) - pytest.main("-s %s" % CURRENT_FILE) diff --git a/src/lib389/lib389/tests/idm/user_compare_m2Repl_test.py b/src/lib389/lib389/tests/idm/user_compare_m2Repl_test.py deleted file mode 100644 index 19ac1c9..0000000 --- a/src/lib389/lib389/tests/idm/user_compare_m2Repl_test.py +++ /dev/null @@ -1,83 +0,0 @@ -import os -import sys -import time -import ldap -import logging -import pytest -import time -from lib389._constants import * -from lib389.properties import * -from lib389.tasks import * -from lib389.utils import * - -from lib389.idm.user import UserAccounts, UserAccount - -from lib389.topologies import topology_m2 - -DEBUGGING = os.getenv('DEBUGGING', False) - -if DEBUGGING is not False: - DEBUGGING = True - -if DEBUGGING: - logging.getLogger(__name__).setLevel(logging.DEBUG) -else: - logging.getLogger(__name__).setLevel(logging.INFO) - -log = logging.getLogger(__name__) - - -def test_user_compare_m2Repl(topology_m2): - """ - User compare test between users of master to master replicaton topology. - """ - if DEBUGGING: - # Add debugging steps(if any)... - pass - - m1 = topology_m2.ms.get('master1') - m2 = topology_m2.ms.get('master2') - - m1_m2_agmtdn = m1.agreement.list(suffix=DEFAULT_SUFFIX)[0].dn - - m1_users = UserAccounts(m1, DEFAULT_SUFFIX) - m2_users = UserAccounts(m2, DEFAULT_SUFFIX) - - # Create 1st user - user1_properties = { - 'uid': 'testuser', - 'cn': 'testuser', - 'sn': 'user', - 'uidNumber': '1000', - 'gidNumber': '2000', - 'homeDirectory': '/home/testuser' - } - - m1_users.create(properties=user1_properties) - m1_testuser = m1_users.get('testuser') - - log.info("Waiting for completion of replicaation.....") - # wait for replication to complete - m1.startReplication(m1_m2_agmtdn) - - m1_ruv = m1.replica.ruv(DEFAULT_SUFFIX) - m2_ruv = m2.replica.ruv(DEFAULT_SUFFIX) - - log.debug("m1 ruv : " + str(m1_ruv)) - log.debug("m2 ruv : " + str(m2_ruv)) - log.debug("RUV diffs: " + str(m1_ruv.getdiffs(m2_ruv))) - - # ruv comparison, if replication is complete then ruv's should be same - assert(m1_ruv == m2_ruv) - log.info("Replication completed") - m2_testuser = m2_users.get('testuser') - - assert(UserAccount.compare(m1_testuser, m2_testuser) == True) - log.info("Test PASSED") - - -if __name__ == '__main__': - # Run isolated - # -s for DEBUG mode - CURRENT_FILE = os.path.realpath(__file__) - pytest.main("-s %s" % CURRENT_FILE) diff --git a/src/lib389/lib389/tests/idm/user_compare_st_test.py b/src/lib389/lib389/tests/idm/user_compare_st_test.py deleted file mode 100644 index 900d980..0000000 --- a/src/lib389/lib389/tests/idm/user_compare_st_test.py +++ /dev/null @@ -1,92 +0,0 @@ -import os -import sys -import time -import ldap -import logging -import pytest -from lib389._constants import * -from lib389.properties import * -from lib389.tasks import * -from lib389.utils import * - -from lib389.idm.group import Groups -from lib389.idm.user import UserAccounts, UserAccount - -from lib389.topologies import topology_st as topology - -DEBUGGING = os.getenv('DEBUGGING', False) - -if DEBUGGING is not False: - DEBUGGING = True - -if DEBUGGING: - logging.getLogger(__name__).setLevel(logging.DEBUG) -else: - logging.getLogger(__name__).setLevel(logging.INFO) - -log = logging.getLogger(__name__) - - -def test_user_compare(topology): - """ - Testing compare function - 1. Testing comparison of two different users. - 2. Testing comparison of 'str' object with itself, should raise 'ValueError'. - 3. Testing comparison of user with similar user (different object id). - 4. Testing comparison of user with group. - """ - if DEBUGGING: - # Add debugging steps(if any)... - pass - - users = UserAccounts(topology.standalone, DEFAULT_SUFFIX) - groups = Groups(topology.standalone, DEFAULT_SUFFIX) - # Create 1st user - user1_properties = { - 'uid': 'testuser1', - 'cn': 'testuser1', - 'sn': 'user', - 'uidNumber': '1000', - 'gidNumber': '2000', - 'homeDirectory': '/home/testuser1' - } - - users.create(properties=user1_properties) - testuser1 = users.get('testuser1') - # Create 2nd user - user2_properties = { - 'uid': 'testuser2', - 'cn': 'testuser2', - 'sn': 'user', - 'uidNumber': '1001', - 'gidNumber': '2002', - 'homeDirectory': '/home/testuser2' - } - - users.create(properties=user2_properties) - testuser2 = users.get('testuser2') - # create group - group_properties = { - 'cn' : 'group1', - 'description' : 'testgroup' - } - - testuser1_copy = users.get("testuser1") - group = groups.create(properties=group_properties) - - assert(UserAccount.compare(testuser1, testuser2) == False) - - with pytest.raises(ValueError): - UserAccount.compare("test_str_object","test_str_object") - - assert(UserAccount.compare(testuser1, testuser1_copy) == True) - assert(UserAccount.compare(testuser1, group) == False) - - log.info("Test PASSED") - - -if __name__ == '__main__': - # Run isolated - # -s for DEBUG mode - CURRENT_FILE = os.path.realpath(__file__) - pytest.main("-s %s" % CURRENT_FILE)