From 2a181763d0cff5f31dc18f3e71f79dd815906c09 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Fri, 22 Feb 2019 13:46:48 -0500 Subject: [PATCH] Ticket 50238 - Failed modrdn can corrupt entry cache Bug Description: Under certain conditions (found under IPA) when a backend transaction plugin fails and causes a modrdn operation to fail the entry cache no longer contains the original/pre entry, but instead it has the post modrdn'ed entry with the original entry's ID Fix Description: Upon failure, if the post entry is in the cache, then swap it out with the original entry. https://pagure.io/389-ds-base/issue/50238 Reviewed by: firstyear, spichugi, & tboardaz (Thanks!!!) --- dirsrvtests/tests/suites/betxns/betxn_test.py | 57 +++++++++++++++++++ ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 16 ++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/dirsrvtests/tests/suites/betxns/betxn_test.py b/dirsrvtests/tests/suites/betxns/betxn_test.py index 175496495..48181a9ea 100644 --- a/dirsrvtests/tests/suites/betxns/betxn_test.py +++ b/dirsrvtests/tests/suites/betxns/betxn_test.py @@ -8,6 +8,7 @@ # import pytest import six +import ldap from lib389.tasks import * from lib389.utils import * from lib389.topologies import topology_st @@ -248,6 +249,62 @@ def test_betxn_memberof(topology_st, dynamic_plugins): log.info('test_betxn_memberof: PASSED') +def test_betxn_modrdn_memberof(topology_st): + """Test modrdn operartions and memberOf + + :id: 70d0b96e-b693-4bf7-bbf5-102a66ac5994 + + :setup: Standalone instance + + :steps: 1. Enable and configure memberOf plugin + 2. Set memberofgroupattr="member" and memberofAutoAddOC="nsContainer" + 3. Create group and user outside of memberOf plugin scope + 4. Do modrdn to move group into scope + 5. Do modrdn to move group into scope (again) + + :expectedresults: + 1. memberOf plugin plugin should be ON + 2. Set memberofgroupattr="member" and memberofAutoAddOC="nsContainer" should PASS + 3. Creating group and user should PASS + 4. Modrdn should fail with objectclass violation + 5. Second modrdn should also fail with objectclass violation + """ + + peoplebase = 'ou=people,%s' % DEFAULT_SUFFIX + memberof = MemberOfPlugin(topology_st.standalone) + memberof.enable() + memberof.set_autoaddoc('nsContainer') # Bad OC + memberof.set('memberOfEntryScope', peoplebase) + memberof.set('memberOfAllBackends', 'on') + topology_st.standalone.restart() + + groups = Groups(topology_st.standalone, DEFAULT_SUFFIX) + group = groups.create(properties={ + 'cn': 'group', + }) + + # Create user and add it to group + users = UserAccounts(topology_st.standalone, basedn=DEFAULT_SUFFIX) + user = users.create(properties=TEST_USER_PROPERTIES) + if not ds_is_older('1.3.7'): + user.remove('objectClass', 'nsMemberOf') + + group.add_member(user.dn) + + # Attempt modrdn that should fail, but the original entry should stay in the cache + with pytest.raises(ldap.OBJECTCLASS_VIOLATION): + group.rename('cn=group_to_people', newsuperior=peoplebase) + + # Should fail, but not with NO_SUCH_OBJECT as the original entry should still be in the cache + with pytest.raises(ldap.OBJECTCLASS_VIOLATION): + group.rename('cn=group_to_people', newsuperior=peoplebase) + + # + # Done + # + log.info('test_betxn_modrdn_memberof: PASSED') + + if __name__ == '__main__': # Run isolated # -s for DEBUG mode diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c index 684b040b8..e4d0337d4 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c @@ -1411,14 +1411,20 @@ common_return: "operation failed, the target entry is cleared from dncache (%s)\n", slapi_entry_get_dn(ec->ep_entry)); CACHE_REMOVE(&inst->inst_dncache, bdn); CACHE_RETURN(&inst->inst_dncache, &bdn); + /* + * If the new/invalid entry (ec) is in the cache, that means we need to + * swap it out with the original entry (e) --> to undo the swap that + * modrdn_rename_entry_update_indexes() did. + */ + if (cache_is_in_cache(&inst->inst_cache, ec)) { + if (cache_replace(&inst->inst_cache, ec, e) != 0) { + slapi_log_err(SLAPI_LOG_ALERT, "ldbm_back_modrdn", + "failed to replace cache entry after error\n"); + } + } } - /* remove the new entry from the cache if the op failed - - otherwise, leave it in */ if (ec && inst) { - if (retval && cache_is_in_cache(&inst->inst_cache, ec)) { - CACHE_REMOVE(&inst->inst_cache, ec); - } CACHE_RETURN(&inst->inst_cache, &ec); } ec = NULL; -- 2.17.2