zrhoffman / rpms / 389-ds-base

Forked from rpms/389-ds-base 3 years ago
Clone

Blame SOURCES/0001-Ticket-50238-Failed-modrdn-can-corrupt-entry-cache.patch

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