From cd9afbbeb1570adf663acb5b8f043a2012b839aa Mon Sep 17 00:00:00 2001 From: Thierry Bordaz Date: May 07 2015 12:05:48 +0000 Subject: Ticket 47927: Uniqueness plugin: should allow to exclude some subtrees from its scope Bug Description: Support excluding subtrees in uniqueness plugin Fix Description: Exclude subtrees from uniqueness check if they are specified in the plugin config. To exclude certain parts of the tree from uniqueness check, set uniqueness-exclude-subtrees: cn=subtree,dc=example,dc=com in the plugin's configuration. Only new style configuration is supported for specifying excludes. https://fedorahosted.org/389/ticket/47927 Reviewed by: Rich (thanks !) Platforms tested: F17 Flag Day: no Doc impact: yes (new uniqueness attribute uniqueness-exclude-subtrees) --- diff --git a/dirsrvtests/tickets/ticket47823_test.py b/dirsrvtests/tickets/ticket47823_test.py index 317ebf5..e685fbf 100644 --- a/dirsrvtests/tickets/ticket47823_test.py +++ b/dirsrvtests/tickets/ticket47823_test.py @@ -59,11 +59,11 @@ def topology(request): ''' global installation_prefix - if installation_prefix: - args_instance[SER_DEPLOYED_DIR] = installation_prefix - standalone = DirSrv(verbose=False) + standalone = DirSrv(verbose=False) + if installation_prefix: + args_instance[SER_DEPLOYED_DIR] = installation_prefix # Args for the standalone instance args_instance[SER_HOST] = HOST_STANDALONE args_instance[SER_PORT] = PORT_STANDALONE @@ -629,6 +629,7 @@ def test_ticket47823_invalid_config_1(topology): topology.standalone.getEntry(config.dn, ldap.SCOPE_BASE, "(objectclass=nsSlapdPlugin)", ALL_CONFIG_ATTRS) # Check the server did not restart + topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '65536')]) try: topology.standalone.restart(timeout=5) ent = topology.standalone.getEntry(config.dn, ldap.SCOPE_BASE, "(objectclass=nsSlapdPlugin)", ALL_CONFIG_ATTRS) @@ -640,7 +641,7 @@ def test_ticket47823_invalid_config_1(topology): pass # Check the expected error message - regex = re.compile("Config info: attribute name not defined") + regex = re.compile("Config fail: unable to parse old style") res = _pattern_errorlog(topology.standalone.errorlog_file, regex) if not res: # be sure to restore a valid config before assert @@ -732,6 +733,7 @@ def test_ticket47823_invalid_config_3(topology): topology.standalone.getEntry(config.dn, ldap.SCOPE_BASE, "(objectclass=nsSlapdPlugin)", ALL_CONFIG_ATTRS) # Check the server did not restart + topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '65536')]) try: topology.standalone.restart(timeout=5) ent = topology.standalone.getEntry(config.dn, ldap.SCOPE_BASE, "(objectclass=nsSlapdPlugin)", ALL_CONFIG_ATTRS) @@ -743,7 +745,7 @@ def test_ticket47823_invalid_config_3(topology): pass # Check the expected error message - regex = re.compile("Config info: objectclass for subtree entries is not defined") + regex = re.compile("Config fail: unable to parse old style") res = _pattern_errorlog(topology.standalone.errorlog_file, regex) if not res: # be sure to restore a valid config before assert @@ -925,6 +927,7 @@ def test_ticket47823_invalid_config_7(topology): # create an invalid config without arg0 config = _build_config(topology, attr_name='cn', subtree_1="this_is dummy DN", subtree_2="an other=dummy DN", type_config='new', across_subtrees=False) + topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '65536')]) # replace 'cn' uniqueness entry try: topology.standalone.delete_s(config.dn) diff --git a/dirsrvtests/tickets/ticket47927_test.py b/dirsrvtests/tickets/ticket47927_test.py new file mode 100644 index 0000000..ee1f130 --- /dev/null +++ b/dirsrvtests/tickets/ticket47927_test.py @@ -0,0 +1,300 @@ +import os +import sys +import time +import ldap +import logging +import pytest +from lib389 import DirSrv, Entry, tools, tasks +from lib389.tools import DirSrvTools +from lib389._constants import * +from lib389.properties import * +from lib389.tasks import * +from lib389.utils import * + +logging.getLogger(__name__).setLevel(logging.DEBUG) +log = logging.getLogger(__name__) + +installation1_prefix = None + +EXCLUDED_CONTAINER_CN = "excluded_container" +EXCLUDED_CONTAINER_DN = "cn=%s,%s" % (EXCLUDED_CONTAINER_CN, SUFFIX) + +EXCLUDED_BIS_CONTAINER_CN = "excluded_bis_container" +EXCLUDED_BIS_CONTAINER_DN = "cn=%s,%s" % (EXCLUDED_BIS_CONTAINER_CN, SUFFIX) + +ENFORCED_CONTAINER_CN = "enforced_container" +ENFORCED_CONTAINER_DN = "cn=%s,%s" % (ENFORCED_CONTAINER_CN, SUFFIX) + +USER_1_CN = "test_1" +USER_1_DN = "cn=%s,%s" % (USER_1_CN, ENFORCED_CONTAINER_DN) +USER_2_CN = "test_2" +USER_2_DN = "cn=%s,%s" % (USER_2_CN, ENFORCED_CONTAINER_DN) +USER_3_CN = "test_3" +USER_3_DN = "cn=%s,%s" % (USER_3_CN, EXCLUDED_CONTAINER_DN) +USER_4_CN = "test_4" +USER_4_DN = "cn=%s,%s" % (USER_4_CN, EXCLUDED_BIS_CONTAINER_DN) + + +class TopologyStandalone(object): + def __init__(self, standalone): + standalone.open() + self.standalone = standalone + + +@pytest.fixture(scope="module") +def topology(request): + global installation1_prefix + + # Creating standalone instance ... + standalone = DirSrv(verbose=False) + if installation1_prefix: + args_instance[SER_DEPLOYED_DIR] = installation1_prefix + args_instance[SER_HOST] = HOST_STANDALONE + args_instance[SER_PORT] = PORT_STANDALONE + args_instance[SER_SERVERID_PROP] = SERVERID_STANDALONE + args_instance[SER_CREATION_SUFFIX] = DEFAULT_SUFFIX + args_standalone = args_instance.copy() + standalone.allocate(args_standalone) + instance_standalone = standalone.exists() + if instance_standalone: + standalone.delete() + standalone.create() + standalone.open() + + # Clear out the tmp dir + standalone.clearTmpDir(__file__) + + return TopologyStandalone(standalone) + +def test_ticket47927_init(topology): + topology.standalone.plugins.enable(name=PLUGIN_ATTR_UNIQUENESS) + try: + topology.standalone.modify_s('cn=' + PLUGIN_ATTR_UNIQUENESS + ',cn=plugins,cn=config', + [(ldap.MOD_REPLACE, 'uniqueness-attribute-name', 'telephonenumber'), + (ldap.MOD_REPLACE, 'uniqueness-subtrees', DEFAULT_SUFFIX), + ]) + except ldap.LDAPError, e: + log.fatal('test_ticket47927: Failed to configure plugin for "telephonenumber": error ' + e.message['desc']) + assert False + topology.standalone.restart(timeout=120) + + topology.standalone.add_s(Entry((EXCLUDED_CONTAINER_DN, {'objectclass': "top nscontainer".split(), + 'cn': EXCLUDED_CONTAINER_CN}))) + topology.standalone.add_s(Entry((EXCLUDED_BIS_CONTAINER_DN, {'objectclass': "top nscontainer".split(), + 'cn': EXCLUDED_BIS_CONTAINER_CN}))) + topology.standalone.add_s(Entry((ENFORCED_CONTAINER_DN, {'objectclass': "top nscontainer".split(), + 'cn': ENFORCED_CONTAINER_CN}))) + + # adding an entry on a stage with a different 'cn' + topology.standalone.add_s(Entry((USER_1_DN, { + 'objectclass': "top person".split(), + 'sn': USER_1_CN, + 'cn': USER_1_CN}))) + # adding an entry on a stage with a different 'cn' + topology.standalone.add_s(Entry((USER_2_DN, { + 'objectclass': "top person".split(), + 'sn': USER_2_CN, + 'cn': USER_2_CN}))) + topology.standalone.add_s(Entry((USER_3_DN, { + 'objectclass': "top person".split(), + 'sn': USER_3_CN, + 'cn': USER_3_CN}))) + topology.standalone.add_s(Entry((USER_4_DN, { + 'objectclass': "top person".split(), + 'sn': USER_4_CN, + 'cn': USER_4_CN}))) + +def test_ticket47927_one(topology): + ''' + Check that uniqueness is enforce on all SUFFIX + ''' + UNIQUE_VALUE='1234' + try: + topology.standalone.modify_s(USER_1_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_one: Failed to set the telephonenumber for %s: %s' % (USER_1_DN, e.message['desc'])) + assert False + + # we expect to fail because user1 is in the scope of the plugin + try: + topology.standalone.modify_s(USER_2_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_one: unexpected success to set the telephonenumber for %s' % (USER_2_DN)) + assert False + except ldap.LDAPError, e: + log.fatal('test_ticket47927_one: Failed (expected) to set the telephonenumber for %s: %s' % (USER_2_DN, e.message['desc'])) + pass + + + # we expect to fail because user1 is in the scope of the plugin + try: + topology.standalone.modify_s(USER_3_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_one: unexpected success to set the telephonenumber for %s' % (USER_3_DN)) + assert False + except ldap.LDAPError, e: + log.fatal('test_ticket47927_one: Failed (expected) to set the telephonenumber for %s: %s' % (USER_3_DN, e.message['desc'])) + pass + + +def test_ticket47927_two(topology): + ''' + Exclude the EXCLUDED_CONTAINER_DN from the uniqueness plugin + ''' + try: + topology.standalone.modify_s('cn=' + PLUGIN_ATTR_UNIQUENESS + ',cn=plugins,cn=config', + [(ldap.MOD_REPLACE, 'uniqueness-exclude-subtrees', EXCLUDED_CONTAINER_DN)]) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_two: Failed to configure plugin for to exclude %s: error %s' % (EXCLUDED_CONTAINER_DN, e.message['desc'])) + assert False + topology.standalone.restart(timeout=120) + +def test_ticket47927_three(topology): + ''' + Check that uniqueness is enforced on full SUFFIX except EXCLUDED_CONTAINER_DN + First case: it exists an entry (with the same attribute value) in the scope + of the plugin and we set the value in an entry that is in an excluded scope + ''' + UNIQUE_VALUE='9876' + try: + topology.standalone.modify_s(USER_1_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_three: Failed to set the telephonenumber ' + e.message['desc']) + assert False + + # we should not be allowed to set this value (because user1 is in the scope) + try: + topology.standalone.modify_s(USER_2_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_three: unexpected success to set the telephonenumber for %s' % (USER_2_DN)) + assert False + except ldap.LDAPError, e: + log.fatal('test_ticket47927_three: Failed (expected) to set the telephonenumber for %s: %s' % (USER_2_DN , e.message['desc'])) + + + # USER_3_DN is in EXCLUDED_CONTAINER_DN so update should be successful + try: + topology.standalone.modify_s(USER_3_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_three: success to set the telephonenumber for %s' % (USER_3_DN)) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_three: Failed (unexpected) to set the telephonenumber for %s: %s' % (USER_3_DN, e.message['desc'])) + assert False + + +def test_ticket47927_four(topology): + ''' + Check that uniqueness is enforced on full SUFFIX except EXCLUDED_CONTAINER_DN + Second case: it exists an entry (with the same attribute value) in an excluded scope + of the plugin and we set the value in an entry is in the scope + ''' + UNIQUE_VALUE='1111' + # USER_3_DN is in EXCLUDED_CONTAINER_DN so update should be successful + try: + topology.standalone.modify_s(USER_3_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_four: success to set the telephonenumber for %s' % USER_3_DN) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_four: Failed (unexpected) to set the telephonenumber for %s: %s' % (USER_3_DN, e.message['desc'])) + assert False + + + # we should be allowed to set this value (because user3 is excluded from scope) + try: + topology.standalone.modify_s(USER_1_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_four: Failed to set the telephonenumber for %s: %s' % (USER_1_DN, e.message['desc'])) + assert False + + # we should not be allowed to set this value (because user1 is in the scope) + try: + topology.standalone.modify_s(USER_2_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_four: unexpected success to set the telephonenumber %s' % USER_2_DN) + assert False + except ldap.LDAPError, e: + log.fatal('test_ticket47927_four: Failed (expected) to set the telephonenumber for %s: %s' % (USER_2_DN, e.message['desc'])) + pass + +def test_ticket47927_five(topology): + ''' + Exclude the EXCLUDED_BIS_CONTAINER_DN from the uniqueness plugin + ''' + try: + topology.standalone.modify_s('cn=' + PLUGIN_ATTR_UNIQUENESS + ',cn=plugins,cn=config', + [(ldap.MOD_ADD, 'uniqueness-exclude-subtrees', EXCLUDED_BIS_CONTAINER_DN)]) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_five: Failed to configure plugin for to exclude %s: error %s' % (EXCLUDED_BIS_CONTAINER_DN, e.message['desc'])) + assert False + topology.standalone.restart(timeout=120) + topology.standalone.getEntry('cn=' + PLUGIN_ATTR_UNIQUENESS + ',cn=plugins,cn=config', ldap.SCOPE_BASE) + +def test_ticket47927_six(topology): + ''' + Check that uniqueness is enforced on full SUFFIX except EXCLUDED_CONTAINER_DN + and EXCLUDED_BIS_CONTAINER_DN + First case: it exists an entry (with the same attribute value) in the scope + of the plugin and we set the value in an entry that is in an excluded scope + ''' + UNIQUE_VALUE='222' + try: + topology.standalone.modify_s(USER_1_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_six: Failed to set the telephonenumber ' + e.message['desc']) + assert False + + # we should not be allowed to set this value (because user1 is in the scope) + try: + topology.standalone.modify_s(USER_2_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_six: unexpected success to set the telephonenumber for %s' % (USER_2_DN)) + assert False + except ldap.LDAPError, e: + log.fatal('test_ticket47927_six: Failed (expected) to set the telephonenumber for %s: %s' % (USER_2_DN , e.message['desc'])) + + + # USER_3_DN is in EXCLUDED_CONTAINER_DN so update should be successful + try: + topology.standalone.modify_s(USER_3_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_six: success to set the telephonenumber for %s' % (USER_3_DN)) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_six: Failed (unexpected) to set the telephonenumber for %s: %s' % (USER_3_DN, e.message['desc'])) + assert False + # USER_4_DN is in EXCLUDED_CONTAINER_DN so update should be successful + try: + topology.standalone.modify_s(USER_4_DN, + [(ldap.MOD_REPLACE, 'telephonenumber', UNIQUE_VALUE)]) + log.fatal('test_ticket47927_six: success to set the telephonenumber for %s' % (USER_4_DN)) + except ldap.LDAPError, e: + log.fatal('test_ticket47927_six: Failed (unexpected) to set the telephonenumber for %s: %s' % (USER_4_DN, e.message['desc'])) + assert False + + +def test_ticket47927_final(topology): + topology.standalone.delete() + log.info('Testcase PASSED') + + +def run_isolated(): + global installation1_prefix + installation1_prefix = None + + topo = topology(True) + test_ticket47927_init(topo) + test_ticket47927_one(topo) + test_ticket47927_two(topo) + test_ticket47927_three(topo) + test_ticket47927_four(topo) + test_ticket47927_five(topo) + test_ticket47927_six(topo) + test_ticket47927_final(topo) + + +if __name__ == '__main__': + run_isolated() + diff --git a/ldap/servers/plugins/uiduniq/uid.c b/ldap/servers/plugins/uiduniq/uid.c index b9c4e72..5b57828 100644 --- a/ldap/servers/plugins/uiduniq/uid.c +++ b/ldap/servers/plugins/uiduniq/uid.c @@ -70,7 +70,7 @@ int ldap_quote_filter_value( static int search_one_berval(Slapi_DN *baseDN, const char **attrNames, - const struct berval *value, const char *requiredObjectClass, Slapi_DN *target); + const struct berval *value, const char *requiredObjectClass, Slapi_DN *target, Slapi_DN **excludes); /* * ISSUES: @@ -103,6 +103,7 @@ typedef struct attr_uniqueness_config { const char **attrs; char *attr_friendly; Slapi_DN **subtrees; + Slapi_DN **exclude_subtrees; PRBool unique_in_all_subtrees; char *top_entry_oc; char *subtree_entries_oc; @@ -111,6 +112,7 @@ typedef struct attr_uniqueness_config { #define ATTR_UNIQUENESS_ATTRIBUTE_NAME "uniqueness-attribute-name" #define ATTR_UNIQUENESS_SUBTREES "uniqueness-subtrees" +#define ATTR_UNIQUENESS_EXCLUDE_SUBTREES "uniqueness-exclude-subtrees" #define ATTR_UNIQUENESS_ACROSS_ALL_SUBTREES "uniqueness-across-all-subtrees" #define ATTR_UNIQUENESS_TOP_ENTRY_OC "uniqueness-top-entry-oc" #define ATTR_UNIQUENESS_SUBTREE_ENTRIES_OC "uniqueness-subtree-entries-oc" @@ -135,7 +137,11 @@ free_uniqueness_config(struct attr_uniqueness_config *config) for (i = 0; config->subtrees && config->subtrees[i]; i++) { slapi_sdn_free(&config->subtrees[i]); } + for (i = 0; config->exclude_subtrees && config->exclude_subtrees[i]; i++) { + slapi_sdn_free(&config->exclude_subtrees[i]); + } slapi_ch_free((void **) &config->subtrees); + slapi_ch_free((void **) &config->exclude_subtrees); slapi_ch_free_string((char **) &config->top_entry_oc); slapi_ch_free_string((char **) &config->subtree_entries_oc); } @@ -147,6 +153,7 @@ free_uniqueness_config(struct attr_uniqueness_config *config) * uniqueness-attribute-name: uid * uniqueness-subtrees: dc=people,dc=example,dc=com * uniqueness-subtrees: dc=sales, dc=example,dc=com + * uniqueness-exclude-subtrees: dc=machines, dc=examples, dc=com * uniqueness-across-all-subtrees: on * * or @@ -256,6 +263,27 @@ uniqueness_entry_to_config(Slapi_PBlock *pb, Slapi_Entry *config_entry) values = NULL; } + /* Subtrees where uniqueness is explicitly ignored */ + values = slapi_entry_attr_get_charray(config_entry, ATTR_UNIQUENESS_EXCLUDE_SUBTREES); + if (values) { + for (i = 0; values && values[i]; i++); + /* slapi_ch_calloc never returns NULL unless the 2 args are 0 or negative. */ + tmp_config->exclude_subtrees = (Slapi_DN **) slapi_ch_calloc(i + 1, sizeof (Slapi_DN *)); + /* copy the valid subtree DN into the config */ + for (i = 0, nb_subtrees = 0; values && values[i]; i++) { + if (slapi_dn_syntax_check(pb, values[i], 1)) { /* syntax check failed */ + slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Config info: Invalid DN (skipped): %s\n", values[i]); + continue; + } + tmp_config->exclude_subtrees[nb_subtrees] = slapi_sdn_new_dn_byval(values[i]); + nb_subtrees++; + + } + + slapi_ch_array_free(values); + values = NULL; + } + /* Uniqueness may be enforced accross all the subtrees, by default it is not */ tmp_config->unique_in_all_subtrees = slapi_entry_attr_get_bool(config_entry, ATTR_UNIQUENESS_ACROSS_ALL_SUBTREES); @@ -376,6 +404,7 @@ uniqueness_entry_to_config(Slapi_PBlock *pb, Slapi_Entry *config_entry) fp++; } + /* Only ensure subtrees are set, no need to check excluded subtrees as setting exclusion without actual subtrees make little sense */ if (tmp_config->subtrees == NULL) { /* Uniqueness is enforced on entries matching objectclass */ if (tmp_config->subtree_entries_oc == NULL) { @@ -569,7 +598,7 @@ create_filter(const char **attributes, const struct berval *value, const char *r static int search(Slapi_DN *baseDN, const char **attrNames, Slapi_Attr *attr, struct berval **values, const char *requiredObjectClass, - Slapi_DN *target) + Slapi_DN *target, Slapi_DN **excludes) { int result; @@ -604,7 +633,7 @@ search(Slapi_DN *baseDN, const char **attrNames, Slapi_Attr *attr, { result = search_one_berval(baseDN, attrNames, slapi_value_get_berval(v), - requiredObjectClass, target); + requiredObjectClass, target, excludes); } } else @@ -612,7 +641,7 @@ search(Slapi_DN *baseDN, const char **attrNames, Slapi_Attr *attr, for (;*values != NULL && LDAP_SUCCESS == result; values++) { result = search_one_berval(baseDN, attrNames, *values, requiredObjectClass, - target); + target, excludes); } } @@ -628,7 +657,7 @@ search(Slapi_DN *baseDN, const char **attrNames, Slapi_Attr *attr, static int search_one_berval(Slapi_DN *baseDN, const char **attrNames, const struct berval *value, const char *requiredObjectClass, - Slapi_DN *target) + Slapi_DN *target, Slapi_DN **excludes) { int result; char *filter; @@ -695,8 +724,28 @@ search_one_berval(Slapi_DN *baseDN, const char **attrNames, */ if (!target || slapi_sdn_compare(slapi_entry_get_sdn(*entries), target) != 0) { + int i; result = LDAP_CONSTRAINT_VIOLATION; - break; + if (excludes == NULL || *excludes == NULL) + { + break; + } + + /* Do the same check for excluded subtrees as resulted entries may have matched them */ + for (i = 0;excludes && excludes[i]; i++) + { + Slapi_DN *entry_dn = slapi_entry_get_sdn(*entries); + if (slapi_sdn_issuffix(entry_dn, excludes[i])) + { + result = LDAP_SUCCESS; + break; + } + } + + if (result == LDAP_CONSTRAINT_VIOLATION) + { + break; + } } } @@ -734,7 +783,7 @@ search_one_berval(Slapi_DN *baseDN, const char **attrNames, * LDAP_OPERATIONS_ERROR - a server failure. */ static int -searchAllSubtrees(Slapi_DN **subtrees, const char **attrNames, +searchAllSubtrees(Slapi_DN **subtrees, Slapi_DN **exclude_subtrees, const char **attrNames, Slapi_Attr *attr, struct berval **values, const char *requiredObjectClass, Slapi_DN *dn, PRBool unique_in_all_subtrees) { @@ -762,6 +811,22 @@ searchAllSubtrees(Slapi_DN **subtrees, const char **attrNames, return result; } } + + /* If DN is in the excluded subtrees, we should ignore it in any case, not only + * in the case of uniqueness in all subtrees. */ + if (exclude_subtrees != NULL) + { + PRBool in_a_subtree = PR_FALSE; + for (i = 0;exclude_subtrees && exclude_subtrees[i]; i++) { + if (slapi_sdn_issuffix(dn, exclude_subtrees[i])) { + in_a_subtree = PR_TRUE; + break; + } + } + if (in_a_subtree) { + return result; + } + } /* * For each DN in the managed list, do uniqueness checking if @@ -775,7 +840,7 @@ searchAllSubtrees(Slapi_DN **subtrees, const char **attrNames, * worry about that here. */ if (unique_in_all_subtrees || slapi_sdn_issuffix(dn, sufdn)) { - result = search(sufdn, attrNames, attr, values, requiredObjectClass, dn); + result = search(sufdn, attrNames, attr, values, requiredObjectClass, dn, exclude_subtrees); if (result) break; } } @@ -865,7 +930,7 @@ getArguments(Slapi_PBlock *pb, char **attrName, char **markerObjectClass, static int findSubtreeAndSearch(Slapi_DN *parentDN, const char **attrNames, Slapi_Attr *attr, struct berval **values, const char *requiredObjectClass, Slapi_DN *target, - const char *markerObjectClass) + const char *markerObjectClass, Slapi_DN **excludes) { int result = LDAP_SUCCESS; Slapi_PBlock *spb = NULL; @@ -883,7 +948,7 @@ findSubtreeAndSearch(Slapi_DN *parentDN, const char **attrNames, Slapi_Attr *att * to have the attribute already. */ result = search(curpar, attrNames, attr, values, requiredObjectClass, - target); + target, excludes); break; } newpar = slapi_sdn_new(); @@ -995,11 +1060,11 @@ preop_add(Slapi_PBlock *pb) /* Subtree defined by location of marker object class */ result = findSubtreeAndSearch(sdn, attrNames, attr, NULL, requiredObjectClass, sdn, - markerObjectClass); + markerObjectClass, config->exclude_subtrees); } else { /* Subtrees listed on invocation line */ - result = searchAllSubtrees(config->subtrees, attrNames, attr, NULL, + result = searchAllSubtrees(config->subtrees, config->exclude_subtrees, attrNames, attr, NULL, requiredObjectClass, sdn, config->unique_in_all_subtrees); } if (result != LDAP_SUCCESS) { @@ -1164,11 +1229,11 @@ preop_modify(Slapi_PBlock *pb) /* Subtree defined by location of marker object class */ result = findSubtreeAndSearch(sdn, attrNames, NULL, mod->mod_bvalues, requiredObjectClass, - sdn, markerObjectClass); + sdn, markerObjectClass, config->exclude_subtrees); } else { /* Subtrees listed on invocation line */ - result = searchAllSubtrees(config->subtrees, attrNames, NULL, + result = searchAllSubtrees(config->subtrees, config->exclude_subtrees, attrNames, NULL, mod->mod_bvalues, requiredObjectClass, sdn, config->unique_in_all_subtrees); } } @@ -1326,11 +1391,11 @@ preop_modrdn(Slapi_PBlock *pb) /* Subtree defined by location of marker object class */ result = findSubtreeAndSearch(slapi_entry_get_sdn(e), attrNames, attr, NULL, requiredObjectClass, sdn, - markerObjectClass); + markerObjectClass, config->exclude_subtrees); } else { /* Subtrees listed on invocation line */ - result = searchAllSubtrees(config->subtrees, attrNames, attr, NULL, + result = searchAllSubtrees(config->subtrees, config->exclude_subtrees, attrNames, attr, NULL, requiredObjectClass, sdn, config->unique_in_all_subtrees); } if (result != LDAP_SUCCESS) {