dirsrvtests/tickets/ticket47823_test.py | 13 -
dirsrvtests/tickets/ticket47927_test.py | 300 ++++++++++++++++++++++++++++++++
ldap/servers/plugins/uiduniq/uid.c | 97 ++++++++--
3 files changed, 389 insertions(+), 21 deletions(-)
New commits:
commit cd9afbbeb1570adf663acb5b8f043a2012b839aa
Author: Thierry Bordaz <tbordaz(a)redhat.com>
Date: Tue Apr 28 19:35:34 2015 +0200
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
+
+
+(a)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) {