From 092631cacba6066f842b9c4c047e71ea4b2842aa Mon Sep 17 00:00:00 2001 From: "Thierry bordaz (tbordaz)" Date: Mon, 17 Mar 2014 15:30:44 +0100 Subject: [PATCH] Ticket 47553: test case Bug Description: This is the testcase of the ticket 47553 Fix Description: Reviewed by: ? Platforms tested: Flag Day: no Doc impact: no --- dirsrvtests/tickets/ticket47553_single_aci_test.py | 1161 ++++++++++++++++++++ 1 file changed, 1161 insertions(+) create mode 100644 dirsrvtests/tickets/ticket47553_single_aci_test.py diff --git a/dirsrvtests/tickets/ticket47553_single_aci_test.py b/dirsrvtests/tickets/ticket47553_single_aci_test.py new file mode 100644 index 0000000..218e938 --- /dev/null +++ b/dirsrvtests/tickets/ticket47553_single_aci_test.py @@ -0,0 +1,1161 @@ +''' +Created on Nov 7, 2013 + +@author: tbordaz +''' +import os +import sys +import time +import ldap +import logging +import socket +import time +import logging +import pytest +import re +from lib389 import DirSrv, Entry, tools +from lib389.tools import DirSrvTools +from lib389._constants import * +from lib389.properties import * +from constants import * +from lib389._constants import REPLICAROLE_MASTER + +logging.getLogger(__name__).setLevel(logging.DEBUG) +log = logging.getLogger(__name__) + +# +# important part. We can deploy Master1 and Master2 on different versions +# +installation1_prefix = None +installation2_prefix = None + +TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX + +STAGING_CN = "staged user" +PRODUCTION_CN = "accounts" +EXCEPT_CN = "excepts" + +STAGING_DN = "cn=%s,%s" % (STAGING_CN, SUFFIX) +PRODUCTION_DN = "cn=%s,%s" % (PRODUCTION_CN, SUFFIX) +PROD_EXCEPT_DN = "cn=%s,%s" % (EXCEPT_CN, PRODUCTION_DN) + +STAGING_PATTERN = "cn=%s*,%s" % (STAGING_CN[:2], SUFFIX) +PRODUCTION_PATTERN = "cn=%s*,%s" % (PRODUCTION_CN[:2], SUFFIX) +BAD_STAGING_PATTERN = "cn=bad*,%s" % (SUFFIX) +BAD_PRODUCTION_PATTERN = "cn=bad*,%s" % (SUFFIX) + +BIND_CN = "bind_entry" +BIND_DN = "cn=%s,%s" % (BIND_CN, SUFFIX) +BIND_PW = "password" + +NEW_ACCOUNT = "new_account" +MAX_ACCOUNTS = 20 + +CONFIG_MODDN_ACI_ATTR = "nsslapd-moddn-aci" + +class TopologyMaster1Master2(object): + def __init__(self, master1, master2): + master1.open() + self.master1 = master1 + + master2.open() + self.master2 = master2 + + +@pytest.fixture(scope="module") +def topology(request): + ''' + This fixture is used to create a replicated topology for the 'module'. + The replicated topology is MASTER1 <-> Master2. + At the beginning, It may exists a master2 instance and/or a master2 instance. + It may also exists a backup for the master1 and/or the master2. + + Principle: + If master1 instance exists: + restart it + If master2 instance exists: + restart it + If backup of master1 AND backup of master2 exists: + create or rebind to master1 + create or rebind to master2 + + restore master1 from backup + restore master2 from backup + else: + Cleanup everything + remove instances + remove backups + Create instances + Initialize replication + Create backups + ''' + global installation1_prefix + global installation2_prefix + + # allocate master1 on a given deployement + master1 = DirSrv(verbose=False) + if installation1_prefix: + args_instance[SER_DEPLOYED_DIR] = installation1_prefix + + # Args for the master1 instance + args_instance[SER_HOST] = HOST_MASTER_1 + args_instance[SER_PORT] = PORT_MASTER_1 + args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1 + args_master = args_instance.copy() + master1.allocate(args_master) + + # allocate master1 on a given deployement + master2 = DirSrv(verbose=False) + if installation2_prefix: + args_instance[SER_DEPLOYED_DIR] = installation2_prefix + + # Args for the consumer instance + args_instance[SER_HOST] = HOST_MASTER_2 + args_instance[SER_PORT] = PORT_MASTER_2 + args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2 + args_master = args_instance.copy() + master2.allocate(args_master) + + + # Get the status of the backups + backup_master1 = master1.checkBackupFS() + backup_master2 = master2.checkBackupFS() + + # Get the status of the instance and restart it if it exists + instance_master1 = master1.exists() + if instance_master1: + master1.stop(timeout=10) + master1.start(timeout=10) + + instance_master2 = master2.exists() + if instance_master2: + master2.stop(timeout=10) + master2.start(timeout=10) + + if backup_master1 and backup_master2: + # The backups exist, assuming they are correct + # we just re-init the instances with them + if not instance_master1: + master1.create() + # Used to retrieve configuration information (dbdir, confdir...) + master1.open() + + if not instance_master2: + master2.create() + # Used to retrieve configuration information (dbdir, confdir...) + master2.open() + + # restore master1 from backup + master1.stop(timeout=10) + master1.restoreFS(backup_master1) + master1.start(timeout=10) + + # restore master2 from backup + master2.stop(timeout=10) + master2.restoreFS(backup_master2) + master2.start(timeout=10) + else: + # We should be here only in two conditions + # - This is the first time a test involve master-consumer + # so we need to create everything + # - Something weird happened (instance/backup destroyed) + # so we discard everything and recreate all + + # Remove all the backups. So even if we have a specific backup file + # (e.g backup_master) we clear all backups that an instance my have created + if backup_master1: + master1.clearBackupFS() + if backup_master2: + master2.clearBackupFS() + + # Remove all the instances + if instance_master1: + master1.delete() + if instance_master2: + master2.delete() + + # Create the instances + master1.create() + master1.open() + master2.create() + master2.open() + + # + # Now prepare the Master-Consumer topology + # + # First Enable replication + master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1) + master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2) + + # Initialize the supplier->consumer + + properties = {RA_NAME: r'meTo_$host:$port', + RA_BINDDN: defaultProperties[REPLICATION_BIND_DN], + RA_BINDPW: defaultProperties[REPLICATION_BIND_PW], + RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD], + RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]} + repl_agreement = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties) + + if not repl_agreement: + log.fatal("Fail to create a replica agreement") + sys.exit(1) + + log.debug("%s created" % repl_agreement) + + properties = {RA_NAME: r'meTo_$host:$port', + RA_BINDDN: defaultProperties[REPLICATION_BIND_DN], + RA_BINDPW: defaultProperties[REPLICATION_BIND_PW], + RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD], + RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]} + master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties) + + master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2) + master1.waitForReplInit(repl_agreement) + + # Check replication is working fine + master1.add_s(Entry((TEST_REPL_DN, { + 'objectclass': "top person".split(), + 'sn': 'test_repl', + 'cn': 'test_repl'}))) + loop = 0 + while loop <= 10: + try: + ent = master2.getEntry(TEST_REPL_DN, ldap.SCOPE_BASE, "(objectclass=*)") + break + except ldap.NO_SUCH_OBJECT: + time.sleep(1) + loop += 1 + + # Time to create the backups + master1.stop(timeout=10) + master1.backupfile = master1.backupFS() + master1.start(timeout=10) + + master2.stop(timeout=10) + master2.backupfile = master2.backupFS() + master2.start(timeout=10) + + # + # Here we have two instances master and consumer + # with replication working. Either coming from a backup recovery + # or from a fresh (re)init + # Time to return the topology + return TopologyMaster1Master2(master1, master2) + + + +def _bind_manager(topology): + topology.master1.log.info("Bind as %s " % DN_DM) + topology.master1.simple_bind_s(DN_DM, PASSWORD) + +def _bind_normal(topology): + # bind as bind_entry + topology.master1.log.info("Bind as %s" % BIND_DN) + topology.master1.simple_bind_s(BIND_DN, BIND_PW) + +def _moddn_aci_deny_tree(topology, mod_type=None, target_from=STAGING_DN, target_to=PROD_EXCEPT_DN): + ''' + It denies the access moddn_to in cn=except,cn=accounts,SUFFIX + ''' + assert mod_type != None + + ACI_TARGET_FROM = "" + ACI_TARGET_TO = "" + if target_from: + ACI_TARGET_FROM = "(target_from = \"ldap:///%s\")" % (target_from) + if target_to: + ACI_TARGET_TO = "(target_to = \"ldap:///%s\")" % (target_to) + + ACI_ALLOW = "(version 3.0; acl \"Deny MODDN to prod_except\"; deny (moddn)" + ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN + ACI_BODY = ACI_TARGET_TO + ACI_TARGET_FROM + ACI_ALLOW + ACI_SUBJECT + mod = [(mod_type, 'aci', ACI_BODY)] + #topology.master1.modify_s(SUFFIX, mod) + topology.master1.log.info("Add a DENY aci under %s " % PROD_EXCEPT_DN) + topology.master1.modify_s(PROD_EXCEPT_DN, mod) + +def _moddn_aci_staging_to_production(topology, mod_type=None, target_from=STAGING_DN, target_to=PRODUCTION_DN): + assert mod_type != None + + + ACI_TARGET_FROM = "" + ACI_TARGET_TO = "" + if target_from: + ACI_TARGET_FROM = "(target_from = \"ldap:///%s\")" % (target_from) + if target_to: + ACI_TARGET_TO = "(target_to = \"ldap:///%s\")" % (target_to) + + ACI_ALLOW = "(version 3.0; acl \"MODDN from staging to production\"; allow (moddn)" + ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN + ACI_BODY = ACI_TARGET_FROM + ACI_TARGET_TO + ACI_ALLOW + ACI_SUBJECT + mod = [(mod_type, 'aci', ACI_BODY)] + topology.master1.modify_s(SUFFIX, mod) + +def _moddn_aci_from_production_to_staging(topology, mod_type=None): + assert mod_type != None + + ACI_TARGET = "(target_from = \"ldap:///%s\") (target_to = \"ldap:///%s\")" % (PRODUCTION_DN, STAGING_DN) + ACI_ALLOW = "(version 3.0; acl \"MODDN from production to staging\"; allow (moddn)" + ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN + ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT + mod = [(mod_type, 'aci', ACI_BODY)] + topology.master1.modify_s(SUFFIX, mod) + + +def test_ticket47553_init(topology): + """ + Creates + - a staging DIT + - a production DIT + - add accounts in staging DIT + - enable ACL logging + + """ + + topology.master1.log.info("\n\n######################### INITIALIZATION ######################\n") + + # entry used to bind with + topology.master1.log.info("Add %s" % BIND_DN) + topology.master1.add_s(Entry((BIND_DN, { + 'objectclass': "top person".split(), + 'sn': BIND_CN, + 'cn': BIND_CN, + 'userpassword': BIND_PW}))) + + # DIT for staging + topology.master1.log.info("Add %s" % STAGING_DN) + topology.master1.add_s(Entry((STAGING_DN, { + 'objectclass': "top organizationalRole".split(), + 'cn': STAGING_CN, + 'description': "staging DIT"}))) + + # DIT for production + topology.master1.log.info("Add %s" % PRODUCTION_DN) + topology.master1.add_s(Entry((PRODUCTION_DN, { + 'objectclass': "top organizationalRole".split(), + 'cn': PRODUCTION_CN, + 'description': "production DIT"}))) + + # DIT for production/except + topology.master1.log.info("Add %s" % PROD_EXCEPT_DN) + topology.master1.add_s(Entry((PROD_EXCEPT_DN, { + 'objectclass': "top organizationalRole".split(), + 'cn': EXCEPT_CN, + 'description': "production except DIT"}))) + + # enable acl error logging + mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '128')] + topology.master1.modify_s(DN_CONFIG, mod) + topology.master2.modify_s(DN_CONFIG, mod) + + + + + + # add dummy entries in the staging DIT + for cpt in range(MAX_ACCOUNTS): + name = "%s%d" % (NEW_ACCOUNT, cpt) + topology.master1.add_s(Entry(("cn=%s,%s" % (name, STAGING_DN), { + 'objectclass': "top person".split(), + 'sn': name, + 'cn': name}))) + + +def test_ticket47553_add(topology): + + topology.master1.log.info("\n\n######################### ADD (should fail) ######################\n") + + _bind_normal(topology) + + # + # First try to add an entry in production => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to add %s" % PRODUCTION_DN) + name = "%s%d" % (NEW_ACCOUNT, 0) + topology.master1.add_s(Entry(("cn=%s,%s" % (name, PRODUCTION_DN), { + 'objectclass': "top person".split(), + 'sn': name, + 'cn': name}))) + assert 0 # this is an error, we should not be allowed to add an entry in production + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + +def test_ticket47553_delete(topology): + + topology.master1.log.info("\n\n######################### DELETE (should fail) ######################\n") + + _bind_normal(topology) + # + # Second try to delete an entry in staging => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to delete %s" % STAGING_DN) + name = "%s%d" % (NEW_ACCOUNT, 0) + topology.master1.delete_s("cn=%s,%s" % (name, STAGING_DN)) + assert 0 # this is an error, we should not be allowed to add an entry in production + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + +def test_ticket47553_moddn_staging_prod_0(topology): + ''' + This test case MOVE entry NEW_ACCOUNT0 from staging to prod + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (0) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s0" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to and from equality filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + +def test_ticket47553_moddn_staging_prod_1(topology): + ''' + This test case MOVE entry NEW_ACCOUNT1 from staging to prod + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (1) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s1" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to substring/ from equality filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_PATTERN) + _bind_normal(topology) + + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_PATTERN) + _bind_normal(topology) + +def test_ticket47553_moddn_staging_prod_2(topology): + ''' + This test case fails to MOVE entry NEW_ACCOUNT2 from staging to prod + because of bad pattern + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (2) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s2" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to substring (BAD)/ from equality filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=BAD_PRODUCTION_PATTERN) + _bind_normal(topology) + + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=BAD_PRODUCTION_PATTERN) + _bind_normal(topology) + +def test_ticket47553_moddn_staging_prod_3(topology): + ''' + This test case MOVE entry NEW_ACCOUNT3 from staging to prod + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (3) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s3" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to:equality filter / from substring filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_PATTERN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_PATTERN, target_to=PRODUCTION_DN) + _bind_normal(topology) + +def test_ticket47553_moddn_staging_prod_4(topology): + ''' + This test case fails to MOVE entry NEW_ACCOUNT4 from staging to prod + because of bad pattern + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (4) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s4" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to: equality filter/ from: substring (BAD) ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=BAD_STAGING_PATTERN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=BAD_STAGING_PATTERN, target_to=PRODUCTION_DN) + _bind_normal(topology) + +def test_ticket47553_moddn_staging_prod_5(topology): + ''' + This test case MOVE entry NEW_ACCOUNT5 from staging to prod + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (5) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s5" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to:substring filter / from: substring filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_PATTERN, target_to=PRODUCTION_PATTERN) + _bind_normal(topology) + + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_PATTERN, target_to=PRODUCTION_PATTERN) + _bind_normal(topology) + +def test_ticket47553_moddn_staging_prod_6(topology): + ''' + This test case MOVE entry NEW_ACCOUNT6 from staging to prod + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (6) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s6" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to:substring filter / from: empty ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=None, target_to=PRODUCTION_PATTERN) + _bind_normal(topology) + + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=None, target_to=PRODUCTION_PATTERN) + _bind_normal(topology) + +def test_ticket47553_moddn_staging_prod_7(topology): + ''' + This test case MOVE entry NEW_ACCOUNT7 from staging to prod + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (7) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s7" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to: empty/ from: substring filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_PATTERN, target_to=None) + _bind_normal(topology) + + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_PATTERN, target_to=None) + _bind_normal(topology) + + +def test_ticket47553_moddn_staging_prod_8(topology): + ''' + This test case MOVE entry NEW_ACCOUNT8 from staging to prod + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (8) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s8" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to: empty/ from: empty ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=None, target_to=None) + _bind_normal(topology) + + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=None, target_to=None) + _bind_normal(topology) + +def test_ticket47553_moddn_staging_prod_9(topology): + ''' + This test case disable the 'moddn' right so a MODDN requires a 'add' right + to be successfull. + It fails to MOVE entry NEW_ACCOUNT9 from staging to prod. + Add a 'add' right to prod. + Then it succeeds to MOVE NEW_ACCOUNT9 from staging to prod. + + Then enable the 'moddn' right so a MODDN + It fails to MOVE entry NEW_ACCOUNT10 from staging to prod. + Add a 'moddn' right to prod. + Then it succeeds to MOVE NEW_ACCOUNT10 from staging to prod. + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (9) ######################\n") + + _bind_normal(topology) + old_rdn = "cn=%s9" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + ############################################ + # Now do tests with no support of moddn aci + ############################################ + topology.master1.log.info("Disable the moddn right" ) + _bind_manager(topology) + mod = [(ldap.MOD_REPLACE, CONFIG_MODDN_ACI_ATTR, 'off')] + topology.master1.modify_s(DN_CONFIG, mod) + + # Add the moddn aci that will not be evaluated because of the config flag + topology.master1.log.info("\n\n######################### MOVE to and from equality filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + # It will fail because it will test the ADD right + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # remove the moddn aci + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + # + # add the 'add' right to the production DN + # Then do a successfull moddn + # + ACI_ALLOW = "(version 3.0; acl \"ADD rights to allow moddn\"; allow (add)" + ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN + ACI_BODY = ACI_ALLOW + ACI_SUBJECT + + _bind_manager(topology) + mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) + _bind_normal(topology) + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + _bind_manager(topology) + mod = [(ldap.MOD_DELETE, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) + _bind_normal(topology) + + + ############################################ + # Now do tests with support of moddn aci + ############################################ + topology.master1.log.info("Enable the moddn right" ) + _bind_manager(topology) + mod = [(ldap.MOD_REPLACE, CONFIG_MODDN_ACI_ATTR, 'on')] + topology.master1.modify_s(DN_CONFIG, mod) + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (10) ######################\n") + + _bind_normal(topology) + old_rdn = "cn=%s10" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # + # add the 'add' right to the production DN + # Then do a failing moddn + # + ACI_ALLOW = "(version 3.0; acl \"ADD rights to allow moddn\"; allow (add)" + ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN + ACI_BODY = ACI_ALLOW + ACI_SUBJECT + + _bind_manager(topology) + mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) + _bind_normal(topology) + + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + _bind_manager(topology) + mod = [(ldap.MOD_DELETE, 'aci', ACI_BODY)] + topology.master1.modify_s(PRODUCTION_DN, mod) + _bind_normal(topology) + + # Add the moddn aci that will be evaluated because of the config flag + topology.master1.log.info("\n\n######################### MOVE to and from equality filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # remove the moddn aci + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + +def test_ticket47553_moddn_prod_staging(topology): + ''' + This test checks that we can move ACCOUNT11 from staging to prod + but not move back ACCOUNT11 from prod to staging + ''' + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (11) ######################\n") + + _bind_normal(topology) + + old_rdn = "cn=%s11" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to and from equality filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + + # + # Now check we can not move back the entry to staging + old_rdn = "cn=%s11" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, PRODUCTION_DN) + new_rdn = old_rdn + new_superior = STAGING_DN + + try: + topology.master1.log.info("Try to move back MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _bind_normal(topology) + + +def test_ticket47553_check_repl_M2_to_M1(topology): + ''' + Checks that replication is still working M2->M1, using ACCOUNT12 + ''' + topology.master1.log.info("Bind as %s (M2)" % DN_DM) + topology.master2.simple_bind_s(DN_DM, PASSWORD) + + + rdn = "cn=%s12" % NEW_ACCOUNT + dn = "%s,%s" % (rdn, STAGING_DN) + attribute = 'description' + tested_value = 'Hello world' + mod = [(ldap.MOD_REPLACE, attribute, tested_value)] + topology.master1.log.info("Update (M2) %s (%s)" % (dn, attribute)) + topology.master2.modify_s(dn, mod) + + loop = 0 + while loop <= 10: + ent = topology.master1.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent != None + if ent.hasAttr(attribute) and (ent.getValue(attribute) == tested_value): + break + + time.sleep(1) + loop += 1 + assert loop < 10 + topology.master1.log.info("Update %s (%s) replicated on M1" % (dn, attribute)) + +def test_ticket47553_moddn_staging_prod_except(topology): + ''' + This test case MOVE entry NEW_ACCOUNT13 from staging to prod + but fails to move entry NEW_ACCOUNT14 from staging to prod_except + ''' + + topology.master1.log.info("\n\n######################### MOVE staging -> Prod (13) ######################\n") + _bind_normal(topology) + + old_rdn = "cn=%s13" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PRODUCTION_DN + + # + # Try to rename without the apropriate ACI => INSUFFICIENT_ACCESS + # + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + + # successfull MOD with the ACI + topology.master1.log.info("\n\n######################### MOVE to and from equality filter ######################\n") + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _moddn_aci_deny_tree(topology, mod_type=ldap.MOD_ADD) + _bind_normal(topology) + + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + + # + # Now try to move an entry under except + # + topology.master1.log.info("\n\n######################### MOVE staging -> Prod/Except (14) ######################\n") + old_rdn = "cn=%s14" % NEW_ACCOUNT + old_dn = "%s,%s" % (old_rdn, STAGING_DN) + new_rdn = old_rdn + new_superior = PROD_EXCEPT_DN + try: + topology.master1.log.info("Try to MODDN %s -> %s,%s" % (old_dn, new_rdn, new_superior)) + topology.master1.rename_s(old_dn, new_rdn, newsuperior=new_superior) + assert 0 + except AssertionError: + topology.master1.log.info("Exception (not really expected exception but that is fine as it fails to rename)") + except Exception as e: + topology.master1.log.info("Exception (expected): %s" % type(e).__name__) + assert isinstance(e, ldap.INSUFFICIENT_ACCESS) + + # successfull MOD with the both ACI + _bind_manager(topology) + _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN) + _moddn_aci_deny_tree(topology, mod_type=ldap.MOD_DELETE) + _bind_normal(topology) + +def test_ticket47553_final(topology): + topology.master1.stop(timeout=10) + topology.master2.stop(timeout=10) + +def run_isolated(): + ''' + run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..) + To run isolated without py.test, you need to + - edit this file and comment '@pytest.fixture' line before 'topology' function. + - set the installation prefix + - run this program + ''' + global installation1_prefix + global installation2_prefix + installation1_prefix = None + installation2_prefix = None + + topo = topology(True) + topo.master1.log.info("\n\n######################### Ticket 47553 ######################\n") + test_ticket47553_init(topo) + + + # Check that without appropriate aci we are not allowed to add/delete + test_ticket47553_add(topo) + test_ticket47553_delete(topo) + + # tests the ACI as equality/substring filter + test_ticket47553_moddn_staging_prod_0(topo) + test_ticket47553_moddn_staging_prod_1(topo) + test_ticket47553_moddn_staging_prod_2(topo) + test_ticket47553_moddn_staging_prod_3(topo) + test_ticket47553_moddn_staging_prod_4(topo) + test_ticket47553_moddn_staging_prod_5(topo) + + # tests the ACI with undefined 'target_to'/'target_from' + test_ticket47553_moddn_staging_prod_6(topo) + test_ticket47553_moddn_staging_prod_7(topo) + test_ticket47553_moddn_staging_prod_8(topo) + + # Check we can control the behavior with nsslapd-moddn-aci + test_ticket47553_moddn_staging_prod_9(topo) + + # Check we can move entry 'from' -> 'to' but not 'to' -> 'from' + test_ticket47553_moddn_prod_staging(topo) + + # check replication is still working + test_ticket47553_check_repl_M2_to_M1(topo) + + # check DENY rule is working + test_ticket47553_moddn_staging_prod_except(topo) + + test_ticket47553_final(topo) + + + + +if __name__ == '__main__': + run_isolated() + -- 1.7.11.7