|
|
26521d |
From 933b46f1f434ac7c11e155611c91f21e31c4d6f7 Mon Sep 17 00:00:00 2001
|
|
|
26521d |
From: Mark Reynolds <mreynolds@redhat.com>
|
|
|
26521d |
Date: Thu, 21 Feb 2019 16:58:05 -0500
|
|
|
26521d |
Subject: [PATCH] Ticket 50236 - memberOf should be more robust
|
|
|
26521d |
|
|
|
26521d |
Bug Description: When doing a modrdn, or any memberOf update, if the entry
|
|
|
26521d |
already has the memberOf attribute with the same value
|
|
|
26521d |
the operation is incorrectly rejected.
|
|
|
26521d |
|
|
|
26521d |
Fix Description: If we get an error 20 (type or value exists) return success.
|
|
|
26521d |
|
|
|
26521d |
Also fixed a coding mistake that causes the wrong error
|
|
|
26521d |
code to be returned. This also required fixing the CI
|
|
|
26521d |
test to check for the new correct errro code.
|
|
|
26521d |
|
|
|
26521d |
https://pagure.io/389-ds-base/issue/50236
|
|
|
26521d |
|
|
|
26521d |
Reviewed by: firstyear, spichugi, and tbordaz (Thanks!!!)
|
|
|
26521d |
---
|
|
|
26521d |
.../suites/memberof_plugin/regression_test.py | 48 ++++++++++---------
|
|
|
26521d |
ldap/servers/plugins/memberof/memberof.c | 35 ++++++++++----
|
|
|
26521d |
2 files changed, 53 insertions(+), 30 deletions(-)
|
|
|
26521d |
|
|
|
26521d |
diff --git a/dirsrvtests/tests/suites/memberof_plugin/regression_test.py b/dirsrvtests/tests/suites/memberof_plugin/regression_test.py
|
|
|
26521d |
index 2b0c4aec2..9d0ce35ed 100644
|
|
|
26521d |
--- a/dirsrvtests/tests/suites/memberof_plugin/regression_test.py
|
|
|
26521d |
+++ b/dirsrvtests/tests/suites/memberof_plugin/regression_test.py
|
|
|
26521d |
@@ -11,13 +11,12 @@ import pytest
|
|
|
26521d |
import os
|
|
|
26521d |
import time
|
|
|
26521d |
import ldap
|
|
|
26521d |
-import subprocess
|
|
|
26521d |
from random import sample
|
|
|
26521d |
from lib389.utils import ds_is_older, ensure_list_bytes, ensure_bytes, ensure_str
|
|
|
26521d |
from lib389.topologies import topology_m1h1c1 as topo, topology_st, topology_m2 as topo_m2
|
|
|
26521d |
from lib389._constants import *
|
|
|
26521d |
from lib389.plugins import MemberOfPlugin
|
|
|
26521d |
-from lib389 import agreement, Entry
|
|
|
26521d |
+from lib389 import Entry
|
|
|
26521d |
from lib389.idm.user import UserAccount, UserAccounts, TEST_USER_PROPERTIES
|
|
|
26521d |
from lib389.idm.group import Groups, Group
|
|
|
26521d |
from lib389.replica import ReplicationManager
|
|
|
26521d |
@@ -49,7 +48,7 @@ def add_users(topo_m2, users_num, suffix):
|
|
|
26521d |
users_list = []
|
|
|
26521d |
users = UserAccounts(topo_m2.ms["master1"], suffix, rdn=None)
|
|
|
26521d |
log.info('Adding %d users' % users_num)
|
|
|
26521d |
- for num in sample(range(1000), users_num):
|
|
|
26521d |
+ for num in sample(list(range(1000)), users_num):
|
|
|
26521d |
num_ran = int(round(num))
|
|
|
26521d |
USER_NAME = 'test%05d' % num_ran
|
|
|
26521d |
user = users.create(properties={
|
|
|
26521d |
@@ -76,8 +75,8 @@ def config_memberof(server):
|
|
|
26521d |
for ent in ents:
|
|
|
26521d |
log.info('update %s to add nsDS5ReplicatedAttributeListTotal' % ent.dn)
|
|
|
26521d |
server.agreement.setProperties(agmnt_dn=ents[0].dn,
|
|
|
26521d |
- properties={RA_FRAC_EXCLUDE:'(objectclass=*) $ EXCLUDE memberOf',
|
|
|
26521d |
- RA_FRAC_EXCLUDE_TOTAL_UPDATE:'(objectclass=*) $ EXCLUDE '})
|
|
|
26521d |
+ properties={RA_FRAC_EXCLUDE: '(objectclass=*) $ EXCLUDE memberOf',
|
|
|
26521d |
+ RA_FRAC_EXCLUDE_TOTAL_UPDATE: '(objectclass=*) $ EXCLUDE '})
|
|
|
26521d |
|
|
|
26521d |
|
|
|
26521d |
def send_updates_now(server):
|
|
|
26521d |
@@ -184,14 +183,14 @@ def test_memberof_with_repl(topo):
|
|
|
26521d |
for i in range(3):
|
|
|
26521d |
CN = '%s%d' % (GROUP_CN, i)
|
|
|
26521d |
groups = Groups(M1, SUFFIX)
|
|
|
26521d |
- testgroup = groups.create(properties={'cn' : CN})
|
|
|
26521d |
+ testgroup = groups.create(properties={'cn': CN})
|
|
|
26521d |
time.sleep(2)
|
|
|
26521d |
test_groups.append(testgroup)
|
|
|
26521d |
|
|
|
26521d |
# Step 4
|
|
|
26521d |
#Now start testing by adding differnt user to differn group
|
|
|
26521d |
if not ds_is_older('1.3.7'):
|
|
|
26521d |
- test_groups[0].remove('objectClass', 'nsMemberOf')
|
|
|
26521d |
+ test_groups[0].remove('objectClass', 'nsMemberOf')
|
|
|
26521d |
|
|
|
26521d |
member_dn = test_users[0].dn
|
|
|
26521d |
grp0_dn = test_groups[0].dn
|
|
|
26521d |
@@ -211,7 +210,7 @@ def test_memberof_with_repl(topo):
|
|
|
26521d |
# Step 7
|
|
|
26521d |
for i in [grp0_dn, grp1_dn]:
|
|
|
26521d |
for inst in [M1, H1, C1]:
|
|
|
26521d |
- _find_memberof(inst, member_dn, i)
|
|
|
26521d |
+ _find_memberof(inst, member_dn, i)
|
|
|
26521d |
|
|
|
26521d |
# Step 8
|
|
|
26521d |
for i in [M1, H1, C1]:
|
|
|
26521d |
@@ -225,7 +224,7 @@ def test_memberof_with_repl(topo):
|
|
|
26521d |
# For negative testcase, we are using assertionerror
|
|
|
26521d |
for inst in [M1, H1, C1]:
|
|
|
26521d |
for i in [grp0_dn, member_dn]:
|
|
|
26521d |
- with pytest.raises(AssertionError):
|
|
|
26521d |
+ with pytest.raises(AssertionError):
|
|
|
26521d |
_find_memberof(inst, i, grp1_dn)
|
|
|
26521d |
|
|
|
26521d |
# Step 11
|
|
|
26521d |
@@ -369,7 +368,7 @@ def test_memberof_with_changelog_reset(topo_m2):
|
|
|
26521d |
'objectclass': ensure_list_bytes(['top', 'groupOfNames'])}
|
|
|
26521d |
|
|
|
26521d |
for user in users_list:
|
|
|
26521d |
- dic_of_attributes.setdefault('member',[])
|
|
|
26521d |
+ dic_of_attributes.setdefault('member', [])
|
|
|
26521d |
dic_of_attributes['member'].append(user.dn)
|
|
|
26521d |
|
|
|
26521d |
log.info('Adding the test group using async function')
|
|
|
26521d |
@@ -427,7 +426,7 @@ def rename_entry(server, cn, from_subtree, to_subtree):
|
|
|
26521d |
server.rename_s(dn, nrdn, newsuperior=to_subtree, delold=0)
|
|
|
26521d |
|
|
|
26521d |
|
|
|
26521d |
-def _find_memberof(server, user_dn=None, group_dn=None, find_result=True):
|
|
|
26521d |
+def _find_memberof_ext(server, user_dn=None, group_dn=None, find_result=True):
|
|
|
26521d |
assert (server)
|
|
|
26521d |
assert (user_dn)
|
|
|
26521d |
assert (group_dn)
|
|
|
26521d |
@@ -495,18 +494,19 @@ def test_memberof_group(topology_st):
|
|
|
26521d |
dn2 = '%s,%s' % ('uid=test_m2', SUBTREE_1)
|
|
|
26521d |
g1 = '%s,%s' % ('cn=g1', SUBTREE_1)
|
|
|
26521d |
g2 = '%s,%s' % ('cn=g2', SUBTREE_2)
|
|
|
26521d |
- _find_memberof(inst, dn1, g1, True)
|
|
|
26521d |
- _find_memberof(inst, dn2, g1, True)
|
|
|
26521d |
- _find_memberof(inst, dn1, g2, False)
|
|
|
26521d |
- _find_memberof(inst, dn2, g2, False)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn1, g1, True)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn2, g1, True)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn1, g2, False)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn2, g2, False)
|
|
|
26521d |
|
|
|
26521d |
rename_entry(inst, 'cn=g2', SUBTREE_2, SUBTREE_1)
|
|
|
26521d |
|
|
|
26521d |
g2n = '%s,%s' % ('cn=g2-new', SUBTREE_1)
|
|
|
26521d |
- _find_memberof(inst, dn1, g1, True)
|
|
|
26521d |
- _find_memberof(inst, dn2, g1, True)
|
|
|
26521d |
- _find_memberof(inst, dn1, g2n, True)
|
|
|
26521d |
- _find_memberof(inst, dn2, g2n, True)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn1, g1, True)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn2, g1, True)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn1, g2n, True)
|
|
|
26521d |
+ _find_memberof_ext(inst, dn2, g2n, True)
|
|
|
26521d |
+
|
|
|
26521d |
|
|
|
26521d |
def _config_memberof_entrycache_on_modrdn_failure(server):
|
|
|
26521d |
|
|
|
26521d |
@@ -517,11 +517,13 @@ def _config_memberof_entrycache_on_modrdn_failure(server):
|
|
|
26521d |
(ldap.MOD_REPLACE, 'memberOfEntryScope', peoplebase.encode()),
|
|
|
26521d |
(ldap.MOD_REPLACE, 'memberOfAutoAddOC', b'nsMemberOf')])
|
|
|
26521d |
|
|
|
26521d |
+
|
|
|
26521d |
def _disable_auto_oc_memberof(server):
|
|
|
26521d |
MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
|
|
|
26521d |
server.modify_s(MEMBEROF_PLUGIN_DN,
|
|
|
26521d |
[(ldap.MOD_REPLACE, 'memberOfAutoAddOC', b'nsContainer')])
|
|
|
26521d |
|
|
|
26521d |
+
|
|
|
26521d |
@pytest.mark.ds49967
|
|
|
26521d |
def test_entrycache_on_modrdn_failure(topology_st):
|
|
|
26521d |
"""This test checks that when a modrdn fails, the destination entry is not returned by a search
|
|
|
26521d |
@@ -657,7 +659,7 @@ def test_entrycache_on_modrdn_failure(topology_st):
|
|
|
26521d |
topology_st.standalone.rename_s(group2_dn, 'cn=group_in2', newsuperior=peoplebase, delold=0)
|
|
|
26521d |
topology_st.standalone.log.info("This is unexpected, modrdn should fail as the member entry have not the appropriate objectclass")
|
|
|
26521d |
assert False
|
|
|
26521d |
- except ldap.OPERATIONS_ERROR:
|
|
|
26521d |
+ except ldap.OBJECT_CLASS_VIOLATION:
|
|
|
26521d |
pass
|
|
|
26521d |
|
|
|
26521d |
# retrieve the entry having the specific description value
|
|
|
26521d |
@@ -671,9 +673,11 @@ def test_entrycache_on_modrdn_failure(topology_st):
|
|
|
26521d |
assert ent.dn == group2_dn
|
|
|
26521d |
assert found
|
|
|
26521d |
|
|
|
26521d |
+
|
|
|
26521d |
def _config_memberof_silent_memberof_failure(server):
|
|
|
26521d |
_config_memberof_entrycache_on_modrdn_failure(server)
|
|
|
26521d |
|
|
|
26521d |
+
|
|
|
26521d |
def test_silent_memberof_failure(topology_st):
|
|
|
26521d |
"""This test checks that if during a MODRDN, the memberof plugin fails
|
|
|
26521d |
then MODRDN also fails
|
|
|
26521d |
@@ -817,7 +821,7 @@ def test_silent_memberof_failure(topology_st):
|
|
|
26521d |
topology_st.standalone.rename_s(group2_dn, 'cn=group_in2', newsuperior=peoplebase, delold=0)
|
|
|
26521d |
topology_st.standalone.log.info("This is unexpected, modrdn should fail as the member entry have not the appropriate objectclass")
|
|
|
26521d |
assert False
|
|
|
26521d |
- except ldap.OPERATIONS_ERROR:
|
|
|
26521d |
+ except ldap.OBJECT_CLASS_VIOLATION:
|
|
|
26521d |
pass
|
|
|
26521d |
|
|
|
26521d |
# Check the those entries have not memberof
|
|
|
26521d |
@@ -843,7 +847,7 @@ def test_silent_memberof_failure(topology_st):
|
|
|
26521d |
except ldap.OPERATIONS_ERROR:
|
|
|
26521d |
pass
|
|
|
26521d |
|
|
|
26521d |
- # Check the those entries have not memberof
|
|
|
26521d |
+ # Check the those entries do not have memberof
|
|
|
26521d |
for i in (4, 5):
|
|
|
26521d |
user_dn = 'cn=user%d,%s' % (i, peoplebase)
|
|
|
26521d |
ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
|
|
|
26521d |
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
|
|
|
26521d |
index 329a845a7..b54eb3977 100644
|
|
|
26521d |
--- a/ldap/servers/plugins/memberof/memberof.c
|
|
|
26521d |
+++ b/ldap/servers/plugins/memberof/memberof.c
|
|
|
26521d |
@@ -919,8 +919,7 @@ memberof_postop_modrdn(Slapi_PBlock *pb)
|
|
|
26521d |
* entry that is being renamed. */
|
|
|
26521d |
for (i = 0; configCopy.groupattrs && configCopy.groupattrs[i]; i++) {
|
|
|
26521d |
if (0 == slapi_entry_attr_find(post_e, configCopy.groupattrs[i], &attr)) {
|
|
|
26521d |
- if ((ret = memberof_moddn_attr_list(pb, &configCopy, pre_sdn,
|
|
|
26521d |
- post_sdn, attr) != 0)) {
|
|
|
26521d |
+ if ((ret = memberof_moddn_attr_list(pb, &configCopy, pre_sdn, post_sdn, attr)) != 0) {
|
|
|
26521d |
slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
|
|
|
26521d |
"memberof_postop_modrdn - Update failed for (%s), error (%d)\n",
|
|
|
26521d |
slapi_sdn_get_dn(pre_sdn), ret);
|
|
|
26521d |
@@ -1720,12 +1719,32 @@ memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config, int mod_o
|
|
|
26521d |
replace_mod.mod_values = replace_val;
|
|
|
26521d |
}
|
|
|
26521d |
rc = memberof_add_memberof_attr(mods, op_to, config->auto_add_oc);
|
|
|
26521d |
- if (rc == LDAP_NO_SUCH_ATTRIBUTE) {
|
|
|
26521d |
- /* the memberof values to be replaced do not exist
|
|
|
26521d |
- * just add the new values */
|
|
|
26521d |
- mods[0] = mods[1];
|
|
|
26521d |
- mods[1] = NULL;
|
|
|
26521d |
- rc = memberof_add_memberof_attr(mods, op_to, config->auto_add_oc);
|
|
|
26521d |
+ if (rc == LDAP_NO_SUCH_ATTRIBUTE || rc == LDAP_TYPE_OR_VALUE_EXISTS) {
|
|
|
26521d |
+ if (rc == LDAP_TYPE_OR_VALUE_EXISTS) {
|
|
|
26521d |
+ /*
|
|
|
26521d |
+ * For some reason the new modrdn value is present, so retry
|
|
|
26521d |
+ * the delete by itself and ignore the add op by tweaking
|
|
|
26521d |
+ * the mod array.
|
|
|
26521d |
+ */
|
|
|
26521d |
+ mods[1] = NULL;
|
|
|
26521d |
+ rc = memberof_add_memberof_attr(mods, op_to, config->auto_add_oc);
|
|
|
26521d |
+ } else {
|
|
|
26521d |
+ /*
|
|
|
26521d |
+ * The memberof value to be replaced does not exist so just
|
|
|
26521d |
+ * add the new value. Shuffle the mod array to apply only
|
|
|
26521d |
+ * the add operation.
|
|
|
26521d |
+ */
|
|
|
26521d |
+ mods[0] = mods[1];
|
|
|
26521d |
+ mods[1] = NULL;
|
|
|
26521d |
+ rc = memberof_add_memberof_attr(mods, op_to, config->auto_add_oc);
|
|
|
26521d |
+ if (rc == LDAP_TYPE_OR_VALUE_EXISTS) {
|
|
|
26521d |
+ /*
|
|
|
26521d |
+ * The entry already has the expected memberOf value, no
|
|
|
26521d |
+ * problem just return success.
|
|
|
26521d |
+ */
|
|
|
26521d |
+ rc = LDAP_SUCCESS;
|
|
|
26521d |
+ }
|
|
|
26521d |
+ }
|
|
|
26521d |
}
|
|
|
26521d |
}
|
|
|
26521d |
}
|
|
|
26521d |
--
|
|
|
26521d |
2.17.2
|
|
|
26521d |
|