From 60339ce804b74408fc2de0b4289a62fff5b79903 Mon Sep 17 00:00:00 2001 From: Akshay Adhikari Date: Jun 13 2018 10:55:35 +0000 Subject: Issue 49588 - Add py3 support for tickets : part-2 Description: Added py3 support by explicitly changing strings to bytes. Ported tests from ticket to test suites, also added docstrings. https://pagure.io/389-ds-base/issue/49588 Reviewed by: spichugi (Thanks!) --- diff --git a/dirsrvtests/tests/suites/acl/repeated_ldap_add_test.py b/dirsrvtests/tests/suites/acl/repeated_ldap_add_test.py new file mode 100644 index 0000000..9225f8f --- /dev/null +++ b/dirsrvtests/tests/suites/acl/repeated_ldap_add_test.py @@ -0,0 +1,487 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- +# +from subprocess import Popen + +import pytest +from lib389.paths import Paths +from lib389.tasks import * +from lib389.utils import * +from lib389.topologies import topology_st + +from lib389._constants import DN_DM, DEFAULT_SUFFIX, PASSWORD, SERVERID_STANDALONE + +logging.getLogger(__name__).setLevel(logging.DEBUG) +log = logging.getLogger(__name__) + +CONFIG_DN = 'cn=config' +BOU = 'BOU' +BINDOU = 'ou=%s,%s' % (BOU, DEFAULT_SUFFIX) +BUID = 'buser123' +TUID = 'tuser0' +BINDDN = 'uid=%s,%s' % (BUID, BINDOU) +BINDPW = BUID +TESTDN = 'uid=%s,ou=people,%s' % (TUID, DEFAULT_SUFFIX) +TESTPW = TUID +BOGUSDN = 'uid=bogus,%s' % DEFAULT_SUFFIX +BOGUSDN2 = 'uid=bogus,ou=people,%s' % DEFAULT_SUFFIX +BOGUSSUFFIX = 'uid=bogus,ou=people,dc=bogus' +GROUPOU = 'ou=groups,%s' % DEFAULT_SUFFIX +BOGUSOU = 'ou=OU,%s' % DEFAULT_SUFFIX + +def get_ldap_error_msg(e, type): + return e.args[0][type] + +def pattern_accesslog(file, log_pattern): + for i in range(5): + try: + pattern_accesslog.last_pos += 1 + except AttributeError: + pattern_accesslog.last_pos = 0 + + found = None + file.seek(pattern_accesslog.last_pos) + + # Use a while true iteration because 'for line in file: hit a + # python bug that break file.tell() + while True: + line = file.readline() + found = log_pattern.search(line) + if ((line == '') or (found)): + break + + pattern_accesslog.last_pos = file.tell() + if found: + return line + else: + time.sleep(1) + return None + + +def check_op_result(server, op, dn, superior, exists, rc): + targetdn = dn + if op == 'search': + if exists: + opstr = 'Searching existing entry' + else: + opstr = 'Searching non-existing entry' + elif op == 'add': + if exists: + opstr = 'Adding existing entry' + else: + opstr = 'Adding non-existing entry' + elif op == 'modify': + if exists: + opstr = 'Modifying existing entry' + else: + opstr = 'Modifying non-existing entry' + elif op == 'modrdn': + if superior is not None: + targetdn = superior + if exists: + opstr = 'Moving to existing superior' + else: + opstr = 'Moving to non-existing superior' + else: + if exists: + opstr = 'Renaming existing entry' + else: + opstr = 'Renaming non-existing entry' + elif op == 'delete': + if exists: + opstr = 'Deleting existing entry' + else: + opstr = 'Deleting non-existing entry' + + if ldap.SUCCESS == rc: + expstr = 'be ok' + else: + expstr = 'fail with %s' % rc.__name__ + + log.info('%s %s, which should %s.' % (opstr, targetdn, expstr)) + time.sleep(1) + hit = 0 + try: + if op == 'search': + centry = server.search_s(dn, ldap.SCOPE_BASE, 'objectclass=*') + elif op == 'add': + server.add_s(Entry((dn, {'objectclass': 'top extensibleObject'.split(), + 'cn': 'test entry'}))) + elif op == 'modify': + server.modify_s(dn, [(ldap.MOD_REPLACE, 'description', b'test')]) + elif op == 'modrdn': + if superior is not None: + server.rename_s(dn, 'uid=new', newsuperior=superior, delold=1) + else: + server.rename_s(dn, 'uid=new', delold=1) + elif op == 'delete': + server.delete_s(dn) + else: + log.fatal('Unknown operation %s' % op) + assert False + except ldap.LDAPError as e: + hit = 1 + log.info("Exception (expected): %s" % type(e).__name__) + log.info('Desc {}'.format(get_ldap_error_msg(e,'desc'))) + assert isinstance(e, rc) + if 'matched' in e.args: + log.info('Matched is returned: {}'.format(get_ldap_error_msg(e, 'matched'))) + if rc != ldap.NO_SUCH_OBJECT: + assert False + + if ldap.SUCCESS == rc: + if op == 'search': + log.info('Search should return none') + assert len(centry) == 0 + else: + if 0 == hit: + log.info('Expected to fail with %s, but passed' % rc.__name__) + assert False + + log.info('PASSED\n') + + +@pytest.mark.bz1347760 +def test_repeated_ldap_add(topology_st): + """Prevent revealing the entry info to whom has no access rights. + + :id: 76d278bd-3e51-4579-951a-753e6703b4df + :setup: Standalone instance + :steps: + 1. Disable accesslog logbuffering + 2. Bind as "cn=Directory Manager" + 3. Add a organisational unit as BOU + 4. Add a bind user as uid=buser123,ou=BOU,dc=example,dc=com + 5. Add a test user as uid=tuser0,ou=People,dc=example,dc=com + 6. Delete aci in dc=example,dc=com + 7. Bind as Directory Manager, acquire an access log path and instance dir + 8. Bind as uid=buser123,ou=BOU,dc=example,dc=com who has no right to read the entry + 9. Bind as uid=bogus,ou=people,dc=bogus,bogus who does not exist + 10. Bind as uid=buser123,ou=BOU,dc=example,dc=com,bogus with wrong password + 11. Adding aci for uid=buser123,ou=BOU,dc=example,dc=com to ou=BOU,dc=example,dc=com. + 12. Bind as uid=buser123,ou=BOU,dc=example,dc=com now who has right to read the entry + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. Operation should be successful + 6. Operation should be successful + 7. Operation should be successful + 8. Bind operation should be successful with no search result + 9. Bind operation should Fail + 10. Bind operation should Fail + 11. Operation should be successful + 12. Bind operation should be successful with search result + """ + log.info('Testing Bug 1347760 - Information disclosure via repeated use of LDAP ADD operation, etc.') + + log.info('Disabling accesslog logbuffering') + topology_st.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-accesslog-logbuffering', b'off')]) + + log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD)) + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + + log.info('Adding ou=%s a bind user belongs to.' % BOU) + topology_st.standalone.add_s(Entry((BINDOU, { + 'objectclass': 'top organizationalunit'.split(), + 'ou': BOU}))) + + log.info('Adding a bind user.') + topology_st.standalone.add_s(Entry((BINDDN, + {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': 'bind user', + 'sn': 'user', + 'userPassword': BINDPW}))) + + log.info('Adding a test user.') + topology_st.standalone.add_s(Entry((TESTDN, + {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': 'test user', + 'sn': 'user', + 'userPassword': TESTPW}))) + + log.info('Deleting aci in %s.' % DEFAULT_SUFFIX) + topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE, 'aci', None)]) + + log.info('While binding as DM, acquire an access log path and instance dir') + ds_paths = Paths(serverid=topology_st.standalone.serverid, + instance=topology_st.standalone) + file_path = ds_paths.access_log + inst_dir = ds_paths.inst_dir + + log.info('Bind case 1. the bind user has no rights to read the entry itself, bind should be successful.') + log.info('Bind as {%s,%s} who has no access rights.' % (BINDDN, BINDPW)) + try: + topology_st.standalone.simple_bind_s(BINDDN, BINDPW) + except ldap.LDAPError as e: + log.info('Desc {}'.format(get_ldap_error_msg(e,'desc'))) + assert False + + file_obj = open(file_path, "r") + log.info('Access log path: %s' % file_path) + + log.info( + 'Bind case 2-1. the bind user does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__) + log.info('Bind as {%s,%s} who does not exist.' % (BOGUSDN, 'bogus')) + try: + topology_st.standalone.simple_bind_s(BOGUSDN, 'bogus') + except ldap.LDAPError as e: + log.info("Exception (expected): %s" % type(e).__name__) + log.info('Desc {}'.format(get_ldap_error_msg(e,'desc'))) + assert isinstance(e, ldap.INVALID_CREDENTIALS) + regex = re.compile('No such entry') + cause = pattern_accesslog(file_obj, regex) + if cause is None: + log.fatal('Cause not found - %s' % cause) + assert False + else: + log.info('Cause found - %s' % cause) + time.sleep(1) + + log.info( + 'Bind case 2-2. the bind user\'s suffix does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__) + log.info('Bind as {%s,%s} who does not exist.' % (BOGUSSUFFIX, 'bogus')) + with pytest.raises(ldap.INVALID_CREDENTIALS): + topology_st.standalone.simple_bind_s(BOGUSSUFFIX, 'bogus') + regex = re.compile('No suffix for bind') + cause = pattern_accesslog(file_obj, regex) + if cause is None: + log.fatal('Cause not found - %s' % cause) + assert False + else: + log.info('Cause found - %s' % cause) + time.sleep(1) + + log.info( + 'Bind case 2-3. the bind user\'s password is wrong, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__) + log.info('Bind as {%s,%s} who does not exist.' % (BINDDN, 'bogus')) + try: + topology_st.standalone.simple_bind_s(BINDDN, 'bogus') + except ldap.LDAPError as e: + log.info("Exception (expected): %s" % type(e).__name__) + log.info('Desc {}'.format(get_ldap_error_msg(e,'desc'))) + assert isinstance(e, ldap.INVALID_CREDENTIALS) + regex = re.compile('Invalid credentials') + cause = pattern_accesslog(file_obj, regex) + if cause is None: + log.fatal('Cause not found - %s' % cause) + assert False + else: + log.info('Cause found - %s' % cause) + time.sleep(1) + + log.info('Adding aci for %s to %s.' % (BINDDN, BINDOU)) + acival = '(targetattr="*")(version 3.0; acl "%s"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN) + log.info('aci: %s' % acival) + log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD)) + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + topology_st.standalone.modify_s(BINDOU, [(ldap.MOD_ADD, 'aci', ensure_bytes(acival))]) + time.sleep(1) + + log.info('Bind case 3. the bind user has the right to read the entry itself, bind should be successful.') + log.info('Bind as {%s,%s} which should be ok.\n' % (BINDDN, BINDPW)) + topology_st.standalone.simple_bind_s(BINDDN, BINDPW) + + log.info('The following operations are against the subtree the bind user %s has no rights.' % BINDDN) + # Search + exists = True + rc = ldap.SUCCESS + log.info( + 'Search case 1. the bind user has no rights to read the search entry, it should return no search results with %s' % rc) + check_op_result(topology_st.standalone, 'search', TESTDN, None, exists, rc) + + exists = False + rc = ldap.SUCCESS + log.info( + 'Search case 2-1. the search entry does not exist, the search should return no search results with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'search', BOGUSDN, None, exists, rc) + + exists = False + rc = ldap.SUCCESS + log.info( + 'Search case 2-2. the search entry does not exist, the search should return no search results with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc) + + # Add + exists = True + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Add case 1. the bind user has no rights AND the adding entry exists, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Add case 2-1. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'add', BOGUSDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Add case 2-2. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'add', BOGUSDN2, None, exists, rc) + + # Modify + exists = True + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modify case 1. the bind user has no rights AND the modifying entry exists, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modify', TESTDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modify case 2-1. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modify case 2-2. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modify', BOGUSDN2, None, exists, rc) + + # Modrdn + exists = True + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modrdn case 1. the bind user has no rights AND the renaming entry exists, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', TESTDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modrdn case 2-1. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modrdn case 2-2. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', BOGUSDN2, None, exists, rc) + + exists = True + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modrdn case 3. the bind user has no rights AND the node moving an entry to exists, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', TESTDN, GROUPOU, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modrdn case 4-1. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Modrdn case 4-2. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc) + + # Delete + exists = True + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Delete case 1. the bind user has no rights AND the deleting entry exists, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'delete', TESTDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Delete case 2-1. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc) + + exists = False + rc = ldap.INSUFFICIENT_ACCESS + log.info( + 'Delete case 2-2. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'delete', BOGUSDN2, None, exists, rc) + + log.info('EXTRA: Check no regressions') + log.info('Adding aci for %s to %s.' % (BINDDN, DEFAULT_SUFFIX)) + acival = '(targetattr="*")(version 3.0; acl "%s-all"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN) + log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD)) + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD, 'aci', ensure_bytes(acival))]) + time.sleep(1) + + log.info('Bind as {%s,%s}.' % (BINDDN, BINDPW)) + try: + topology_st.standalone.simple_bind_s(BINDDN, BINDPW) + except ldap.LDAPError as e: + log.info('Desc {}'.format(get_ldap_error_msg(e,'desc'))) + assert False + time.sleep(1) + + exists = False + rc = ldap.NO_SUCH_OBJECT + log.info('Search case. the search entry does not exist, the search should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc) + file_obj.close() + + exists = True + rc = ldap.ALREADY_EXISTS + log.info('Add case. the adding entry already exists, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc) + + exists = False + rc = ldap.NO_SUCH_OBJECT + log.info('Modify case. the modifying entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc) + + exists = False + rc = ldap.NO_SUCH_OBJECT + log.info('Modrdn case 1. the renaming entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc) + + exists = False + rc = ldap.NO_SUCH_OBJECT + log.info('Modrdn case 2. the node moving an entry to does not, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc) + + exists = False + rc = ldap.NO_SUCH_OBJECT + log.info('Delete case. the deleting entry does not exist, it should fail with %s' % rc.__name__) + check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc) + + log.info('Inactivate %s' % BINDDN) + if ds_paths.version < '1.3': + nsinactivate = '%s/ns-inactivate.pl' % inst_dir + nsinactivate_cmd = [nsinactivate, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN] + else: + nsinactivate = '%s/ns-inactivate.pl' % ds_paths.sbin_dir + nsinactivate_cmd = [nsinactivate, '-Z', SERVERID_STANDALONE, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN] + log.info(nsinactivate_cmd) + p = Popen(nsinactivate_cmd) + assert (p.wait() == 0) + + log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, BUID, ldap.UNWILLING_TO_PERFORM.__name__)) + try: + topology_st.standalone.simple_bind_s(BINDDN, BUID) + except ldap.LDAPError as e: + log.info("Exception (expected): %s" % type(e).__name__) + log.info('Desc {}'.format(get_ldap_error_msg(e,'desc'))) + assert isinstance(e, ldap.UNWILLING_TO_PERFORM) + + log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, 'bogus', ldap.UNWILLING_TO_PERFORM.__name__)) + try: + topology_st.standalone.simple_bind_s(BINDDN, 'bogus') + except ldap.LDAPError as e: + log.info("Exception (expected): %s" % type(e).__name__) + log.info('Desc {}'.format(get_ldap_error_msg(e,'desc'))) + assert isinstance(e, ldap.UNWILLING_TO_PERFORM) + + log.info('SUCCESS') + + +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/acl/selfdn_permissions_test.py b/dirsrvtests/tests/suites/acl/selfdn_permissions_test.py new file mode 100644 index 0000000..6858ca7 --- /dev/null +++ b/dirsrvtests/tests/suites/acl/selfdn_permissions_test.py @@ -0,0 +1,351 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- +# +import logging + +import ldap +import pytest +from lib389 import Entry +from lib389._constants import * +from lib389.topologies import topology_st + +log = logging.getLogger(__name__) + +from lib389.utils import * + +# Skip on older versions +pytestmark = pytest.mark.skipif(ds_is_older('1.3.2'), reason="Not implemented") +OC_NAME = 'OCticket47653' +MUST = "(postalAddress $ postalCode)" +MAY = "(member $ street)" + +OTHER_NAME = 'other_entry' +MAX_OTHERS = 10 + +BIND_NAME = 'bind_entry' +BIND_DN = 'cn=%s, %s' % (BIND_NAME, SUFFIX) +BIND_PW = 'password' + +ENTRY_NAME = 'test_entry' +ENTRY_DN = 'cn=%s, %s' % (ENTRY_NAME, SUFFIX) +ENTRY_OC = "top person %s" % OC_NAME + + +def _oc_definition(oid_ext, name, must=None, may=None): + oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext + desc = 'To test ticket 47490' + sup = 'person' + if not must: + must = MUST + if not may: + may = MAY + + new_oc = "( %s NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may) + return ensure_bytes(new_oc) + + +@pytest.fixture(scope="module") +def allow_user_init(topology_st): + """Initialize the test environment + + """ + topology_st.standalone.log.info("Add %s that allows 'member' attribute" % OC_NAME) + new_oc = _oc_definition(2, OC_NAME, must=MUST, may=MAY) + topology_st.standalone.schema.add_schema('objectClasses', new_oc) + + # entry used to bind with + topology_st.standalone.log.info("Add %s" % BIND_DN) + topology_st.standalone.add_s(Entry((BIND_DN, { + 'objectclass': "top person".split(), + 'sn': BIND_NAME, + 'cn': BIND_NAME, + 'userpassword': BIND_PW}))) + + # enable acl error logging + mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'128')] + topology_st.standalone.modify_s(DN_CONFIG, mod) + + # Remove aci's to start with a clean slate + mod = [(ldap.MOD_DELETE, 'aci', None)] + topology_st.standalone.modify_s(SUFFIX, mod) + + # add dummy entries + for cpt in range(MAX_OTHERS): + name = "%s%d" % (OTHER_NAME, cpt) + topology_st.standalone.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), { + 'objectclass': "top person".split(), + 'sn': name, + 'cn': name}))) + + +@pytest.mark.ds47653 +def test_selfdn_permission_add(topology_st, allow_user_init): + """Check add entry operation with and without SelfDN aci + + :id: e837a9ef-be92-48da-ad8b-ebf42b0fede1 + :setup: Standalone instance, add a entry which is used to bind, + enable acl error logging by setting 'nsslapd-errorlog-level' to '128', + remove aci's to start with a clean slate, and add dummy entries + :steps: + 1. Check we can not ADD an entry without the proper SELFDN aci + 2. Check with the proper ACI we can not ADD with 'member' attribute + 3. Check entry to add with memberS and with the ACI + 4. Check with the proper ACI and 'member' it succeeds to ADD + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should fail with Insufficient Access + 4. Operation should be successful + """ + topology_st.standalone.log.info("\n\n######################### ADD ######################\n") + + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + # Prepare the entry with multivalued members + entry_with_members = Entry(ENTRY_DN) + entry_with_members.setValues('objectclass', 'top', 'person', 'OCticket47653') + entry_with_members.setValues('sn', ENTRY_NAME) + entry_with_members.setValues('cn', ENTRY_NAME) + entry_with_members.setValues('postalAddress', 'here') + entry_with_members.setValues('postalCode', '1234') + members = [] + for cpt in range(MAX_OTHERS): + name = "%s%d" % (OTHER_NAME, cpt) + members.append("cn=%s,%s" % (name, SUFFIX)) + members.append(BIND_DN) + entry_with_members.setValues('member', members) + + # Prepare the entry with one member + entry_with_member = Entry(ENTRY_DN) + entry_with_member.setValues('objectclass', 'top', 'person', 'OCticket47653') + entry_with_member.setValues('sn', ENTRY_NAME) + entry_with_member.setValues('cn', ENTRY_NAME) + entry_with_member.setValues('postalAddress', 'here') + entry_with_member.setValues('postalCode', '1234') + member = [] + member.append(BIND_DN) + entry_with_member.setValues('member', member) + + # entry to add WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS + try: + topology_st.standalone.log.info("Try to add Add %s (aci is missing): %r" % (ENTRY_DN, entry_with_member)) + + topology_st.standalone.add_s(entry_with_member) + except Exception as e: + topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # Ok Now add the proper ACI + topology_st.standalone.log.info("Bind as %s and add the ADD SELFDN aci" % DN_DM) + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + + ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX + ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME + ACI_ALLOW = "(version 3.0; acl \"SelfDN add\"; allow (add)" + ACI_SUBJECT = " userattr = \"member#selfDN\";)" + ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT + mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))] + topology_st.standalone.modify_s(SUFFIX, mod) + + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + # entry to add WITHOUT member and WITH the ACI -> ldap.INSUFFICIENT_ACCESS + try: + topology_st.standalone.log.info("Try to add Add %s (member is missing)" % ENTRY_DN) + topology_st.standalone.add_s(Entry((ENTRY_DN, { + 'objectclass': ENTRY_OC.split(), + 'sn': ENTRY_NAME, + 'cn': ENTRY_NAME, + 'postalAddress': 'here', + 'postalCode': '1234'}))) + except Exception as e: + topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # entry to add WITH memberS and WITH the ACI -> ldap.INSUFFICIENT_ACCESS + # member should contain only one value + try: + topology_st.standalone.log.info("Try to add Add %s (with several member values)" % ENTRY_DN) + topology_st.standalone.add_s(entry_with_members) + except Exception as e: + topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + topology_st.standalone.log.info("Try to add Add %s should be successful" % ENTRY_DN) + topology_st.standalone.add_s(entry_with_member) + + +@pytest.mark.ds47653 +def test_selfdn_permission_search(topology_st, allow_user_init): + """Check search operation with and without SelfDN aci + + :id: 06d51ef9-c675-4583-99b2-4852dbda190e + :setup: Standalone instance, add a entry which is used to bind, + enable acl error logging by setting 'nsslapd-errorlog-level' to '128', + remove aci's to start with a clean slate, and add dummy entries + :steps: + 1. Check we can not search an entry without the proper SELFDN aci + 2. Add proper ACI + 3. Check we can search with the proper ACI + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + """ + topology_st.standalone.log.info("\n\n######################### SEARCH ######################\n") + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + # entry to search WITH member being BIND_DN but WITHOUT the ACI -> no entry returned + topology_st.standalone.log.info("Try to search %s (aci is missing)" % ENTRY_DN) + ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*') + assert len(ents) == 0 + + # Ok Now add the proper ACI + topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM) + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + + ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX + ACI_TARGETATTR = "(targetattr = *)" + ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME + ACI_ALLOW = "(version 3.0; acl \"SelfDN search-read\"; allow (read, search, compare)" + ACI_SUBJECT = " userattr = \"member#selfDN\";)" + ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT + mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))] + topology_st.standalone.modify_s(SUFFIX, mod) + + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + # entry to search with the proper aci + topology_st.standalone.log.info("Try to search %s should be successful" % ENTRY_DN) + ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*') + assert len(ents) == 1 + + +@pytest.mark.ds47653 +def test_selfdn_permission_modify(topology_st, allow_user_init): + """Check modify operation with and without SelfDN aci + + :id: 97a58844-095f-44b0-9029-dd29a7d83d68 + :setup: Standalone instance, add a entry which is used to bind, + enable acl error logging by setting 'nsslapd-errorlog-level' to '128', + remove aci's to start with a clean slate, and add dummy entries + :steps: + 1. Check we can not modify an entry without the proper SELFDN aci + 2. Add proper ACI + 3. Modify the entry and check the modified value + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + """ + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + topology_st.standalone.log.info("\n\n######################### MODIFY ######################\n") + + # entry to modify WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS + try: + topology_st.standalone.log.info("Try to modify %s (aci is missing)" % ENTRY_DN) + mod = [(ldap.MOD_REPLACE, 'postalCode', b'9876')] + topology_st.standalone.modify_s(ENTRY_DN, mod) + except Exception as e: + topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # Ok Now add the proper ACI + topology_st.standalone.log.info("Bind as %s and add the WRITE SELFDN aci" % DN_DM) + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + + ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX + ACI_TARGETATTR = "(targetattr = *)" + ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME + ACI_ALLOW = "(version 3.0; acl \"SelfDN write\"; allow (write)" + ACI_SUBJECT = " userattr = \"member#selfDN\";)" + ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT + mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))] + topology_st.standalone.modify_s(SUFFIX, mod) + + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + # modify the entry and checks the value + topology_st.standalone.log.info("Try to modify %s. It should succeeds" % ENTRY_DN) + mod = [(ldap.MOD_REPLACE, 'postalCode', b'1928')] + topology_st.standalone.modify_s(ENTRY_DN, mod) + + ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*') + assert len(ents) == 1 + assert ensure_str(ents[0].postalCode) == '1928' + + +@pytest.mark.ds47653 +def test_selfdn_permission_delete(topology_st, allow_user_init): + """Check delete operation with and without SelfDN aci + + :id: 0ec4c0ec-e7b0-4ef1-8373-ab25aae34516 + :setup: Standalone instance, add a entry which is used to bind, + enable acl error logging by setting 'nsslapd-errorlog-level' to '128', + remove aci's to start with a clean slate, and add dummy entries + :steps: + 1. Check we can not delete an entry without the proper SELFDN aci + 2. Add proper ACI + 3. Check we can perform delete operation with proper ACI + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + """ + topology_st.standalone.log.info("\n\n######################### DELETE ######################\n") + + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + # entry to delete WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS + try: + topology_st.standalone.log.info("Try to delete %s (aci is missing)" % ENTRY_DN) + topology_st.standalone.delete_s(ENTRY_DN) + except Exception as e: + topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # Ok Now add the proper ACI + topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM) + topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) + + ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX + ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME + ACI_ALLOW = "(version 3.0; acl \"SelfDN delete\"; allow (delete)" + ACI_SUBJECT = " userattr = \"member#selfDN\";)" + ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT + mod = [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI_BODY))] + topology_st.standalone.modify_s(SUFFIX, mod) + + # bind as bind_entry + topology_st.standalone.log.info("Bind as %s" % BIND_DN) + topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) + + # entry to delete with the proper aci + topology_st.standalone.log.info("Try to delete %s should be successful" % ENTRY_DN) + topology_st.standalone.delete_s(ENTRY_DN) + + +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/plugins/attr_nsslapd-pluginarg_test.py b/dirsrvtests/tests/suites/plugins/attr_nsslapd-pluginarg_test.py new file mode 100644 index 0000000..4dee6a0 --- /dev/null +++ b/dirsrvtests/tests/suites/plugins/attr_nsslapd-pluginarg_test.py @@ -0,0 +1,210 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2016 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.tasks import * +from lib389.utils import * +from lib389.topologies import topology_st + +from lib389._constants import DEFAULT_SUFFIX, DN_PLUGIN, SUFFIX, PLUGIN_7_BIT_CHECK + +# Skip on older versions +pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented") + +logging.getLogger(__name__).setLevel(logging.DEBUG) +log = logging.getLogger(__name__) + +DN_7BITPLUGIN = "cn=7-bit check,%s" % DN_PLUGIN +ATTRS = ["uid", "mail", "userpassword", ",", SUFFIX, None] + + +@pytest.fixture(scope="module") +def enable_plugin(topology_st): + """Enabling the 7-bit plugin for the + environment setup""" + log.info("Ticket 47431 - 0: Enable 7bit plugin...") + topology_st.standalone.plugins.enable(name=PLUGIN_7_BIT_CHECK) + + +@pytest.mark.ds47431 +def test_duplicate_values(topology_st, enable_plugin): + """Check 26 duplicate values are treated as one + + :id: b23e04f1-2757-42cc-b3a2-26426c903f6d + :setup: Standalone instance, enable 7bit plugin + :steps: + 1. Modify the entry for cn=7-bit check,cn=plugins,cn=config as : + nsslapd-pluginarg0 : uid + nsslapd-pluginarg1 : mail + nsslapd-pluginarg2 : userpassword + nsslapd-pluginarg3 : , + nsslapd-pluginarg4 : dc=example,dc=com + 2. Set nsslapd-pluginarg2 to 'userpassword' for multiple time (ideally 27) + 3. Check whether duplicate values are treated as one + :expectedresults: + 1. It should be modified successfully + 2. It should be successful + 3. It should be successful + """ + + log.info("Ticket 47431 - 1: Check 26 duplicate values are treated as one...") + expected = "str2entry_dupcheck.* duplicate values for attribute type nsslapd-pluginarg2 detected in entry cn=7-bit check,cn=plugins,cn=config." + + log.debug('modify_s %s' % DN_7BITPLUGIN) + topology_st.standalone.modify_s(DN_7BITPLUGIN, + [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', b"uid"), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', b"mail"), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', b"userpassword"), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', b","), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg4', ensure_bytes(SUFFIX))]) + + arg2 = "nsslapd-pluginarg2: userpassword" + topology_st.standalone.stop() + dse_ldif = topology_st.standalone.confdir + '/dse.ldif' + os.system('mv %s %s.47431' % (dse_ldif, dse_ldif)) + os.system( + 'sed -e "s/\\(%s\\)/\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1/" %s.47431 > %s' % ( + arg2, dse_ldif, dse_ldif)) + topology_st.standalone.start() + + cmdline = 'egrep -i "%s" %s' % (expected, topology_st.standalone.errlog) + p = os.popen(cmdline, "r") + line = p.readline() + if line == "": + log.error('Expected error "%s" not logged in %s' % (expected, topology_st.standalone.errlog)) + assert False + else: + log.debug('line: %s' % line) + log.info('Expected error "%s" logged in %s' % (expected, topology_st.standalone.errlog)) + + log.info("Ticket 47431 - 1: done") + + +@pytest.mark.ds47431 +def test_multiple_value(topology_st, enable_plugin): + """Check two values belonging to one arg is fixed + + :id: 20c802bc-332f-4e8d-bcfb-8cd28123d695 + :setup: Standalone instance, enable 7bit plugin + :steps: + 1. Modify the entry for cn=7-bit check,cn=plugins,cn=config as : + nsslapd-pluginarg0 : uid + nsslapd-pluginarg0 : mail + nsslapd-pluginarg1 : userpassword + nsslapd-pluginarg2 : , + nsslapd-pluginarg3 : dc=example,dc=com + nsslapd-pluginarg4 : None + (Note : While modifying add two attributes entries for nsslapd-pluginarg0) + + 2. Check two values belonging to one arg is fixed + :expectedresults: + 1. Entries should be modified successfully + 2. Operation should be successful + """ + + log.info("Ticket 47431 - 2: Check two values belonging to one arg is fixed...") + + topology_st.standalone.modify_s(DN_7BITPLUGIN, + [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', b"uid"), + (ldap.MOD_ADD, 'nsslapd-pluginarg0', b"mail"), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', b"userpassword"), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', b","), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', ensure_bytes(SUFFIX)), + (ldap.MOD_DELETE, 'nsslapd-pluginarg4', None)]) + + # PLUGIN LOG LEVEL + topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'65536')]) + + topology_st.standalone.restart() + + cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog) + p = os.popen(cmdline, "r") + i = 0 + while ATTRS[i]: + line = p.readline() + log.debug('line - %s' % line) + log.debug('ATTRS[%d] %s' % (i, ATTRS[i])) + if line == "": + break + elif line.find(ATTRS[i]) >= 0: + log.debug('%s was logged' % ATTRS[i]) + else: + log.error('%s was not logged.' % ATTRS[i]) + assert False + i = i + 1 + + log.info("Ticket 47431 - 2: done") + + +@pytest.mark.ds47431 +def test_missing_args(topology_st, enable_plugin): + """Check missing args are fixed + + :id: b2814399-7ed2-4fe0-981d-b0bdbbe31cfb + :setup: Standalone instance, enable 7bit plugin + :steps: + 1. Modify the entry for cn=7-bit check,cn=plugins,cn=config as : + nsslapd-pluginarg0 : None + nsslapd-pluginarg1 : uid + nsslapd-pluginarg2 : None + nsslapd-pluginarg3 : mail + nsslapd-pluginarg5 : userpassword + nsslapd-pluginarg7 : , + nsslapd-pluginarg9 : dc=example,dc=com + (Note: While modifying add 2 entries as None) + + 2. Change the nsslapd-errorlog-level to 65536 + 3. Check missing agrs are fixed + :expectedresults: + 1. Entries should be modified successfully + 2. Operation should be successful + 3. Operation should be successful + """ + + log.info("Ticket 47431 - 3: Check missing args are fixed...") + + topology_st.standalone.modify_s(DN_7BITPLUGIN, + [(ldap.MOD_DELETE, 'nsslapd-pluginarg0', None), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', b"uid"), + (ldap.MOD_DELETE, 'nsslapd-pluginarg2', None), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', b"mail"), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg5', b"userpassword"), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg7', b","), + (ldap.MOD_REPLACE, 'nsslapd-pluginarg9', ensure_bytes(SUFFIX))]) + + # PLUGIN LOG LEVEL + topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'65536')]) + + topology_st.standalone.stop() + os.system('mv %s %s.47431' % (topology_st.standalone.errlog, topology_st.standalone.errlog)) + os.system('touch %s' % (topology_st.standalone.errlog)) + topology_st.standalone.start() + + cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog) + p = os.popen(cmdline, "r") + i = 0 + while ATTRS[i]: + line = p.readline() + if line == "": + break + elif line.find(ATTRS[i]) >= 0: + log.debug('%s was logged' % ATTRS[i]) + else: + log.error('%s was not logged.' % ATTRS[i]) + assert False + i = i + 1 + + log.info("Ticket 47431 - 3: done") + log.info('Test complete') + + +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/replication/changelog_test.py b/dirsrvtests/tests/suites/replication/changelog_test.py index 3b5d547..b4ae7c6 100755 --- a/dirsrvtests/tests/suites/replication/changelog_test.py +++ b/dirsrvtests/tests/suites/replication/changelog_test.py @@ -16,9 +16,17 @@ from lib389.replica import Replicas from lib389.idm.user import UserAccounts from lib389.topologies import topology_m2 as topo from lib389._constants import * +from lib389.tasks import * +from lib389.utils import * TEST_ENTRY_NAME = 'replusr' NEW_RDN_NAME = 'cl5usr' +CHANGELOG = 'cn=changelog5,cn=config' +RETROCHANGELOG = 'cn=Retro Changelog Plugin,cn=plugins,cn=config' +MAXAGE = 'nsslapd-changelogmaxage' +TRIMINTERVAL = 'nsslapd-changelogtrim-interval' +COMPACTDBINTERVAL = 'nsslapd-changelogcompactdb-interval' +FILTER = '(cn=*)' DEBUGGING = os.getenv('DEBUGGING', default=False) if DEBUGGING: @@ -109,6 +117,84 @@ def _check_changelog_ldif(topo, changelog_ldif): changetype operations' +def get_ldap_error_msg(e, type): + return e.args[0][type] + + +@pytest.fixture(scope="module") +def changelog_init(topo): + """Initialize the test environment by changing log dir and + enabling cn=Retro Changelog Plugin,cn=plugins,cn=config + """ + log.info('Testing Ticket 47669 - Test duration syntax in the changelogs') + + # bind as directory manager + topo.ms["master1"].log.info("Bind as %s" % DN_DM) + topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD) + + try: + changelogdir = os.path.join(os.path.dirname(topo.ms["master1"].dbdir), 'changelog') + topo.ms["master1"].modify_s(CHANGELOG, [(ldap.MOD_REPLACE, 'nsslapd-changelogdir', + ensure_bytes(changelogdir))]) + except ldap.LDAPError as e: + log.error('Failed to modify ' + CHANGELOG + ': error {}'.format(get_ldap_error_msg(e,'desc'))) + assert False + + try: + topo.ms["master1"].modify_s(RETROCHANGELOG, [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', b'on')]) + except ldap.LDAPError as e: + log.error('Failed to enable ' + RETROCHANGELOG + ': error {}'.format(get_ldap_error_msg(e, 'desc'))) + assert False + + # restart the server + topo.ms["master1"].restart(timeout=10) + + +def add_and_check(topo, plugin, attr, val, isvalid): + """ + Helper function to add/replace attr: val and check the added value + """ + if isvalid: + log.info('Test %s: %s -- valid' % (attr, val)) + try: + topo.ms["master1"].modify_s(plugin, [(ldap.MOD_REPLACE, attr, ensure_bytes(val))]) + except ldap.LDAPError as e: + log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error {}'.format(get_ldap_error_msg(e,'desc'))) + assert False + else: + log.info('Test %s: %s -- invalid' % (attr, val)) + if plugin == CHANGELOG: + try: + topo.ms["master1"].modify_s(plugin, [(ldap.MOD_REPLACE, attr, ensure_bytes(val))]) + except ldap.LDAPError as e: + log.error('Expectedly failed to add ' + attr + ': ' + val + + ' to ' + plugin + ': error {}'.format(get_ldap_error_msg(e,'desc'))) + else: + try: + topo.ms["master1"].modify_s(plugin, [(ldap.MOD_REPLACE, attr, ensure_bytes(val))]) + except ldap.LDAPError as e: + log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error {}'.format(get_ldap_error_msg(e,'desc'))) + + try: + entries = topo.ms["master1"].search_s(plugin, ldap.SCOPE_BASE, FILTER, [attr]) + if isvalid: + if not entries[0].hasValue(attr, val): + log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val)) + assert False + else: + if plugin == CHANGELOG: + if entries[0].hasValue(attr, val): + log.fatal('%s has unexpected (%s: %s)' % (plugin, attr, val)) + assert False + else: + if not entries[0].hasValue(attr, val): + log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val)) + assert False + except ldap.LDAPError as e: + log.fatal('Unable to search for entry %s: error %s' % (plugin, e.message['desc'])) + assert False + + def test_verify_changelog(topo): """Check if changelog dump file contains required ldap operations @@ -230,8 +316,143 @@ def test_verify_changelog_offline_backup(topo): _check_changelog_ldif(topo, changelog_ldif) +@pytest.mark.ds47669 +def test_changelog_maxage(topo, changelog_init): + """Check nsslapd-changelog max age values + + :id: d284ff27-03b2-412c-ac74-ac4f2d2fae3b + :setup: Replication with two master, change nsslapd-changelogdir to + '/var/lib/dirsrv/slapd-master1/changelog' and + set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on' + :steps: + 1. Set nsslapd-changelogmaxage in cn=changelog5,cn=config to values - '12345','10s','30M','12h','2D','4w' + 2. Set nsslapd-changelogmaxage in cn=changelog5,cn=config to values - '-123','xyz' + + :expectedresults: + 1. Operation should be successful + 2. Operation should be unsuccessful + """ + log.info('1. Test nsslapd-changelogmaxage in cn=changelog5,cn=config') + + # bind as directory manager + topo.ms["master1"].log.info("Bind as %s" % DN_DM) + topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD) + + add_and_check(topo, CHANGELOG, MAXAGE, '12345', True) + add_and_check(topo, CHANGELOG, MAXAGE, '10s', True) + add_and_check(topo, CHANGELOG, MAXAGE, '30M', True) + add_and_check(topo, CHANGELOG, MAXAGE, '12h', True) + add_and_check(topo, CHANGELOG, MAXAGE, '2D', True) + add_and_check(topo, CHANGELOG, MAXAGE, '4w', True) + add_and_check(topo, CHANGELOG, MAXAGE, '-123', False) + add_and_check(topo, CHANGELOG, MAXAGE, 'xyz', False) + + +@pytest.mark.ds47669 +def test_ticket47669_changelog_triminterval(topo, changelog_init): + """Check nsslapd-changelog triminterval values + + :id: 8f850c37-7e7c-49dd-a4e0-9344638616d6 + :setup: Replication with two master, change nsslapd-changelogdir to + '/var/lib/dirsrv/slapd-master1/changelog' and + set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on' + :steps: + 1. Set nsslapd-changelogtrim-interval in cn=changelog5,cn=config to values - + '12345','10s','30M','12h','2D','4w' + 2. Set nsslapd-changelogtrim-interval in cn=changelog5,cn=config to values - '-123','xyz' + + :expectedresults: + 1. Operation should be successful + 2. Operation should be unsuccessful + """ + log.info('2. Test nsslapd-changelogtrim-interval in cn=changelog5,cn=config') + + # bind as directory manager + topo.ms["master1"].log.info("Bind as %s" % DN_DM) + topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD) + + add_and_check(topo, CHANGELOG, TRIMINTERVAL, '12345', True) + add_and_check(topo, CHANGELOG, TRIMINTERVAL, '10s', True) + add_and_check(topo, CHANGELOG, TRIMINTERVAL, '30M', True) + add_and_check(topo, CHANGELOG, TRIMINTERVAL, '12h', True) + add_and_check(topo, CHANGELOG, TRIMINTERVAL, '2D', True) + add_and_check(topo, CHANGELOG, TRIMINTERVAL, '4w', True) + add_and_check(topo, CHANGELOG, TRIMINTERVAL, '-123', False) + add_and_check(topo, CHANGELOG, TRIMINTERVAL, 'xyz', False) + + +@pytest.mark.ds47669 +def test_changelog_compactdbinterval(topo, changelog_init): + """Check nsslapd-changelog compactdbinterval values + + :id: 0f4b3118-9dfa-4c2a-945c-72847b42a48c + :setup: Replication with two master, change nsslapd-changelogdir to + '/var/lib/dirsrv/slapd-master1/changelog' and + set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on' + :steps: + 1. Set nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config to values - + '12345','10s','30M','12h','2D','4w' + 2. Set nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config to values - + '-123','xyz' + + :expectedresults: + 1. Operation should be successful + 2. Operation should be unsuccessful + """ + log.info('3. Test nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config') + + # bind as directory manager + topo.ms["master1"].log.info("Bind as %s" % DN_DM) + topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD) + + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '12345', True) + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '10s', True) + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '30M', True) + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '12h', True) + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '2D', True) + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '4w', True) + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, '-123', False) + add_and_check(topo, CHANGELOG, COMPACTDBINTERVAL, 'xyz', False) + + +@pytest.mark.ds47669 +def test_retrochangelog_maxage(topo, changelog_init): + """Check nsslapd-retrochangelog max age values + + :id: 0cb84d81-3e86-4dbf-84a2-66aefd8281db + :setup: Replication with two master, change nsslapd-changelogdir to + '/var/lib/dirsrv/slapd-master1/changelog' and + set cn=Retro Changelog Plugin,cn=plugins,cn=config to 'on' + :steps: + 1. Set nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config to values - + '12345','10s','30M','12h','2D','4w' + 2. Set nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config to values - + '-123','xyz' + + :expectedresults: + 1. Operation should be successful + 2. Operation should be unsuccessful + """ + log.info('4. Test nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config') + + # bind as directory manager + topo.ms["master1"].log.info("Bind as %s" % DN_DM) + topo.ms["master1"].simple_bind_s(DN_DM, PASSWORD) + + add_and_check(topo, RETROCHANGELOG, MAXAGE, '12345', True) + add_and_check(topo, RETROCHANGELOG, MAXAGE, '10s', True) + add_and_check(topo, RETROCHANGELOG, MAXAGE, '30M', True) + add_and_check(topo, RETROCHANGELOG, MAXAGE, '12h', True) + add_and_check(topo, RETROCHANGELOG, MAXAGE, '2D', True) + add_and_check(topo, RETROCHANGELOG, MAXAGE, '4w', True) + add_and_check(topo, RETROCHANGELOG, MAXAGE, '-123', False) + add_and_check(topo, RETROCHANGELOG, MAXAGE, 'xyz', False) + + topo.ms["master1"].log.info("ticket47669 was successfully verified.") + + if __name__ == '__main__': # Run isolated # -s for DEBUG mode CURRENT_FILE = os.path.realpath(__file__) - pytest.main('-s {}'.format(CURRENT_FILE)) + pytest.main("-s %s" % CURRENT_FILE) diff --git a/dirsrvtests/tests/suites/schema/schema_replication_test.py b/dirsrvtests/tests/suites/schema/schema_replication_test.py new file mode 100644 index 0000000..00568c8 --- /dev/null +++ b/dirsrvtests/tests/suites/schema/schema_replication_test.py @@ -0,0 +1,701 @@ +# --- BEGIN COPYRIGHT BLOCK --- +# Copyright (C) 2016 Red Hat, Inc. +# All rights reserved. +# +# License: GPL (version 3 or any later version). +# See LICENSE for details. +# --- END COPYRIGHT BLOCK --- +# +''' +Created on Nov 7, 2013 + +@author: tbordaz +''' +import logging +import re +import time + +import ldap +import pytest +from lib389 import Entry +from lib389._constants import * +from lib389.topologies import topology_m1c1 + +from lib389.utils import * + +# Skip on older versions +pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented") +logging.getLogger(__name__).setLevel(logging.DEBUG) +log = logging.getLogger(__name__) + +TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX +ENTRY_DN = "cn=test_entry, %s" % SUFFIX +MUST_OLD = "(postalAddress $ preferredLocale)" +MUST_NEW = "(postalAddress $ preferredLocale $ telexNumber)" +MAY_OLD = "(postalCode $ street)" +MAY_NEW = "(postalCode $ street $ postOfficeBox)" + + +def _header(topology_m1c1, label): + topology_m1c1.ms["master1"].log.info("\n\n###############################################") + topology_m1c1.ms["master1"].log.info("#######") + topology_m1c1.ms["master1"].log.info("####### %s" % label) + topology_m1c1.ms["master1"].log.info("#######") + topology_m1c1.ms["master1"].log.info("###################################################") + + +def pattern_errorlog(file, log_pattern): + try: + pattern_errorlog.last_pos += 1 + except AttributeError: + pattern_errorlog.last_pos = 0 + + found = None + log.debug("_pattern_errorlog: start at offset %d" % pattern_errorlog.last_pos) + file.seek(pattern_errorlog.last_pos) + + # Use a while true iteration because 'for line in file: hit a + # python bug that break file.tell() + while True: + line = file.readline() + log.debug("_pattern_errorlog: [%d] %s" % (file.tell(), line)) + found = log_pattern.search(line) + if ((line == '') or (found)): + break + + log.debug("_pattern_errorlog: end at offset %d" % file.tell()) + pattern_errorlog.last_pos = file.tell() + return found + + +def _oc_definition(oid_ext, name, must=None, may=None): + oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext + desc = 'To test ticket 47490' + sup = 'person' + if not must: + must = MUST_OLD + if not may: + may = MAY_OLD + + new_oc = "( %s NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may) + return new_oc + + +def add_OC(instance, oid_ext, name): + new_oc = _oc_definition(oid_ext, name) + instance.schema.add_schema('objectClasses', ensure_bytes(new_oc)) + + +def mod_OC(instance, oid_ext, name, old_must=None, old_may=None, new_must=None, new_may=None): + old_oc = _oc_definition(oid_ext, name, old_must, old_may) + new_oc = _oc_definition(oid_ext, name, new_must, new_may) + instance.schema.del_schema('objectClasses', ensure_bytes(old_oc)) + instance.schema.add_schema('objectClasses', ensure_bytes(new_oc)) + + +def support_schema_learning(topology_m1c1): + """ + with https://fedorahosted.org/389/ticket/47721, the supplier and consumer can learn + schema definitions when a replication occurs. + Before that ticket: replication of the schema fails requiring administrative operation + In the test the schemaCSN (master consumer) differs + + After that ticket: replication of the schema succeeds (after an initial phase of learning) + In the test the schema CSN (master consumer) are in sync + + This function returns True if 47721 is fixed in the current release + False else + """ + ent = topology_m1c1.cs["consumer1"].getEntry(DN_CONFIG, ldap.SCOPE_BASE, "(cn=config)", ['nsslapd-versionstring']) + if ent.hasAttr('nsslapd-versionstring'): + val = ent.getValue('nsslapd-versionstring') + version = ensure_str(val).split('/')[1].split('.') # something like ['1', '3', '1', '23', 'final_fix'] + major = int(version[0]) + minor = int(version[1]) + if major > 1: + return True + if minor > 3: + # version is 1.4 or after + return True + if minor == 3: + if version[2].isdigit(): + if int(version[2]) >= 3: + return True + return False + + +def trigger_update(topology_m1c1): + """ + It triggers an update on the supplier. This will start a replication + session and a schema push + """ + try: + trigger_update.value += 1 + except AttributeError: + trigger_update.value = 1 + replace = [(ldap.MOD_REPLACE, 'telephonenumber', ensure_bytes(str(trigger_update.value)))] + topology_m1c1.ms["master1"].modify_s(ENTRY_DN, replace) + + # wait 10 seconds that the update is replicated + loop = 0 + while loop <= 10: + try: + ent = topology_m1c1.cs["consumer1"].getEntry(ENTRY_DN, ldap.SCOPE_BASE, "(objectclass=*)", + ['telephonenumber']) + val = ent.telephonenumber or "0" + if int(val) == trigger_update.value: + return + # the expected value is not yet replicated. try again + time.sleep(1) + loop += 1 + log.debug("trigger_update: receive %s (expected %d)" % (val, trigger_update.value)) + except ldap.NO_SUCH_OBJECT: + time.sleep(1) + loop += 1 + + +def trigger_schema_push(topology_m1c1): + ''' + Trigger update to create a replication session. + In case of 47721 is fixed and the replica needs to learn the missing definition, then + the first replication session learn the definition and the second replication session + push the schema (and the schemaCSN. + This is why there is two updates and replica agreement is stopped/start (to create a second session) + ''' + agreements = topology_m1c1.ms["master1"].agreement.list(suffix=SUFFIX, + consumer_host=topology_m1c1.cs["consumer1"].host, + consumer_port=topology_m1c1.cs["consumer1"].port) + assert (len(agreements) == 1) + ra = agreements[0] + trigger_update(topology_m1c1) + topology_m1c1.ms["master1"].agreement.pause(ra.dn) + topology_m1c1.ms["master1"].agreement.resume(ra.dn) + trigger_update(topology_m1c1) + + +@pytest.fixture(scope="module") +def schema_replication_init(topology_m1c1): + """Initialize the test environment + + """ + log.debug("test_schema_replication_init topology_m1c1 %r (master %r, consumer %r" % ( + topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"])) + # check if a warning message is logged in the + # error log of the supplier + topology_m1c1.ms["master1"].errorlog_file = open(topology_m1c1.ms["master1"].errlog, "r") + + # This entry will be used to trigger attempt of schema push + topology_m1c1.ms["master1"].add_s(Entry((ENTRY_DN, { + 'objectclass': "top person".split(), + 'sn': 'test_entry', + 'cn': 'test_entry'}))) + + +@pytest.mark.ds47490 +def test_schema_replication_one(topology_m1c1, schema_replication_init): + """Check supplier schema is a superset (one extra OC) of consumer schema, then + schema is pushed and there is no message in the error log + + :id: d6c6ff30-b3ae-4001-80ff-0fb18563a393 + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Update the schema of supplier, so it will be superset of consumer + 2. Push the Schema (no error) + 3. Check both master and consumer has same schemaCSN + 4. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. State at startup: + - supplier default schema + - consumer default schema + Final state + - supplier +masterNewOCA + - consumer +masterNewOCA + """ + + _header(topology_m1c1, "Extra OC Schema is pushed - no error") + + log.debug("test_schema_replication_one topology_m1c1 %r (master %r, consumer %r" % ( + topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"])) + # update the schema of the supplier so that it is a superset of + # consumer. Schema should be pushed + add_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA') + + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was updated on the consumer + log.debug("test_schema_replication_one master_schema_csn=%s", master_schema_csn) + log.debug("ctest_schema_replication_one onsumer_schema_csn=%s", consumer_schema_csn) + assert master_schema_csn == consumer_schema_csn + + # Check the error log of the supplier does not contain an error + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + if res is not None: + assert False + + +@pytest.mark.ds47490 +def test_schema_replication_two(topology_m1c1, schema_replication_init): + """Check consumer schema is a superset (one extra OC) of supplier schema, then + schema is pushed and there is a message in the error log + + :id: b5db9b75-a9a7-458e-86ec-2a8e7bd1c014 + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Update the schema of consumer, so it will be superset of supplier + 2. Update the schema of supplier so ti make it's nsSchemaCSN larger than consumer + 3. Push the Schema (error should be generated) + 4. Check supplier learns the missing definition + 5. Check the error logs + 6. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. Operation should be successful + 6. State at startup + - supplier +masterNewOCA + - consumer +masterNewOCA + Final state + - supplier +masterNewOCA +masterNewOCB + - consumer +masterNewOCA +consumerNewOCA + """ + + _header(topology_m1c1, "Extra OC Schema is pushed - (ticket 47721 allows to learn missing def)") + + # add this OC on consumer. Supplier will no push the schema + add_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA') + + # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s) + time.sleep(2) + add_OC(topology_m1c1.ms["master1"], 3, 'masterNewOCB') + + # now push the scheam + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was NOT updated on the consumer + # with 47721, supplier learns the missing definition + log.debug("test_schema_replication_two master_schema_csn=%s", master_schema_csn) + log.debug("test_schema_replication_two consumer_schema_csn=%s", consumer_schema_csn) + if support_schema_learning(topology_m1c1): + assert master_schema_csn == consumer_schema_csn + else: + assert master_schema_csn != consumer_schema_csn + + # Check the error log of the supplier does not contain an error + # This message may happen during the learning phase + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + + +@pytest.mark.ds47490 +def test_schema_replication_three(topology_m1c1, schema_replication_init): + """Check supplier schema is again a superset (one extra OC), then + schema is pushed and there is no message in the error log + + :id: 45888895-76bc-4cc3-9f90-33a69d027116 + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Update the schema of master + 2. Push the Schema (no error) + 3. Check the schemaCSN was NOT updated on the consumer + 4. Check the error logs for no errors + 5. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. State at startup + - supplier +masterNewOCA +masterNewOCB + - consumer +masterNewOCA +consumerNewOCA + Final state + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA + """ + _header(topology_m1c1, "Extra OC Schema is pushed - no error") + + # Do an upate to trigger the schema push attempt + # add this OC on consumer. Supplier will no push the schema + add_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA') + + # now push the scheam + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was NOT updated on the consumer + log.debug("test_schema_replication_three master_schema_csn=%s", master_schema_csn) + log.debug("test_schema_replication_three consumer_schema_csn=%s", consumer_schema_csn) + assert master_schema_csn == consumer_schema_csn + + # Check the error log of the supplier does not contain an error + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + if res is not None: + assert False + + +@pytest.mark.ds47490 +def test_schema_replication_four(topology_m1c1, schema_replication_init): + """Check supplier schema is again a superset (OC with more MUST), then + schema is pushed and there is no message in the error log + + :id: 39304242-2641-4eb8-a9fb-5ff0cf80718f + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Add telenumber to 'masterNewOCA' on the master + 2. Push the Schema (no error) + 3. Check the schemaCSN was updated on the consumer + 4. Check the error log of the supplier does not contain an error + 5. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. State at startup + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA + Final state + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA + +must=telexnumber + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA + +must=telexnumber + """ + _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error") + + mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, + new_may=MAY_OLD) + + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was updated on the consumer + log.debug("test_schema_replication_four master_schema_csn=%s", master_schema_csn) + log.debug("ctest_schema_replication_four onsumer_schema_csn=%s", consumer_schema_csn) + assert master_schema_csn == consumer_schema_csn + + # Check the error log of the supplier does not contain an error + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + if res is not None: + assert False + + +@pytest.mark.ds47490 +def test_schema_replication_five(topology_m1c1, schema_replication_init): + """Check consumer schema is a superset (OC with more MUST), then + schema is pushed (fix for 47721) and there is a message in the error log + + :id: 498527df-28c8-4e1a-bc9e-799fd2b7b2bb + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Add telenumber to 'consumerNewOCA' on the consumer + 2. Add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer + 3. Push the Schema + 4. Check the schemaCSN was NOT updated on the consumer + 5. Check the error log of the supplier contain an error + 6. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. Operation should be successful + 6. State at startup + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA + +must=telexnumber + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA + +must=telexnumber + Final state + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA + +must=telexnumber +must=telexnumber + + Note: replication log is enabled to get more details + """ + _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - (fix for 47721)") + + # get more detail why it fails + topology_m1c1.ms["master1"].enableReplLogging() + + # add telenumber to 'consumerNewOCA' on the consumer + mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, + new_may=MAY_OLD) + # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s) + time.sleep(2) + add_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC') + + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was NOT updated on the consumer + # with 47721, supplier learns the missing definition + log.debug("test_schema_replication_five master_schema_csn=%s", master_schema_csn) + log.debug("ctest_schema_replication_five onsumer_schema_csn=%s", consumer_schema_csn) + if support_schema_learning(topology_m1c1): + assert master_schema_csn == consumer_schema_csn + else: + assert master_schema_csn != consumer_schema_csn + + # Check the error log of the supplier does not contain an error + # This message may happen during the learning phase + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + + +@pytest.mark.ds47490 +def test_schema_replication_six(topology_m1c1, schema_replication_init): + """Check supplier schema is again a superset (OC with more MUST), then + schema is pushed and there is no message in the error log + + :id: ed57b0cc-6a10-4f89-94ae-9f18542b1954 + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Add telenumber to 'consumerNewOCA' on the master + 2. Push the Schema (no error) + 3. Check the schemaCSN was NOT updated on the consumer + 4. Check the error log of the supplier does not contain an error + 5. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. State at startup + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA + +must=telexnumber +must=telexnumber + Final state + + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + Note: replication log is enabled to get more details + """ + _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error") + + # add telenumber to 'consumerNewOCA' on the consumer + mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, + new_may=MAY_OLD) + + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was NOT updated on the consumer + log.debug("test_schema_replication_six master_schema_csn=%s", master_schema_csn) + log.debug("ctest_schema_replication_six onsumer_schema_csn=%s", consumer_schema_csn) + assert master_schema_csn == consumer_schema_csn + + # Check the error log of the supplier does not contain an error + # This message may happen during the learning phase + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + if res is not None: + assert False + + +@pytest.mark.ds47490 +def test_schema_replication_seven(topology_m1c1, schema_replication_init): + """Check supplier schema is again a superset (OC with more MAY), then + schema is pushed and there is no message in the error log + + :id: 8725055a-b3f8-4d1d-a4d6-bb7dccf644d0 + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Add telenumber to 'masterNewOCA' on the master + 2. Push the Schema (no error) + 3. Check the schemaCSN was updated on the consumer + 4. Check the error log of the supplier does not contain an error + 5. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. State at startup + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + Final stat + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox + """ + _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error") + + mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, + new_may=MAY_NEW) + + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was updated on the consumer + log.debug("test_schema_replication_seven master_schema_csn=%s", master_schema_csn) + log.debug("ctest_schema_replication_seven consumer_schema_csn=%s", consumer_schema_csn) + assert master_schema_csn == consumer_schema_csn + + # Check the error log of the supplier does not contain an error + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + if res is not None: + assert False + + +@pytest.mark.ds47490 +def test_schema_replication_eight(topology_m1c1, schema_replication_init): + """Check consumer schema is a superset (OC with more MAY), then + schema is pushed (fix for 47721) and there is message in the error log + + :id: 2310d150-a71a-498d-add8-4056beeb58c6 + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Add telenumber to 'consumerNewOCA' on the consumer + 2. Modify OC on the supplier so that its nsSchemaCSN is larger than the consumer + 3. Push the Schema (no error) + 4. Check the schemaCSN was updated on the consumer + 5. Check the error log of the supplier does not contain an error + 6. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. Operation should be successful + 6. State at startup + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox + Final state + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox +may=postOfficeBox + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox +may=postOfficeBox + """ + _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed (fix for 47721)") + + mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, + new_may=MAY_NEW) + + # modify OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s) + time.sleep(2) + mod_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC', old_must=MUST_OLD, new_must=MUST_OLD, old_may=MAY_OLD, + new_may=MAY_NEW) + + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was not updated on the consumer + # with 47721, supplier learns the missing definition + log.debug("test_schema_replication_eight master_schema_csn=%s", master_schema_csn) + log.debug("ctest_schema_replication_eight onsumer_schema_csn=%s", consumer_schema_csn) + if support_schema_learning(topology_m1c1): + assert master_schema_csn == consumer_schema_csn + else: + assert master_schema_csn != consumer_schema_csn + + # Check the error log of the supplier does not contain an error + # This message may happen during the learning phase + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + + +@pytest.mark.ds47490 +def test_schema_replication_nine(topology_m1c1, schema_replication_init): + """Check consumer schema is a superset (OC with more MAY), then + schema is not pushed and there is message in the error log + + :id: 851b24c6-b1e0-466f-9714-aa2940fbfeeb + :setup: Master Consumer, check if a warning message is logged in the + error log of the supplier and add a test entry to trigger attempt of schema push. + :steps: + 1. Add postOfficeBox to 'consumerNewOCA' on the master + 3. Push the Schema + 4. Check the schemaCSN was updated on the consumer + 5. Check the error log of the supplier does contain an error + 6. Check the startup/final state + :expectedresults: + 1. Operation should be successful + 2. Operation should be successful + 3. Operation should be successful + 4. Operation should be successful + 5. Operation should be successful + 6. State at startup + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox +may=postOfficeBox + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox +may=postOfficeBox + + Final state + + - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox +may=postOfficeBox +may=postOfficeBox + - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC + +must=telexnumber +must=telexnumber + +may=postOfficeBox +may=postOfficeBox +may=postOfficeBox + """ + _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error") + + mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, + new_may=MAY_NEW) + + trigger_schema_push(topology_m1c1) + master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() + consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() + + # Check the schemaCSN was updated on the consumer + log.debug("test_schema_replication_nine master_schema_csn=%s", master_schema_csn) + log.debug("ctest_schema_replication_nine onsumer_schema_csn=%s", consumer_schema_csn) + assert master_schema_csn == consumer_schema_csn + + # Check the error log of the supplier does not contain an error + regex = re.compile("must not be overwritten \(set replication log for additional info\)") + res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) + if res is not None: + assert False + + log.info('Testcase 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/dirsrvtests/tests/tickets/ticket1347760_test.py b/dirsrvtests/tests/tickets/ticket1347760_test.py deleted file mode 100644 index 4dc2311..0000000 --- a/dirsrvtests/tests/tickets/ticket1347760_test.py +++ /dev/null @@ -1,455 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2016 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# --- END COPYRIGHT BLOCK --- -# -from subprocess import Popen - -import pytest -from lib389.paths import Paths -from lib389.tasks import * -from lib389.utils import * -from lib389.topologies import topology_st - -from lib389._constants import DN_DM, DEFAULT_SUFFIX, PASSWORD, SERVERID_STANDALONE - -logging.getLogger(__name__).setLevel(logging.DEBUG) -log = logging.getLogger(__name__) - -CONFIG_DN = 'cn=config' -BOU = 'BOU' -BINDOU = 'ou=%s,%s' % (BOU, DEFAULT_SUFFIX) -BUID = 'buser123' -TUID = 'tuser0' -BINDDN = 'uid=%s,%s' % (BUID, BINDOU) -BINDPW = BUID -TESTDN = 'uid=%s,ou=people,%s' % (TUID, DEFAULT_SUFFIX) -TESTPW = TUID -BOGUSDN = 'uid=bogus,%s' % DEFAULT_SUFFIX -BOGUSDN2 = 'uid=bogus,ou=people,%s' % DEFAULT_SUFFIX -BOGUSSUFFIX = 'uid=bogus,ou=people,dc=bogus' -GROUPOU = 'ou=groups,%s' % DEFAULT_SUFFIX -BOGUSOU = 'ou=OU,%s' % DEFAULT_SUFFIX - - -def pattern_accesslog(file, log_pattern): - for i in range(5): - try: - pattern_accesslog.last_pos += 1 - except AttributeError: - pattern_accesslog.last_pos = 0 - - found = None - file.seek(pattern_accesslog.last_pos) - - # Use a while true iteration because 'for line in file: hit a - # python bug that break file.tell() - while True: - line = file.readline() - found = log_pattern.search(line) - if ((line == '') or (found)): - break - - pattern_accesslog.last_pos = file.tell() - if found: - return line - else: - time.sleep(1) - return None - - -def check_op_result(server, op, dn, superior, exists, rc): - targetdn = dn - if op == 'search': - if exists: - opstr = 'Searching existing entry' - else: - opstr = 'Searching non-existing entry' - elif op == 'add': - if exists: - opstr = 'Adding existing entry' - else: - opstr = 'Adding non-existing entry' - elif op == 'modify': - if exists: - opstr = 'Modifying existing entry' - else: - opstr = 'Modifying non-existing entry' - elif op == 'modrdn': - if superior is not None: - targetdn = superior - if exists: - opstr = 'Moving to existing superior' - else: - opstr = 'Moving to non-existing superior' - else: - if exists: - opstr = 'Renaming existing entry' - else: - opstr = 'Renaming non-existing entry' - elif op == 'delete': - if exists: - opstr = 'Deleting existing entry' - else: - opstr = 'Deleting non-existing entry' - - if ldap.SUCCESS == rc: - expstr = 'be ok' - else: - expstr = 'fail with %s' % rc.__name__ - - log.info('%s %s, which should %s.' % (opstr, targetdn, expstr)) - time.sleep(1) - hit = 0 - try: - if op == 'search': - centry = server.search_s(dn, ldap.SCOPE_BASE, 'objectclass=*') - elif op == 'add': - server.add_s(Entry((dn, {'objectclass': 'top extensibleObject'.split(), - 'cn': 'test entry'}))) - elif op == 'modify': - server.modify_s(dn, [(ldap.MOD_REPLACE, 'description', 'test')]) - elif op == 'modrdn': - if superior is not None: - server.rename_s(dn, 'uid=new', newsuperior=superior, delold=1) - else: - server.rename_s(dn, 'uid=new', delold=1) - elif op == 'delete': - server.delete_s(dn) - else: - log.fatal('Unknown operation %s' % op) - assert False - except ldap.LDAPError as e: - hit = 1 - log.info("Exception (expected): %s" % type(e).__name__) - log.info('Desc ' + e.message['desc']) - assert isinstance(e, rc) - if 'matched' in e.message: - log.info('Matched is returned: ' + e.message['matched']) - if rc != ldap.NO_SUCH_OBJECT: - assert False - - if ldap.SUCCESS == rc: - if op == 'search': - log.info('Search should return none') - assert len(centry) == 0 - else: - if 0 == hit: - log.info('Expected to fail with %s, but passed' % rc.__name__) - assert False - - log.info('PASSED\n') - - -def test_ticket1347760(topology_st): - """ - Prevent revealing the entry info to whom has no access rights. - """ - log.info('Testing Bug 1347760 - Information disclosure via repeated use of LDAP ADD operation, etc.') - - log.info('Disabling accesslog logbuffering') - topology_st.standalone.modify_s(CONFIG_DN, [(ldap.MOD_REPLACE, 'nsslapd-accesslog-logbuffering', 'off')]) - - log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD)) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - log.info('Adding ou=%s a bind user belongs to.' % BOU) - topology_st.standalone.add_s(Entry((BINDOU, { - 'objectclass': 'top organizationalunit'.split(), - 'ou': BOU}))) - - log.info('Adding a bind user.') - topology_st.standalone.add_s(Entry((BINDDN, - {'objectclass': "top person organizationalPerson inetOrgPerson".split(), - 'cn': 'bind user', - 'sn': 'user', - 'userPassword': BINDPW}))) - - log.info('Adding a test user.') - topology_st.standalone.add_s(Entry((TESTDN, - {'objectclass': "top person organizationalPerson inetOrgPerson".split(), - 'cn': 'test user', - 'sn': 'user', - 'userPassword': TESTPW}))) - - log.info('Deleting aci in %s.' % DEFAULT_SUFFIX) - topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_DELETE, 'aci', None)]) - - log.info('While binding as DM, acquire an access log path and instance dir') - ds_paths = Paths(serverid=topology_st.standalone.serverid, - instance=topology_st.standalone) - file_path = ds_paths.access_log - inst_dir = ds_paths.inst_dir - - log.info('Bind case 1. the bind user has no rights to read the entry itself, bind should be successful.') - log.info('Bind as {%s,%s} who has no access rights.' % (BINDDN, BINDPW)) - try: - topology_st.standalone.simple_bind_s(BINDDN, BINDPW) - except ldap.LDAPError as e: - log.info('Desc ' + e.message['desc']) - assert False - - file_obj = open(file_path, "r") - log.info('Access log path: %s' % file_path) - - log.info( - 'Bind case 2-1. the bind user does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__) - log.info('Bind as {%s,%s} who does not exist.' % (BOGUSDN, 'bogus')) - try: - topology_st.standalone.simple_bind_s(BOGUSDN, 'bogus') - except ldap.LDAPError as e: - log.info("Exception (expected): %s" % type(e).__name__) - log.info('Desc ' + e.message['desc']) - assert isinstance(e, ldap.INVALID_CREDENTIALS) - regex = re.compile('No such entry') - cause = pattern_accesslog(file_obj, regex) - if cause is None: - log.fatal('Cause not found - %s' % cause) - assert False - else: - log.info('Cause found - %s' % cause) - time.sleep(1) - - log.info( - 'Bind case 2-2. the bind user\'s suffix does not exist, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__) - log.info('Bind as {%s,%s} who does not exist.' % (BOGUSSUFFIX, 'bogus')) - with pytest.raises(ldap.INVALID_CREDENTIALS): - topology_st.standalone.simple_bind_s(BOGUSSUFFIX, 'bogus') - regex = re.compile('No suffix for bind') - cause = pattern_accesslog(file_obj, regex) - if cause is None: - log.fatal('Cause not found - %s' % cause) - assert False - else: - log.info('Cause found - %s' % cause) - time.sleep(1) - - log.info( - 'Bind case 2-3. the bind user\'s password is wrong, bind should fail with error %s' % ldap.INVALID_CREDENTIALS.__name__) - log.info('Bind as {%s,%s} who does not exist.' % (BINDDN, 'bogus')) - try: - topology_st.standalone.simple_bind_s(BINDDN, 'bogus') - except ldap.LDAPError as e: - log.info("Exception (expected): %s" % type(e).__name__) - log.info('Desc ' + e.message['desc']) - assert isinstance(e, ldap.INVALID_CREDENTIALS) - regex = re.compile('Invalid credentials') - cause = pattern_accesslog(file_obj, regex) - if cause is None: - log.fatal('Cause not found - %s' % cause) - assert False - else: - log.info('Cause found - %s' % cause) - time.sleep(1) - - log.info('Adding aci for %s to %s.' % (BINDDN, BINDOU)) - acival = '(targetattr="*")(version 3.0; acl "%s"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN) - log.info('aci: %s' % acival) - log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD)) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - topology_st.standalone.modify_s(BINDOU, [(ldap.MOD_ADD, 'aci', acival)]) - time.sleep(1) - - log.info('Bind case 3. the bind user has the right to read the entry itself, bind should be successful.') - log.info('Bind as {%s,%s} which should be ok.\n' % (BINDDN, BINDPW)) - topology_st.standalone.simple_bind_s(BINDDN, BINDPW) - - log.info('The following operations are against the subtree the bind user %s has no rights.' % BINDDN) - # Search - exists = True - rc = ldap.SUCCESS - log.info( - 'Search case 1. the bind user has no rights to read the search entry, it should return no search results with %s' % rc) - check_op_result(topology_st.standalone, 'search', TESTDN, None, exists, rc) - - exists = False - rc = ldap.SUCCESS - log.info( - 'Search case 2-1. the search entry does not exist, the search should return no search results with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'search', BOGUSDN, None, exists, rc) - - exists = False - rc = ldap.SUCCESS - log.info( - 'Search case 2-2. the search entry does not exist, the search should return no search results with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc) - - # Add - exists = True - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Add case 1. the bind user has no rights AND the adding entry exists, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Add case 2-1. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'add', BOGUSDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Add case 2-2. the bind user has no rights AND the adding entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'add', BOGUSDN2, None, exists, rc) - - # Modify - exists = True - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modify case 1. the bind user has no rights AND the modifying entry exists, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modify', TESTDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modify case 2-1. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modify case 2-2. the bind user has no rights AND the modifying entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modify', BOGUSDN2, None, exists, rc) - - # Modrdn - exists = True - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modrdn case 1. the bind user has no rights AND the renaming entry exists, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', TESTDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modrdn case 2-1. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modrdn case 2-2. the bind user has no rights AND the renaming entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', BOGUSDN2, None, exists, rc) - - exists = True - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modrdn case 3. the bind user has no rights AND the node moving an entry to exists, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', TESTDN, GROUPOU, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modrdn case 4-1. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Modrdn case 4-2. the bind user has no rights AND the node moving an entry to does not, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc) - - # Delete - exists = True - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Delete case 1. the bind user has no rights AND the deleting entry exists, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'delete', TESTDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Delete case 2-1. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc) - - exists = False - rc = ldap.INSUFFICIENT_ACCESS - log.info( - 'Delete case 2-2. the bind user has no rights AND the deleting entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'delete', BOGUSDN2, None, exists, rc) - - log.info('EXTRA: Check no regressions') - log.info('Adding aci for %s to %s.' % (BINDDN, DEFAULT_SUFFIX)) - acival = '(targetattr="*")(version 3.0; acl "%s-all"; allow(all) userdn = "ldap:///%s";)' % (BUID, BINDDN) - log.info('Bind as {%s,%s}' % (DN_DM, PASSWORD)) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - topology_st.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_ADD, 'aci', acival)]) - time.sleep(1) - - log.info('Bind as {%s,%s}.' % (BINDDN, BINDPW)) - try: - topology_st.standalone.simple_bind_s(BINDDN, BINDPW) - except ldap.LDAPError as e: - log.info('Desc ' + e.message['desc']) - assert False - time.sleep(1) - - exists = False - rc = ldap.NO_SUCH_OBJECT - log.info('Search case. the search entry does not exist, the search should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'search', BOGUSDN2, None, exists, rc) - file_obj.close() - - exists = True - rc = ldap.ALREADY_EXISTS - log.info('Add case. the adding entry already exists, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'add', TESTDN, None, exists, rc) - - exists = False - rc = ldap.NO_SUCH_OBJECT - log.info('Modify case. the modifying entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modify', BOGUSDN, None, exists, rc) - - exists = False - rc = ldap.NO_SUCH_OBJECT - log.info('Modrdn case 1. the renaming entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', BOGUSDN, None, exists, rc) - - exists = False - rc = ldap.NO_SUCH_OBJECT - log.info('Modrdn case 2. the node moving an entry to does not, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'modrdn', TESTDN, BOGUSOU, exists, rc) - - exists = False - rc = ldap.NO_SUCH_OBJECT - log.info('Delete case. the deleting entry does not exist, it should fail with %s' % rc.__name__) - check_op_result(topology_st.standalone, 'delete', BOGUSDN, None, exists, rc) - - log.info('Inactivate %s' % BINDDN) - if ds_paths.version < '1.3': - nsinactivate = '%s/ns-inactivate.pl' % inst_dir - nsinactivate_cmd = [nsinactivate, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN] - else: - nsinactivate = '%s/ns-inactivate.pl' % ds_paths.sbin_dir - nsinactivate_cmd = [nsinactivate, '-Z', SERVERID_STANDALONE, '-D', DN_DM, '-w', PASSWORD, '-I', BINDDN] - log.info(nsinactivate_cmd) - p = Popen(nsinactivate_cmd) - assert (p.wait() == 0) - - log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, BUID, ldap.UNWILLING_TO_PERFORM.__name__)) - try: - topology_st.standalone.simple_bind_s(BINDDN, BUID) - except ldap.LDAPError as e: - log.info("Exception (expected): %s" % type(e).__name__) - log.info('Desc ' + e.message['desc']) - assert isinstance(e, ldap.UNWILLING_TO_PERFORM) - - log.info('Bind as {%s,%s} which should fail with %s.' % (BINDDN, 'bogus', ldap.UNWILLING_TO_PERFORM.__name__)) - try: - topology_st.standalone.simple_bind_s(BINDDN, 'bogus') - except ldap.LDAPError as e: - log.info("Exception (expected): %s" % type(e).__name__) - log.info('Desc ' + e.message['desc']) - assert isinstance(e, ldap.UNWILLING_TO_PERFORM) - - log.info('SUCCESS') - - -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/tickets/ticket47431_test.py b/dirsrvtests/tests/tickets/ticket47431_test.py deleted file mode 100644 index d45346b..0000000 --- a/dirsrvtests/tests/tickets/ticket47431_test.py +++ /dev/null @@ -1,208 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2016 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.tasks import * -from lib389.utils import * -from lib389.topologies import topology_st - -from lib389._constants import DEFAULT_SUFFIX, DN_PLUGIN, SUFFIX, PLUGIN_7_BIT_CHECK - -# Skip on older versions -pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented") - -logging.getLogger(__name__).setLevel(logging.DEBUG) -log = logging.getLogger(__name__) - -DN_7BITPLUGIN = "cn=7-bit check,%s" % DN_PLUGIN -ATTRS = ["uid", "mail", "userpassword", ",", SUFFIX, None] - - -def test_ticket47431_0(topology_st): - ''' - Enable 7 bit plugin - ''' - log.info("Ticket 47431 - 0: Enable 7bit plugin...") - topology_st.standalone.plugins.enable(name=PLUGIN_7_BIT_CHECK) - -def test_ticket47431_1(topology_st): - ''' - nsslapd-pluginarg0: uid - nsslapd-pluginarg1: mail - nsslapd-pluginarg2: userpassword <== repeat 27 times - nsslapd-pluginarg3: , - nsslapd-pluginarg4: dc=example,dc=com - - The duplicated values are removed by str2entry_dupcheck as follows: - [..] - str2entry_dupcheck: 27 duplicate values for attribute type nsslapd-pluginarg2 - detected in entry cn=7-bit check,cn=plugins,cn=config. Extra values ignored. - ''' - - log.info("Ticket 47431 - 1: Check 26 duplicate values are treated as one...") - expected = "str2entry_dupcheck.* duplicate values for attribute type nsslapd-pluginarg2 detected in entry cn=7-bit check,cn=plugins,cn=config." - - log.debug('modify_s %s' % DN_7BITPLUGIN) - try: - topology_st.standalone.modify_s(DN_7BITPLUGIN, - [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', "uid"), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', "mail"), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', "userpassword"), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', ","), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg4', SUFFIX)]) - except ValueError: - log.error('modify failed: Some problem occured with a value that was provided') - assert False - - arg2 = "nsslapd-pluginarg2: userpassword" - topology_st.standalone.stop() - dse_ldif = topology_st.standalone.confdir + '/dse.ldif' - os.system('mv %s %s.47431' % (dse_ldif, dse_ldif)) - os.system( - 'sed -e "s/\\(%s\\)/\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1\\n\\1/" %s.47431 > %s' % ( - arg2, dse_ldif, dse_ldif)) - topology_st.standalone.start() - - cmdline = 'egrep -i "%s" %s' % (expected, topology_st.standalone.errlog) - p = os.popen(cmdline, "r") - line = p.readline() - if line == "": - log.error('Expected error "%s" not logged in %s' % (expected, topology_st.standalone.errlog)) - assert False - else: - log.debug('line: %s' % line) - log.info('Expected error "%s" logged in %s' % (expected, topology_st.standalone.errlog)) - - log.info("Ticket 47431 - 1: done") - - -def test_ticket47431_2(topology_st): - ''' - nsslapd-pluginarg0: uid - nsslapd-pluginarg0: mail - nsslapd-pluginarg1: userpassword - nsslapd-pluginarg2: , - nsslapd-pluginarg3: dc=example,dc=com - ==> - nsslapd-pluginarg0: uid - nsslapd-pluginarg1: mail - nsslapd-pluginarg2: userpassword - nsslapd-pluginarg3: , - nsslapd-pluginarg4: dc=example,dc=com - Should be logged in error log: - [..] NS7bitAttr_Init - 0: uid - [..] NS7bitAttr_Init - 1: userpassword - [..] NS7bitAttr_Init - 2: mail - [..] NS7bitAttr_Init - 3: , - [..] NS7bitAttr_Init - 4: dc=example,dc=com - ''' - - log.info("Ticket 47431 - 2: Check two values belonging to one arg is fixed...") - - try: - topology_st.standalone.modify_s(DN_7BITPLUGIN, - [(ldap.MOD_REPLACE, 'nsslapd-pluginarg0', "uid"), - (ldap.MOD_ADD, 'nsslapd-pluginarg0', "mail"), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', "userpassword"), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg2', ","), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', SUFFIX), - (ldap.MOD_DELETE, 'nsslapd-pluginarg4', None)]) - except ValueError: - log.error('modify failed: Some problem occured with a value that was provided') - assert False - - # PLUGIN LOG LEVEL - topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '65536')]) - - topology_st.standalone.restart() - - cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog) - p = os.popen(cmdline, "r") - i = 0 - while ATTRS[i]: - line = p.readline() - log.debug('line - %s' % line) - log.debug('ATTRS[%d] %s' % (i, ATTRS[i])) - if line == "": - break - elif line.find(ATTRS[i]) >= 0: - log.debug('%s was logged' % ATTRS[i]) - else: - log.error('%s was not logged.' % ATTRS[i]) - assert False - i = i + 1 - - log.info("Ticket 47431 - 2: done") - - -def test_ticket47431_3(topology_st): - ''' - nsslapd-pluginarg1: uid - nsslapd-pluginarg3: mail - nsslapd-pluginarg5: userpassword - nsslapd-pluginarg7: , - nsslapd-pluginarg9: dc=example,dc=com - ==> - nsslapd-pluginarg0: uid - nsslapd-pluginarg1: mail - nsslapd-pluginarg2: userpassword - nsslapd-pluginarg3: , - nsslapd-pluginarg4: dc=example,dc=com - Should be logged in error log: - [..] NS7bitAttr_Init - 0: uid - [..] NS7bitAttr_Init - 1: userpassword - [..] NS7bitAttr_Init - 2: mail - [..] NS7bitAttr_Init - 3: , - [..] NS7bitAttr_Init - 4: dc=example,dc=com - ''' - - log.info("Ticket 47431 - 3: Check missing args are fixed...") - - try: - topology_st.standalone.modify_s(DN_7BITPLUGIN, - [(ldap.MOD_DELETE, 'nsslapd-pluginarg0', None), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg1', "uid"), - (ldap.MOD_DELETE, 'nsslapd-pluginarg2', None), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg3', "mail"), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg5', "userpassword"), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg7', ","), - (ldap.MOD_REPLACE, 'nsslapd-pluginarg9', SUFFIX)]) - except ValueError: - log.error('modify failed: Some problem occured with a value that was provided') - assert False - - # PLUGIN LOG LEVEL - topology_st.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '65536')]) - - topology_st.standalone.stop() - os.system('mv %s %s.47431' % (topology_st.standalone.errlog, topology_st.standalone.errlog)) - os.system('touch %s' % (topology_st.standalone.errlog)) - topology_st.standalone.start() - - cmdline = 'egrep -i %s %s' % ("NS7bitAttr_Init", topology_st.standalone.errlog) - p = os.popen(cmdline, "r") - i = 0 - while ATTRS[i]: - line = p.readline() - if line == "": - break - elif line.find(ATTRS[i]) >= 0: - log.debug('%s was logged' % ATTRS[i]) - else: - log.error('%s was not logged.' % ATTRS[i]) - assert False - i = i + 1 - - log.info("Ticket 47431 - 3: done") - log.info('Test complete') - - -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/tickets/ticket47490_test.py b/dirsrvtests/tests/tickets/ticket47490_test.py deleted file mode 100644 index df57aab..0000000 --- a/dirsrvtests/tests/tickets/ticket47490_test.py +++ /dev/null @@ -1,582 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2016 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# --- END COPYRIGHT BLOCK --- -# -''' -Created on Nov 7, 2013 - -@author: tbordaz -''' -import logging -import re -import time - -import ldap -import pytest -from lib389 import Entry -from lib389._constants import * -from lib389.topologies import topology_m1c1 - -from lib389.utils import * - -# Skip on older versions -pytestmark = pytest.mark.skipif(ds_is_older('1.3'), reason="Not implemented") -logging.getLogger(__name__).setLevel(logging.DEBUG) -log = logging.getLogger(__name__) - -TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX -ENTRY_DN = "cn=test_entry, %s" % SUFFIX -MUST_OLD = "(postalAddress $ preferredLocale)" -MUST_NEW = "(postalAddress $ preferredLocale $ telexNumber)" -MAY_OLD = "(postalCode $ street)" -MAY_NEW = "(postalCode $ street $ postOfficeBox)" - - -def _header(topology_m1c1, label): - topology_m1c1.ms["master1"].log.info("\n\n###############################################") - topology_m1c1.ms["master1"].log.info("#######") - topology_m1c1.ms["master1"].log.info("####### %s" % label) - topology_m1c1.ms["master1"].log.info("#######") - topology_m1c1.ms["master1"].log.info("###################################################") - - -def pattern_errorlog(file, log_pattern): - try: - pattern_errorlog.last_pos += 1 - except AttributeError: - pattern_errorlog.last_pos = 0 - - found = None - log.debug("_pattern_errorlog: start at offset %d" % pattern_errorlog.last_pos) - file.seek(pattern_errorlog.last_pos) - - # Use a while true iteration because 'for line in file: hit a - # python bug that break file.tell() - while True: - line = file.readline() - log.debug("_pattern_errorlog: [%d] %s" % (file.tell(), line)) - found = log_pattern.search(line) - if ((line == '') or (found)): - break - - log.debug("_pattern_errorlog: end at offset %d" % file.tell()) - pattern_errorlog.last_pos = file.tell() - return found - - -def _oc_definition(oid_ext, name, must=None, may=None): - oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext - desc = 'To test ticket 47490' - sup = 'person' - if not must: - must = MUST_OLD - if not may: - may = MAY_OLD - - new_oc = "( %s NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may) - return new_oc - - -def add_OC(instance, oid_ext, name): - new_oc = _oc_definition(oid_ext, name) - instance.schema.add_schema('objectClasses', new_oc) - - -def mod_OC(instance, oid_ext, name, old_must=None, old_may=None, new_must=None, new_may=None): - old_oc = _oc_definition(oid_ext, name, old_must, old_may) - new_oc = _oc_definition(oid_ext, name, new_must, new_may) - instance.schema.del_schema('objectClasses', old_oc) - instance.schema.add_schema('objectClasses', new_oc) - - -def support_schema_learning(topology_m1c1): - """ - with https://fedorahosted.org/389/ticket/47721, the supplier and consumer can learn - schema definitions when a replication occurs. - Before that ticket: replication of the schema fails requiring administrative operation - In the test the schemaCSN (master consumer) differs - - After that ticket: replication of the schema succeeds (after an initial phase of learning) - In the test the schema CSN (master consumer) are in sync - - This function returns True if 47721 is fixed in the current release - False else - """ - ent = topology_m1c1.cs["consumer1"].getEntry(DN_CONFIG, ldap.SCOPE_BASE, "(cn=config)", ['nsslapd-versionstring']) - if ent.hasAttr('nsslapd-versionstring'): - val = ent.getValue('nsslapd-versionstring') - version = val.split('/')[1].split('.') # something like ['1', '3', '1', '23', 'final_fix'] - major = int(version[0]) - minor = int(version[1]) - if major > 1: - return True - if minor > 3: - # version is 1.4 or after - return True - if minor == 3: - if version[2].isdigit(): - if int(version[2]) >= 3: - return True - return False - - -def trigger_update(topology_m1c1): - """ - It triggers an update on the supplier. This will start a replication - session and a schema push - """ - try: - trigger_update.value += 1 - except AttributeError: - trigger_update.value = 1 - replace = [(ldap.MOD_REPLACE, 'telephonenumber', str(trigger_update.value))] - topology_m1c1.ms["master1"].modify_s(ENTRY_DN, replace) - - # wait 10 seconds that the update is replicated - loop = 0 - while loop <= 10: - try: - ent = topology_m1c1.cs["consumer1"].getEntry(ENTRY_DN, ldap.SCOPE_BASE, "(objectclass=*)", - ['telephonenumber']) - val = ent.telephonenumber or "0" - if int(val) == trigger_update.value: - return - # the expected value is not yet replicated. try again - time.sleep(1) - loop += 1 - log.debug("trigger_update: receive %s (expected %d)" % (val, trigger_update.value)) - except ldap.NO_SUCH_OBJECT: - time.sleep(1) - loop += 1 - - -def trigger_schema_push(topology_m1c1): - ''' - Trigger update to create a replication session. - In case of 47721 is fixed and the replica needs to learn the missing definition, then - the first replication session learn the definition and the second replication session - push the schema (and the schemaCSN. - This is why there is two updates and replica agreement is stopped/start (to create a second session) - ''' - agreements = topology_m1c1.ms["master1"].agreement.list(suffix=SUFFIX, - consumer_host=topology_m1c1.cs["consumer1"].host, - consumer_port=topology_m1c1.cs["consumer1"].port) - assert (len(agreements) == 1) - ra = agreements[0] - trigger_update(topology_m1c1) - topology_m1c1.ms["master1"].agreement.pause(ra.dn) - topology_m1c1.ms["master1"].agreement.resume(ra.dn) - trigger_update(topology_m1c1) - - -def test_ticket47490_init(topology_m1c1): - """ - Initialize the test environment - """ - log.debug("test_ticket47490_init topology_m1c1 %r (master %r, consumer %r" % ( - topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"])) - # the test case will check if a warning message is logged in the - # error log of the supplier - topology_m1c1.ms["master1"].errorlog_file = open(topology_m1c1.ms["master1"].errlog, "r") - - # This entry will be used to trigger attempt of schema push - topology_m1c1.ms["master1"].add_s(Entry((ENTRY_DN, { - 'objectclass': "top person".split(), - 'sn': 'test_entry', - 'cn': 'test_entry'}))) - - -def test_ticket47490_one(topology_m1c1): - """ - Summary: Extra OC Schema is pushed - no error - - If supplier schema is a superset (one extra OC) of consumer schema, then - schema is pushed and there is no message in the error log - State at startup: - - supplier default schema - - consumer default schema - Final state - - supplier +masterNewOCA - - consumer +masterNewOCA - - """ - _header(topology_m1c1, "Extra OC Schema is pushed - no error") - - log.debug("test_ticket47490_one topology_m1c1 %r (master %r, consumer %r" % ( - topology_m1c1, topology_m1c1.ms["master1"], topology_m1c1.cs["consumer1"])) - # update the schema of the supplier so that it is a superset of - # consumer. Schema should be pushed - add_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA') - - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was updated on the consumer - log.debug("test_ticket47490_one master_schema_csn=%s", master_schema_csn) - log.debug("ctest_ticket47490_one onsumer_schema_csn=%s", consumer_schema_csn) - assert master_schema_csn == consumer_schema_csn - - # Check the error log of the supplier does not contain an error - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - if res is not None: - assert False - - -def test_ticket47490_two(topology_m1c1): - """ - Summary: Extra OC Schema is pushed - (ticket 47721 allows to learn missing def) - - If consumer schema is a superset (one extra OC) of supplier schema, then - schema is pushed and there is a message in the error log - State at startup - - supplier +masterNewOCA - - consumer +masterNewOCA - Final state - - supplier +masterNewOCA +masterNewOCB - - consumer +masterNewOCA +consumerNewOCA - - """ - - _header(topology_m1c1, "Extra OC Schema is pushed - (ticket 47721 allows to learn missing def)") - - # add this OC on consumer. Supplier will no push the schema - add_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA') - - # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s) - time.sleep(2) - add_OC(topology_m1c1.ms["master1"], 3, 'masterNewOCB') - - # now push the scheam - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was NOT updated on the consumer - # with 47721, supplier learns the missing definition - log.debug("test_ticket47490_two master_schema_csn=%s", master_schema_csn) - log.debug("test_ticket47490_two consumer_schema_csn=%s", consumer_schema_csn) - if support_schema_learning(topology_m1c1): - assert master_schema_csn == consumer_schema_csn - else: - assert master_schema_csn != consumer_schema_csn - - # Check the error log of the supplier does not contain an error - # This message may happen during the learning phase - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - - -def test_ticket47490_three(topology_m1c1): - """ - Summary: Extra OC Schema is pushed - no error - - If supplier schema is again a superset (one extra OC), then - schema is pushed and there is no message in the error log - State at startup - - supplier +masterNewOCA +masterNewOCB - - consumer +masterNewOCA +consumerNewOCA - Final state - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA - - """ - _header(topology_m1c1, "Extra OC Schema is pushed - no error") - - # Do an upate to trigger the schema push attempt - # add this OC on consumer. Supplier will no push the schema - add_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA') - - # now push the scheam - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was NOT updated on the consumer - log.debug("test_ticket47490_three master_schema_csn=%s", master_schema_csn) - log.debug("test_ticket47490_three consumer_schema_csn=%s", consumer_schema_csn) - assert master_schema_csn == consumer_schema_csn - - # Check the error log of the supplier does not contain an error - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - if res is not None: - assert False - - -def test_ticket47490_four(topology_m1c1): - """ - Summary: Same OC - extra MUST: Schema is pushed - no error - - If supplier schema is again a superset (OC with more MUST), then - schema is pushed and there is no message in the error log - State at startup - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA - Final state - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA - +must=telexnumber - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA - +must=telexnumber - - """ - _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error") - - mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, - new_may=MAY_OLD) - - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was updated on the consumer - log.debug("test_ticket47490_four master_schema_csn=%s", master_schema_csn) - log.debug("ctest_ticket47490_four onsumer_schema_csn=%s", consumer_schema_csn) - assert master_schema_csn == consumer_schema_csn - - # Check the error log of the supplier does not contain an error - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - if res is not None: - assert False - - -def test_ticket47490_five(topology_m1c1): - """ - Summary: Same OC - extra MUST: Schema is pushed - (fix for 47721) - - If consumer schema is a superset (OC with more MUST), then - schema is pushed (fix for 47721) and there is a message in the error log - State at startup - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA - +must=telexnumber - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA - +must=telexnumber - Final state - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA - +must=telexnumber +must=telexnumber - - Note: replication log is enabled to get more details - """ - _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - (fix for 47721)") - - # get more detail why it fails - topology_m1c1.ms["master1"].enableReplLogging() - - # add telenumber to 'consumerNewOCA' on the consumer - mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, - new_may=MAY_OLD) - # add a new OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s) - time.sleep(2) - add_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC') - - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was NOT updated on the consumer - # with 47721, supplier learns the missing definition - log.debug("test_ticket47490_five master_schema_csn=%s", master_schema_csn) - log.debug("ctest_ticket47490_five onsumer_schema_csn=%s", consumer_schema_csn) - if support_schema_learning(topology_m1c1): - assert master_schema_csn == consumer_schema_csn - else: - assert master_schema_csn != consumer_schema_csn - - # Check the error log of the supplier does not contain an error - # This message may happen during the learning phase - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - - -def test_ticket47490_six(topology_m1c1): - """ - Summary: Same OC - extra MUST: Schema is pushed - no error - - If supplier schema is again a superset (OC with more MUST), then - schema is pushed and there is no message in the error log - State at startup - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA - +must=telexnumber +must=telexnumber - Final state - - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - - Note: replication log is enabled to get more details - """ - _header(topology_m1c1, "Same OC - extra MUST: Schema is pushed - no error") - - # add telenumber to 'consumerNewOCA' on the consumer - mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_OLD, new_must=MUST_NEW, old_may=MAY_OLD, - new_may=MAY_OLD) - - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was NOT updated on the consumer - log.debug("test_ticket47490_six master_schema_csn=%s", master_schema_csn) - log.debug("ctest_ticket47490_six onsumer_schema_csn=%s", consumer_schema_csn) - assert master_schema_csn == consumer_schema_csn - - # Check the error log of the supplier does not contain an error - # This message may happen during the learning phase - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - if res is not None: - assert False - - -def test_ticket47490_seven(topology_m1c1): - """ - Summary: Same OC - extra MAY: Schema is pushed - no error - - If supplier schema is again a superset (OC with more MAY), then - schema is pushed and there is no message in the error log - State at startup - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - Final stat - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox - """ - _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error") - - mod_OC(topology_m1c1.ms["master1"], 2, 'masterNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, - new_may=MAY_NEW) - - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was updated on the consumer - log.debug("test_ticket47490_seven master_schema_csn=%s", master_schema_csn) - log.debug("ctest_ticket47490_seven consumer_schema_csn=%s", consumer_schema_csn) - assert master_schema_csn == consumer_schema_csn - - # Check the error log of the supplier does not contain an error - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - if res is not None: - assert False - - -def test_ticket47490_eight(topology_m1c1): - """ - Summary: Same OC - extra MAY: Schema is pushed (fix for 47721) - - If consumer schema is a superset (OC with more MAY), then - schema is pushed (fix for 47721) and there is message in the error log - State at startup - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox - Final state - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox +may=postOfficeBox - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox +may=postOfficeBox - """ - _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed (fix for 47721)") - - mod_OC(topology_m1c1.cs["consumer1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, - new_may=MAY_NEW) - - # modify OC on the supplier so that its nsSchemaCSN is larger than the consumer (wait 2s) - time.sleep(2) - mod_OC(topology_m1c1.ms["master1"], 4, 'masterNewOCC', old_must=MUST_OLD, new_must=MUST_OLD, old_may=MAY_OLD, - new_may=MAY_NEW) - - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was not updated on the consumer - # with 47721, supplier learns the missing definition - log.debug("test_ticket47490_eight master_schema_csn=%s", master_schema_csn) - log.debug("ctest_ticket47490_eight onsumer_schema_csn=%s", consumer_schema_csn) - if support_schema_learning(topology_m1c1): - assert master_schema_csn == consumer_schema_csn - else: - assert master_schema_csn != consumer_schema_csn - - # Check the error log of the supplier does not contain an error - # This message may happen during the learning phase - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - - -def test_ticket47490_nine(topology_m1c1): - """ - Summary: Same OC - extra MAY: Schema is pushed - no error - - If consumer schema is a superset (OC with more MAY), then - schema is not pushed and there is message in the error log - State at startup - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox +may=postOfficeBox - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox +may=postOfficeBox - - Final state - - - supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox +may=postOfficeBox +may=postOfficeBox - - consumer +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC - +must=telexnumber +must=telexnumber - +may=postOfficeBox +may=postOfficeBox +may=postOfficeBox - """ - _header(topology_m1c1, "Same OC - extra MAY: Schema is pushed - no error") - - mod_OC(topology_m1c1.ms["master1"], 1, 'consumerNewOCA', old_must=MUST_NEW, new_must=MUST_NEW, old_may=MAY_OLD, - new_may=MAY_NEW) - - trigger_schema_push(topology_m1c1) - master_schema_csn = topology_m1c1.ms["master1"].schema.get_schema_csn() - consumer_schema_csn = topology_m1c1.cs["consumer1"].schema.get_schema_csn() - - # Check the schemaCSN was updated on the consumer - log.debug("test_ticket47490_nine master_schema_csn=%s", master_schema_csn) - log.debug("ctest_ticket47490_nine onsumer_schema_csn=%s", consumer_schema_csn) - assert master_schema_csn == consumer_schema_csn - - # Check the error log of the supplier does not contain an error - regex = re.compile("must not be overwritten \(set replication log for additional info\)") - res = pattern_errorlog(topology_m1c1.ms["master1"].errorlog_file, regex) - if res is not None: - assert False - - log.info('Testcase 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/dirsrvtests/tests/tickets/ticket47653_test.py b/dirsrvtests/tests/tickets/ticket47653_test.py deleted file mode 100644 index 241b4d0..0000000 --- a/dirsrvtests/tests/tickets/ticket47653_test.py +++ /dev/null @@ -1,311 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2016 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# --- END COPYRIGHT BLOCK --- -# -import logging - -import ldap -import pytest -from lib389 import Entry -from lib389._constants import * -from lib389.topologies import topology_st - -log = logging.getLogger(__name__) - -from lib389.utils import * - -# Skip on older versions -pytestmark = pytest.mark.skipif(ds_is_older('1.3.2'), reason="Not implemented") -OC_NAME = 'OCticket47653' -MUST = "(postalAddress $ postalCode)" -MAY = "(member $ street)" - -OTHER_NAME = 'other_entry' -MAX_OTHERS = 10 - -BIND_NAME = 'bind_entry' -BIND_DN = 'cn=%s, %s' % (BIND_NAME, SUFFIX) -BIND_PW = 'password' - -ENTRY_NAME = 'test_entry' -ENTRY_DN = 'cn=%s, %s' % (ENTRY_NAME, SUFFIX) -ENTRY_OC = "top person %s" % OC_NAME - - -def _oc_definition(oid_ext, name, must=None, may=None): - oid = "1.2.3.4.5.6.7.8.9.10.%d" % oid_ext - desc = 'To test ticket 47490' - sup = 'person' - if not must: - must = MUST - if not may: - may = MAY - - new_oc = "( %s NAME '%s' DESC '%s' SUP %s AUXILIARY MUST %s MAY %s )" % (oid, name, desc, sup, must, may) - return new_oc - - -def test_ticket47653_init(topology_st): - """ - It adds - - Objectclass with MAY 'member' - - an entry ('bind_entry') with which we bind to test the 'SELFDN' operation - It deletes the anonymous aci - - """ - - topology_st.standalone.log.info("Add %s that allows 'member' attribute" % OC_NAME) - new_oc = _oc_definition(2, OC_NAME, must=MUST, may=MAY) - topology_st.standalone.schema.add_schema('objectClasses', new_oc) - - # entry used to bind with - topology_st.standalone.log.info("Add %s" % BIND_DN) - topology_st.standalone.add_s(Entry((BIND_DN, { - 'objectclass': "top person".split(), - 'sn': BIND_NAME, - 'cn': BIND_NAME, - 'userpassword': BIND_PW}))) - - # enable acl error logging - mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '128')] - topology_st.standalone.modify_s(DN_CONFIG, mod) - - # Remove aci's to start with a clean slate - mod = [(ldap.MOD_DELETE, 'aci', None)] - topology_st.standalone.modify_s(SUFFIX, mod) - - # add dummy entries - for cpt in range(MAX_OTHERS): - name = "%s%d" % (OTHER_NAME, cpt) - topology_st.standalone.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), { - 'objectclass': "top person".split(), - 'sn': name, - 'cn': name}))) - - -def test_ticket47653_add(topology_st): - ''' - It checks that, bound as bind_entry, - - we can not ADD an entry without the proper SELFDN aci. - - with the proper ACI we can not ADD with 'member' attribute - - with the proper ACI and 'member' it succeeds to ADD - ''' - topology_st.standalone.log.info("\n\n######################### ADD ######################\n") - - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - # Prepare the entry with multivalued members - entry_with_members = Entry(ENTRY_DN) - entry_with_members.setValues('objectclass', 'top', 'person', 'OCticket47653') - entry_with_members.setValues('sn', ENTRY_NAME) - entry_with_members.setValues('cn', ENTRY_NAME) - entry_with_members.setValues('postalAddress', 'here') - entry_with_members.setValues('postalCode', '1234') - members = [] - for cpt in range(MAX_OTHERS): - name = "%s%d" % (OTHER_NAME, cpt) - members.append("cn=%s,%s" % (name, SUFFIX)) - members.append(BIND_DN) - entry_with_members.setValues('member', members) - - # Prepare the entry with one member - entry_with_member = Entry(ENTRY_DN) - entry_with_member.setValues('objectclass', 'top', 'person', 'OCticket47653') - entry_with_member.setValues('sn', ENTRY_NAME) - entry_with_member.setValues('cn', ENTRY_NAME) - entry_with_member.setValues('postalAddress', 'here') - entry_with_member.setValues('postalCode', '1234') - member = [] - member.append(BIND_DN) - entry_with_member.setValues('member', member) - - # entry to add WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS - try: - topology_st.standalone.log.info("Try to add Add %s (aci is missing): %r" % (ENTRY_DN, entry_with_member)) - - topology_st.standalone.add_s(entry_with_member) - except Exception as e: - topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) - assert isinstance(e, ldap.INSUFFICIENT_ACCESS) - - # Ok Now add the proper ACI - topology_st.standalone.log.info("Bind as %s and add the ADD SELFDN aci" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX - ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME - ACI_ALLOW = "(version 3.0; acl \"SelfDN add\"; allow (add)" - ACI_SUBJECT = " userattr = \"member#selfDN\";)" - ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT - mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] - topology_st.standalone.modify_s(SUFFIX, mod) - - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - # entry to add WITHOUT member and WITH the ACI -> ldap.INSUFFICIENT_ACCESS - try: - topology_st.standalone.log.info("Try to add Add %s (member is missing)" % ENTRY_DN) - topology_st.standalone.add_s(Entry((ENTRY_DN, { - 'objectclass': ENTRY_OC.split(), - 'sn': ENTRY_NAME, - 'cn': ENTRY_NAME, - 'postalAddress': 'here', - 'postalCode': '1234'}))) - except Exception as e: - topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) - assert isinstance(e, ldap.INSUFFICIENT_ACCESS) - - # entry to add WITH memberS and WITH the ACI -> ldap.INSUFFICIENT_ACCESS - # member should contain only one value - try: - topology_st.standalone.log.info("Try to add Add %s (with several member values)" % ENTRY_DN) - topology_st.standalone.add_s(entry_with_members) - except Exception as e: - topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) - assert isinstance(e, ldap.INSUFFICIENT_ACCESS) - - topology_st.standalone.log.info("Try to add Add %s should be successful" % ENTRY_DN) - topology_st.standalone.add_s(entry_with_member) - - -def test_ticket47653_search(topology_st): - ''' - It checks that, bound as bind_entry, - - we can not search an entry without the proper SELFDN aci. - - adding the ACI, we can search the entry - ''' - topology_st.standalone.log.info("\n\n######################### SEARCH ######################\n") - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - # entry to search WITH member being BIND_DN but WITHOUT the ACI -> no entry returned - topology_st.standalone.log.info("Try to search %s (aci is missing)" % ENTRY_DN) - ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*') - assert len(ents) == 0 - - # Ok Now add the proper ACI - topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX - ACI_TARGETATTR = "(targetattr = *)" - ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME - ACI_ALLOW = "(version 3.0; acl \"SelfDN search-read\"; allow (read, search, compare)" - ACI_SUBJECT = " userattr = \"member#selfDN\";)" - ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT - mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] - topology_st.standalone.modify_s(SUFFIX, mod) - - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - # entry to search with the proper aci - topology_st.standalone.log.info("Try to search %s should be successful" % ENTRY_DN) - ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*') - assert len(ents) == 1 - - -def test_ticket47653_modify(topology_st): - ''' - It checks that, bound as bind_entry, - - we can not modify an entry without the proper SELFDN aci. - - adding the ACI, we can modify the entry - ''' - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - topology_st.standalone.log.info("\n\n######################### MODIFY ######################\n") - - # entry to modify WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS - try: - topology_st.standalone.log.info("Try to modify %s (aci is missing)" % ENTRY_DN) - mod = [(ldap.MOD_REPLACE, 'postalCode', '9876')] - topology_st.standalone.modify_s(ENTRY_DN, mod) - except Exception as e: - topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) - assert isinstance(e, ldap.INSUFFICIENT_ACCESS) - - # Ok Now add the proper ACI - topology_st.standalone.log.info("Bind as %s and add the WRITE SELFDN aci" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX - ACI_TARGETATTR = "(targetattr = *)" - ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME - ACI_ALLOW = "(version 3.0; acl \"SelfDN write\"; allow (write)" - ACI_SUBJECT = " userattr = \"member#selfDN\";)" - ACI_BODY = ACI_TARGET + ACI_TARGETATTR + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT - mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] - topology_st.standalone.modify_s(SUFFIX, mod) - - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - # modify the entry and checks the value - topology_st.standalone.log.info("Try to modify %s. It should succeeds" % ENTRY_DN) - mod = [(ldap.MOD_REPLACE, 'postalCode', '1928')] - topology_st.standalone.modify_s(ENTRY_DN, mod) - - ents = topology_st.standalone.search_s(ENTRY_DN, ldap.SCOPE_BASE, 'objectclass=*') - assert len(ents) == 1 - assert ents[0].postalCode == '1928' - - -def test_ticket47653_delete(topology_st): - ''' - It checks that, bound as bind_entry, - - we can not delete an entry without the proper SELFDN aci. - - adding the ACI, we can delete the entry - ''' - topology_st.standalone.log.info("\n\n######################### DELETE ######################\n") - - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - # entry to delete WITH member being BIND_DN but WITHOUT the ACI -> ldap.INSUFFICIENT_ACCESS - try: - topology_st.standalone.log.info("Try to delete %s (aci is missing)" % ENTRY_DN) - topology_st.standalone.delete_s(ENTRY_DN) - except Exception as e: - topology_st.standalone.log.info("Exception (expected): %s" % type(e).__name__) - assert isinstance(e, ldap.INSUFFICIENT_ACCESS) - - # Ok Now add the proper ACI - topology_st.standalone.log.info("Bind as %s and add the READ/SEARCH SELFDN aci" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - ACI_TARGET = "(target = \"ldap:///cn=*,%s\")" % SUFFIX - ACI_TARGETFILTER = "(targetfilter =\"(objectClass=%s)\")" % OC_NAME - ACI_ALLOW = "(version 3.0; acl \"SelfDN delete\"; allow (delete)" - ACI_SUBJECT = " userattr = \"member#selfDN\";)" - ACI_BODY = ACI_TARGET + ACI_TARGETFILTER + ACI_ALLOW + ACI_SUBJECT - mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] - topology_st.standalone.modify_s(SUFFIX, mod) - - # bind as bind_entry - topology_st.standalone.log.info("Bind as %s" % BIND_DN) - topology_st.standalone.simple_bind_s(BIND_DN, BIND_PW) - - # entry to search with the proper aci - topology_st.standalone.log.info("Try to delete %s should be successful" % ENTRY_DN) - topology_st.standalone.delete_s(ENTRY_DN) - - -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/tickets/ticket47669_test.py b/dirsrvtests/tests/tickets/ticket47669_test.py deleted file mode 100644 index 99aa20b..0000000 --- a/dirsrvtests/tests/tickets/ticket47669_test.py +++ /dev/null @@ -1,190 +0,0 @@ -# --- BEGIN COPYRIGHT BLOCK --- -# Copyright (C) 2016 Red Hat, Inc. -# All rights reserved. -# -# License: GPL (version 3 or any later version). -# See LICENSE for details. -# --- END COPYRIGHT BLOCK --- -# -import logging - -import pytest -from lib389.tasks import * -from lib389.topologies import topology_st - -from lib389._constants import DN_DM, PASSWORD, DEFAULT_SUFFIX - -log = logging.getLogger(__name__) - -CHANGELOG = 'cn=changelog5,cn=config' -RETROCHANGELOG = 'cn=Retro Changelog Plugin,cn=plugins,cn=config' - -MAXAGE = 'nsslapd-changelogmaxage' -TRIMINTERVAL = 'nsslapd-changelogtrim-interval' -COMPACTDBINTERVAL = 'nsslapd-changelogcompactdb-interval' - -FILTER = '(cn=*)' - - -def test_ticket47669_init(topology_st): - """ - Add cn=changelog5,cn=config - Enable cn=Retro Changelog Plugin,cn=plugins,cn=config - """ - log.info('Testing Ticket 47669 - Test duration syntax in the changelogs') - - # bind as directory manager - topology_st.standalone.log.info("Bind as %s" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - try: - changelogdir = os.path.join(os.path.dirname(topology_st.standalone.dbdir), 'changelog') - topology_st.standalone.add_s(Entry((CHANGELOG, - {'objectclass': 'top extensibleObject'.split(), - 'nsslapd-changelogdir': changelogdir}))) - except ldap.LDAPError as e: - log.error('Failed to add ' + CHANGELOG + ': error ' + e.message['desc']) - assert False - - try: - topology_st.standalone.modify_s(RETROCHANGELOG, [(ldap.MOD_REPLACE, 'nsslapd-pluginEnabled', 'on')]) - except ldap.LDAPError as e: - log.error('Failed to enable ' + RETROCHANGELOG + ': error ' + e.message['desc']) - assert False - - # restart the server - topology_st.standalone.restart(timeout=10) - - -def add_and_check(topology_st, plugin, attr, val, isvalid): - """ - Helper function to add/replace attr: val and check the added value - """ - if isvalid: - log.info('Test %s: %s -- valid' % (attr, val)) - try: - topology_st.standalone.modify_s(plugin, [(ldap.MOD_REPLACE, attr, val)]) - except ldap.LDAPError as e: - log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error ' + e.message['desc']) - assert False - else: - log.info('Test %s: %s -- invalid' % (attr, val)) - if plugin == CHANGELOG: - try: - topology_st.standalone.modify_s(plugin, [(ldap.MOD_REPLACE, attr, val)]) - except ldap.LDAPError as e: - log.error('Expectedly failed to add ' + attr + ': ' + val + - ' to ' + plugin + ': error ' + e.message['desc']) - else: - try: - topology_st.standalone.modify_s(plugin, [(ldap.MOD_REPLACE, attr, val)]) - except ldap.LDAPError as e: - log.error('Failed to add ' + attr + ': ' + val + ' to ' + plugin + ': error ' + e.message['desc']) - - try: - entries = topology_st.standalone.search_s(plugin, ldap.SCOPE_BASE, FILTER, [attr]) - if isvalid: - if not entries[0].hasValue(attr, val): - log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val)) - assert False - else: - if plugin == CHANGELOG: - if entries[0].hasValue(attr, val): - log.fatal('%s has unexpected (%s: %s)' % (plugin, attr, val)) - assert False - else: - if not entries[0].hasValue(attr, val): - log.fatal('%s does not have expected (%s: %s)' % (plugin, attr, val)) - assert False - except ldap.LDAPError as e: - log.fatal('Unable to search for entry %s: error %s' % (plugin, e.message['desc'])) - assert False - - -def test_ticket47669_changelog_maxage(topology_st): - """ - Test nsslapd-changelogmaxage in cn=changelog5,cn=config - """ - log.info('1. Test nsslapd-changelogmaxage in cn=changelog5,cn=config') - - # bind as directory manager - topology_st.standalone.log.info("Bind as %s" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - add_and_check(topology_st, CHANGELOG, MAXAGE, '12345', True) - add_and_check(topology_st, CHANGELOG, MAXAGE, '10s', True) - add_and_check(topology_st, CHANGELOG, MAXAGE, '30M', True) - add_and_check(topology_st, CHANGELOG, MAXAGE, '12h', True) - add_and_check(topology_st, CHANGELOG, MAXAGE, '2D', True) - add_and_check(topology_st, CHANGELOG, MAXAGE, '4w', True) - add_and_check(topology_st, CHANGELOG, MAXAGE, '-123', False) - add_and_check(topology_st, CHANGELOG, MAXAGE, 'xyz', False) - - -def test_ticket47669_changelog_triminterval(topology_st): - """ - Test nsslapd-changelogtrim-interval in cn=changelog5,cn=config - """ - log.info('2. Test nsslapd-changelogtrim-interval in cn=changelog5,cn=config') - - # bind as directory manager - topology_st.standalone.log.info("Bind as %s" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '12345', True) - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '10s', True) - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '30M', True) - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '12h', True) - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '2D', True) - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '4w', True) - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, '-123', False) - add_and_check(topology_st, CHANGELOG, TRIMINTERVAL, 'xyz', False) - - -def test_ticket47669_changelog_compactdbinterval(topology_st): - """ - Test nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config - """ - log.info('3. Test nsslapd-changelogcompactdb-interval in cn=changelog5,cn=config') - - # bind as directory manager - topology_st.standalone.log.info("Bind as %s" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '12345', True) - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '10s', True) - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '30M', True) - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '12h', True) - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '2D', True) - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '4w', True) - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, '-123', False) - add_and_check(topology_st, CHANGELOG, COMPACTDBINTERVAL, 'xyz', False) - - -def test_ticket47669_retrochangelog_maxage(topology_st): - """ - Test nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config - """ - log.info('4. Test nsslapd-changelogmaxage in cn=Retro Changelog Plugin,cn=plugins,cn=config') - - # bind as directory manager - topology_st.standalone.log.info("Bind as %s" % DN_DM) - topology_st.standalone.simple_bind_s(DN_DM, PASSWORD) - - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '12345', True) - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '10s', True) - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '30M', True) - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '12h', True) - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '2D', True) - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '4w', True) - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, '-123', False) - add_and_check(topology_st, RETROCHANGELOG, MAXAGE, 'xyz', False) - - topology_st.standalone.log.info("ticket47669 was successfully verified.") - - -if __name__ == '__main__': - # Run isolated - # -s for DEBUG mode - CURRENT_FILE = os.path.realpath(__file__) - pytest.main("-s %s" % CURRENT_FILE)