From b43e899454351274065ecf76cdf31ae2a84645bf Mon Sep 17 00:00:00 2001 From: Thierry bordaz (tbordaz) Date: Feb 24 2015 14:51:58 +0000 Subject: Ticket 47828: DNA scope: allow to exlude some subtrees Bug Description: DNA plugin generates attributes values to any added entry in the scope of the plugin (and filter). For ULC (http://www.freeipa.org/page/V3/User_Life-Cycle_Management), the provisioning container must be excluded. Fix Description: Add the ability to define multivalued attribute: "dnaExcludeScope" in the config entry. DNA will not generate attributes values for entries added under one of the excluded DIT http://fedorahosted.org/389/ticket/47828ticket URL> Platforms tested: F17/F20 Reviewed by : Noriko (many thanks) Flag Day: no Doc impact: yes (adding a config attribute) --- diff --git a/dirsrvtests/tickets/ticket47828_test.py b/dirsrvtests/tickets/ticket47828_test.py new file mode 100644 index 0000000..b9fb288 --- /dev/null +++ b/dirsrvtests/tickets/ticket47828_test.py @@ -0,0 +1,721 @@ +import os +import sys +import time +import ldap +import logging +import socket +import pytest +import shutil +from lib389 import DirSrv, Entry, tools +from lib389.tools import DirSrvTools +from lib389._constants import * +from lib389.properties import * +from constants import * + +log = logging.getLogger(__name__) + +installation_prefix = None + +ACCT_POLICY_CONFIG_DN = 'cn=config,cn=%s,cn=plugins,cn=config' % PLUGIN_ACCT_POLICY +ACCT_POLICY_DN = 'cn=Account Inactivation Pplicy,%s' % SUFFIX +INACTIVITY_LIMIT = '9' +SEARCHFILTER = '(objectclass=*)' + +DUMMY_CONTAINER = 'cn=dummy container,%s' % SUFFIX +PROVISIONING = 'cn=provisioning,%s' % SUFFIX +ACTIVE_USER1_CN = 'active user1' +ACTIVE_USER1_DN = 'cn=%s,%s' % (ACTIVE_USER1_CN, SUFFIX) +STAGED_USER1_CN = 'staged user1' +STAGED_USER1_DN = 'cn=%s,%s' % (STAGED_USER1_CN, PROVISIONING) +DUMMY_USER1_CN = 'dummy user1' +DUMMY_USER1_DN = 'cn=%s,%s' % (DUMMY_USER1_CN, DUMMY_CONTAINER) + +ALLOCATED_ATTR = 'employeeNumber' + +class TopologyStandalone(object): + def __init__(self, standalone): + standalone.open() + self.standalone = standalone + + +@pytest.fixture(scope="module") +def topology(request): + ''' + This fixture is used to standalone topology for the 'module'. + At the beginning, It may exists a standalone instance. + It may also exists a backup for the standalone instance. + + Principle: + If standalone instance exists: + restart it + If backup of standalone exists: + create/rebind to standalone + + restore standalone instance from backup + else: + Cleanup everything + remove instance + remove backup + Create instance + Create backup + ''' + global installation_prefix + + if installation_prefix: + args_instance[SER_DEPLOYED_DIR] = installation_prefix + + standalone = DirSrv(verbose=False) + + # Args for the standalone instance + args_instance[SER_HOST] = HOST_STANDALONE + args_instance[SER_PORT] = PORT_STANDALONE + args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE + args_standalone = args_instance.copy() + standalone.allocate(args_standalone) + + # Get the status of the backups + backup_standalone = standalone.checkBackupFS() + + # Get the status of the instance and restart it if it exists + instance_standalone = standalone.exists() + if instance_standalone: + # assuming the instance is already stopped, just wait 5 sec max + standalone.stop(timeout=5) + try: + standalone.start(timeout=10) + except ldap.SERVER_DOWN: + pass + + if backup_standalone: + # The backup exist, assuming it is correct + # we just re-init the instance with it + if not instance_standalone: + standalone.create() + # Used to retrieve configuration information (dbdir, confdir...) + standalone.open() + + # restore standalone instance from backup + standalone.stop(timeout=10) + standalone.restoreFS(backup_standalone) + standalone.start(timeout=10) + + else: + # We should be here only in two conditions + # - This is the first time a test involve standalone instance + # - Something weird happened (instance/backup destroyed) + # so we discard everything and recreate all + + # Remove the backup. So even if we have a specific backup file + # (e.g backup_standalone) we clear backup that an instance may have created + if backup_standalone: + standalone.clearBackupFS() + + # Remove the instance + if instance_standalone: + standalone.delete() + + # Create the instance + standalone.create() + + # Used to retrieve configuration information (dbdir, confdir...) + standalone.open() + + # Time to create the backups + standalone.stop(timeout=10) + standalone.backupfile = standalone.backupFS() + standalone.start(timeout=10) + + # + # Here we have standalone instance up and running + # Either coming from a backup recovery + # or from a fresh (re)init + # Time to return the topology + return TopologyStandalone(standalone) + +def _header(topology, label): + topology.standalone.log.info("\n\n###############################################") + topology.standalone.log.info("#######") + topology.standalone.log.info("####### %s" % label) + topology.standalone.log.info("#######") + topology.standalone.log.info("###############################################") + +def test_ticket47828_init(topology): + """ + Enable DNA + """ + topology.standalone.plugins.enable(name=PLUGIN_DNA) + + topology.standalone.add_s(Entry((PROVISIONING,{'objectclass': "top nscontainer".split(), + 'cn': 'provisioning'}))) + topology.standalone.add_s(Entry((DUMMY_CONTAINER,{'objectclass': "top nscontainer".split(), + 'cn': 'dummy container'}))) + + dn_config = "cn=excluded scope, cn=%s, %s" % (PLUGIN_DNA, DN_PLUGIN) + topology.standalone.add_s(Entry((dn_config, {'objectclass': "top extensibleObject".split(), + 'cn': 'excluded scope', + 'dnaType': ALLOCATED_ATTR, + 'dnaNextValue': str(1000), + 'dnaMaxValue': str(2000), + 'dnaMagicRegen': str(-1), + 'dnaFilter': '(&(objectClass=person)(objectClass=organizationalPerson)(objectClass=inetOrgPerson))', + 'dnaScope': SUFFIX}))) + topology.standalone.restart(timeout=10) + + + +def test_ticket47828_run_0(topology): + """ + NO exclude scope: Add an active entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'NO exclude scope: Add an active entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_1(topology): + """ + NO exclude scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'NO exclude scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_2(topology): + """ + NO exclude scope: Add a staged entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'NO exclude scope: Add a staged entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_3(topology): + """ + NO exclude scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'NO exclude scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_4(topology): + ''' + Exclude the provisioning container + ''' + _header(topology, 'Exclude the provisioning container') + + dn_config = "cn=excluded scope, cn=%s, %s" % (PLUGIN_DNA, DN_PLUGIN) + mod = [(ldap.MOD_REPLACE, 'dnaExcludeScope', PROVISIONING)] + topology.standalone.modify_s(dn_config, mod) + +def test_ticket47828_run_5(topology): + """ + Provisioning excluded scope: Add an active entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'Provisioning excluded scope: Add an active entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_6(topology): + """ + Provisioning excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_7(topology): + """ + Provisioning excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set + """ + _header(topology, 'Provisioning excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(-1) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_8(topology): + """ + Provisioning excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_9(topology): + """ + Provisioning excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'Provisioning excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + +def test_ticket47828_run_10(topology): + """ + Provisioning excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + +def test_ticket47828_run_11(topology): + ''' + Exclude (in addition) the dummy container + ''' + _header(topology, 'Exclude (in addition) the dummy container') + + dn_config = "cn=excluded scope, cn=%s, %s" % (PLUGIN_DNA, DN_PLUGIN) + mod = [(ldap.MOD_ADD, 'dnaExcludeScope', DUMMY_CONTAINER)] + topology.standalone.modify_s(dn_config, mod) + +def test_ticket47828_run_12(topology): + """ + Provisioning/Dummy excluded scope: Add an active entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'Provisioning/Dummy excluded scope: Add an active entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_13(topology): + """ + Provisioning/Dummy excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning/Dummy excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_14(topology): + """ + Provisioning/Dummy excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set + """ + _header(topology, 'Provisioning/Dummy excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(-1) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_15(topology): + """ + Provisioning/Dummy excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning/Dummy excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_16(topology): + """ + Provisioning/Dummy excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is not set + """ + _header(topology, 'Provisioning/Dummy excluded scope: Add an dummy entry and check its ALLOCATED_ATTR not is set') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(-1) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + +def test_ticket47828_run_17(topology): + """ + Provisioning/Dummy excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning/Dummy excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + + +def test_ticket47828_run_18(topology): + ''' + Exclude PROVISIONING and a wrong container + ''' + _header(topology, 'Exclude PROVISIONING and a wrong container') + + dn_config = "cn=excluded scope, cn=%s, %s" % (PLUGIN_DNA, DN_PLUGIN) + mod = [(ldap.MOD_REPLACE, 'dnaExcludeScope', PROVISIONING)] + topology.standalone.modify_s(dn_config, mod) + try: + mod = [(ldap.MOD_ADD, 'dnaExcludeScope', "invalidDN,%s" % SUFFIX)] + topology.standalone.modify_s(dn_config, mod) + raise ValueError("invalid dnaExcludeScope value (not a DN)") + except ldap.INVALID_SYNTAX: + pass + +def test_ticket47828_run_19(topology): + """ + Provisioning+wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'Provisioning+wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_20(topology): + """ + Provisioning+wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning+wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_21(topology): + """ + Provisioning+wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set + """ + _header(topology, 'Provisioning+wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(-1) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_22(topology): + """ + Provisioning+wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning+wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_23(topology): + """ + Provisioning+wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'Provisioning+wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + +def test_ticket47828_run_24(topology): + """ + Provisioning+wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Provisioning+wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + +def test_ticket47828_run_25(topology): + ''' + Exclude a wrong container + ''' + _header(topology, 'Exclude a wrong container') + + dn_config = "cn=excluded scope, cn=%s, %s" % (PLUGIN_DNA, DN_PLUGIN) + + try: + mod = [(ldap.MOD_REPLACE, 'dnaExcludeScope', "invalidDN,%s" % SUFFIX)] + topology.standalone.modify_s(dn_config, mod) + raise ValueError("invalid dnaExcludeScope value (not a DN)") + except ldap.INVALID_SYNTAX: + pass + +def test_ticket47828_run_26(topology): + """ + Wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'Wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_27(topology): + """ + Wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Wrong container excluded scope: Add an active entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((ACTIVE_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': ACTIVE_USER1_CN, + 'sn': ACTIVE_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(ACTIVE_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (ACTIVE_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(ACTIVE_USER1_DN) + +def test_ticket47828_run_28(topology): + """ + Wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set + """ + _header(topology, 'Wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is not set') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(-1) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_29(topology): + """ + Wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Wrong container excluded scope: Add a staged entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((STAGED_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': STAGED_USER1_CN, + 'sn': STAGED_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(STAGED_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (STAGED_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(STAGED_USER1_DN) + +def test_ticket47828_run_30(topology): + """ + Wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is set + """ + _header(topology, 'Wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is set') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(-1)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) != str(-1) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + +def test_ticket47828_run_31(topology): + """ + Wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic) + """ + _header(topology, 'Wrong container excluded scope: Add an dummy entry and check its ALLOCATED_ATTR is unchanged (!= magic)') + + topology.standalone.add_s(Entry((DUMMY_USER1_DN, {'objectclass': "top person organizationalPerson inetOrgPerson".split(), + 'cn': DUMMY_USER1_CN, + 'sn': DUMMY_USER1_CN, + ALLOCATED_ATTR: str(20)}))) + ent = topology.standalone.getEntry(DUMMY_USER1_DN, ldap.SCOPE_BASE, "(objectclass=*)") + assert ent.hasAttr(ALLOCATED_ATTR) + assert ent.getValue(ALLOCATED_ATTR) == str(20) + topology.standalone.log.debug('%s.%s=%s' % (DUMMY_USER1_CN, ALLOCATED_ATTR, ent.getValue(ALLOCATED_ATTR))) + topology.standalone.delete_s(DUMMY_USER1_DN) + +def test_ticket47828_final(topology): + topology.standalone.plugins.disable(name=PLUGIN_DNA) + topology.standalone.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 installation_prefix + installation_prefix = None + + topo = topology(True) + test_ticket47828_init(topo) + + test_ticket47828_run_0(topo) + test_ticket47828_run_1(topo) + test_ticket47828_run_2(topo) + test_ticket47828_run_3(topo) + test_ticket47828_run_4(topo) + test_ticket47828_run_5(topo) + test_ticket47828_run_6(topo) + test_ticket47828_run_7(topo) + test_ticket47828_run_8(topo) + test_ticket47828_run_9(topo) + test_ticket47828_run_10(topo) + test_ticket47828_run_11(topo) + test_ticket47828_run_12(topo) + test_ticket47828_run_13(topo) + test_ticket47828_run_14(topo) + test_ticket47828_run_15(topo) + test_ticket47828_run_16(topo) + test_ticket47828_run_17(topo) + test_ticket47828_run_18(topo) + test_ticket47828_run_19(topo) + test_ticket47828_run_20(topo) + test_ticket47828_run_21(topo) + test_ticket47828_run_22(topo) + test_ticket47828_run_23(topo) + test_ticket47828_run_24(topo) + test_ticket47828_run_25(topo) + test_ticket47828_run_26(topo) + test_ticket47828_run_27(topo) + test_ticket47828_run_28(topo) + test_ticket47828_run_29(topo) + test_ticket47828_run_30(topo) + test_ticket47828_run_31(topo) + + test_ticket47828_final(topo) + + +if __name__ == '__main__': + run_isolated() diff --git a/ldap/schema/10dna-plugin.ldif b/ldap/schema/10dna-plugin.ldif index 74f5f8b..3143904 100644 --- a/ldap/schema/10dna-plugin.ldif +++ b/ldap/schema/10dna-plugin.ldif @@ -202,6 +202,13 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2160 NAME 'dnaRemoteBindMethod' # ################################################################################ # +attributeTypes: ( 2.16.840.1.113730.3.1.2312 NAME 'dnaExcludeScope' + DESC 'DN of a subtree excluded from DNA plugin scope' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# objectClasses: ( 2.16.840.1.113730.3.2.324 NAME 'dnaPluginConfig' DESC 'DNA plugin configuration' SUP top @@ -214,6 +221,7 @@ objectClasses: ( 2.16.840.1.113730.3.2.324 NAME 'dnaPluginConfig' dnaMagicRegen $ dnaFilter $ dnaScope $ + dnaExcludeScope $ dnaSharedCfgDN $ dnaThreshold $ dnaNextRange $ diff --git a/ldap/servers/plugins/dna/dna.c b/ldap/servers/plugins/dna/dna.c index f4a36f0..25123cb 100644 --- a/ldap/servers/plugins/dna/dna.c +++ b/ldap/servers/plugins/dna/dna.c @@ -87,6 +87,7 @@ #define DNA_GENERATE "dnaMagicRegen" #define DNA_FILTER "dnaFilter" #define DNA_SCOPE "dnaScope" +#define DNA_EXCLUDE_SCOPE "dnaExcludeScope" #define DNA_REMOTE_BIND_DN "dnaRemoteBindDN" #define DNA_REMOTE_BIND_PW "dnaRemoteBindCred" @@ -164,6 +165,7 @@ struct configEntry { Slapi_Filter *slapi_filter; char *generate; char *scope; + Slapi_DN **excludescope; PRUint64 interval; PRUint64 threshold; char *shared_cfg_base; @@ -356,6 +358,17 @@ dna_config_copy() new_entry->slapi_filter = slapi_filter_dup(config_entry->slapi_filter); new_entry->generate = slapi_ch_strdup(config_entry->generate); new_entry->scope = slapi_ch_strdup(config_entry->scope); + if (config_entry->excludescope == NULL) { + new_entry->excludescope = NULL; + } else { + int i; + + for (i = 0; config_entry->excludescope[i]; i++); + new_entry->excludescope = (Slapi_DN **) slapi_ch_calloc(sizeof (Slapi_DN *), i + 1); + for (i = 0; new_entry->excludescope[i]; i++) { + new_entry->excludescope[i] = slapi_sdn_dup(config_entry->excludescope[i]); + } + } new_entry->shared_cfg_base = slapi_ch_strdup(config_entry->shared_cfg_base); new_entry->shared_cfg_dn = slapi_ch_strdup(config_entry->shared_cfg_dn); new_entry->remote_binddn = slapi_ch_strdup(config_entry->remote_binddn); @@ -906,6 +919,7 @@ dna_parse_config_entry(Slapi_PBlock *pb, Slapi_Entry * e, int apply) int entry_added = 0; int i = 0; int ret = DNA_SUCCESS; + char **plugin_attr_values; slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, "--> dna_parse_config_entry\n"); @@ -1056,6 +1070,31 @@ dna_parse_config_entry(Slapi_PBlock *pb, Slapi_Entry * e, int apply) slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, "----------> %s [%s]\n", DNA_SCOPE, entry->scope); + + plugin_attr_values = slapi_entry_attr_get_charray(e, DNA_EXCLUDE_SCOPE); + if (plugin_attr_values) { + int j = 0; + + /* Allocate the array of excluded scopes */ + for (i = 0; plugin_attr_values[i]; i++); + entry->excludescope = (Slapi_DN **) slapi_ch_calloc(sizeof (Slapi_DN *), i + 1); + + /* Copy them in the config at the condition they are valid DN */ + for (i = 0; plugin_attr_values[i]; i++) { + if (slapi_dn_syntax_check(NULL, plugin_attr_values[i], 1) == 1) { + slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, + "Error: Ignoring invalid DN used as excluded scope: [%s]\n", plugin_attr_values[i]); + slapi_ch_free_string(&plugin_attr_values[i]); + } else { + entry->excludescope[j++] = slapi_sdn_new_dn_passin(plugin_attr_values[i]); + } + } + slapi_ch_free((void**) &plugin_attr_values); + } + for (i = 0; entry->excludescope && entry->excludescope[i]; i++) { + slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, + "----------> %s[%d] [%s]\n", DNA_EXCLUDE_SCOPE, i, slapi_sdn_get_dn(entry->excludescope[i])); + } /* optional, if not specified set -1 which is converted to the max unsigned * value */ @@ -1381,6 +1420,14 @@ dna_free_config_entry(struct configEntry ** entry) slapi_filter_free(e->slapi_filter, 1); slapi_ch_free_string(&e->generate); slapi_ch_free_string(&e->scope); + if (e->excludescope) { + int i; + + for (i = 0; e->excludescope[i]; i++) { + slapi_sdn_free(&e->excludescope[i]); + } + slapi_ch_free((void **)&e->excludescope); + } slapi_ch_free_string(&e->shared_cfg_base); slapi_ch_free_string(&e->shared_cfg_dn); slapi_ch_free_string(&e->remote_binddn); @@ -3311,6 +3358,13 @@ _dna_pre_op_add(Slapi_PBlock *pb, Slapi_Entry *e, char **errstr) goto next; } + /* is this entry in an excluded scope? */ + for (i = 0; config_entry->excludescope && config_entry->excludescope[i]; i++) { + if (slapi_dn_issuffix(dn, slapi_sdn_get_dn(config_entry->excludescope[i]))) { + goto next; + } + } + /* does the entry match the filter? */ if (config_entry->slapi_filter) { ret = slapi_vattr_filter_test(pb, e, config_entry->slapi_filter, 0); @@ -3509,7 +3563,14 @@ _dna_pre_op_modify(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Mods *smods, char **e !slapi_dn_issuffix(dn, config_entry->scope)) { goto next; } - + + /* is this entry in an excluded scope? */ + for (i = 0; config_entry->excludescope && config_entry->excludescope[i]; i++) { + if (slapi_dn_issuffix(dn, slapi_sdn_get_dn(config_entry->excludescope[i]))) { + goto next; + } + } + /* does the entry match the filter? */ if (config_entry->slapi_filter) { ret = slapi_vattr_filter_test(pb, e, @@ -3955,6 +4016,13 @@ static int dna_be_txn_pre_op(Slapi_PBlock *pb, int modtype) goto next; } + /* is this entry in an excluded scope? */ + for (i = 0; config_entry->excludescope && config_entry->excludescope[i]; i++) { + if (slapi_dn_issuffix(dn, slapi_sdn_get_dn(config_entry->excludescope[i]))) { + goto next; + } + } + /* does the entry match the filter? */ if (config_entry->slapi_filter) { if(LDAP_SUCCESS != slapi_vattr_filter_test(pb,e,config_entry->slapi_filter, 0)) @@ -4562,6 +4630,9 @@ void dna_dump_config_entry(struct configEntry * entry) printf("<---- filter ---------> %s\n", entry->filter); printf("<---- prefix ---------> %s\n", entry->prefix); printf("<---- scope ----------> %s\n", entry->scope); + for (i = 0; entry->excludescope && entry->excludescope[i]; i++) { + printf("<---- excluded scope -> %s\n", slapi_sdn_get_dn(entry->excludescope[i])); + } printf("<---- next value -----> %" PRIu64 "\n", entry->nextval); printf("<---- max value ------> %" PRIu64 "\n", entry->maxval); printf("<---- interval -------> %" PRIu64 "\n", entry->interval);