Blob Blame Raw
From d33743c8604ff4f97947dad14fddab0691e3d19e Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Thu, 1 Aug 2019 16:50:34 -0400
Subject: [PATCH] Issue 50525 - nsslapd-defaultnamingcontext does not change
 when the assigned suffix gets deleted

Bug Description:

If you delete the suffix that is set as the default naming context, the attribute
is not reset.

Also using dsconf to delete a backend/suffix fails if there are vlv indexes, encrypted
attributes, or replication is configured.

Fix Description:

As for the default naming context, if there is a second suffix configured, it will be
automatically set as the new default naming context, otherwise the attribute is not
modified.

For dsconf backend delete issue, it now checks and removes replication configuration
and agreements, and removes all the child entries under the backend entry.

relates: https://pagure.io/389-ds-base/issue/50525

Reviewed by: spichugi(Thanks!)
---
 .../be_del_and_default_naming_attr_test.py    | 90 +++++++++++++++++++
 ldap/servers/slapd/mapping_tree.c             | 50 ++++++-----
 src/lib389/lib389/backend.py                  | 17 ++--
 src/lib389/lib389/replica.py                  |  2 +-
 4 files changed, 132 insertions(+), 27 deletions(-)
 create mode 100644 dirsrvtests/tests/suites/mapping_tree/be_del_and_default_naming_attr_test.py

diff --git a/dirsrvtests/tests/suites/mapping_tree/be_del_and_default_naming_attr_test.py b/dirsrvtests/tests/suites/mapping_tree/be_del_and_default_naming_attr_test.py
new file mode 100644
index 000000000..34a2de2ad
--- /dev/null
+++ b/dirsrvtests/tests/suites/mapping_tree/be_del_and_default_naming_attr_test.py
@@ -0,0 +1,90 @@
+import logging
+import pytest
+import os
+from lib389._constants import DEFAULT_SUFFIX
+from lib389.topologies import topology_m1 as topo
+from lib389.backend import Backends
+from lib389.encrypted_attributes import EncryptedAttrs
+
+DEBUGGING = os.getenv("DEBUGGING", default=False)
+if DEBUGGING:
+    logging.getLogger(__name__).setLevel(logging.DEBUG)
+else:
+    logging.getLogger(__name__).setLevel(logging.INFO)
+log = logging.getLogger(__name__)
+
+SECOND_SUFFIX = 'o=namingcontext'
+THIRD_SUFFIX = 'o=namingcontext2'
+
+def test_be_delete(topo):
+    """Test that we can delete a backend that contains replication
+    configuration and encrypted attributes.  The default naming 
+    context should also be updated to reflect the next available suffix
+
+    :id: 5208f897-7c95-4925-bad0-9ceb95fee678
+    :setup: Master Instance
+    :steps:
+        1. Create second backend/suffix
+        2. Add an encrypted attribute to the default suffix
+        2. Delete default suffix
+        3. Check the nsslapd-defaultnamingcontext is updated
+        4. Delete the last backend
+        5. Check the namingcontext has not changed
+        6. Add new backend
+        7. Set default naming context
+        8. Verify the naming context is correct
+    :expectedresults:
+        1. Success
+        2. Success
+        3. Success
+        4. Success
+        5. Success
+        6. Success
+        7. Success
+        8. Success
+    """
+    
+    inst = topo.ms["master1"] 
+    
+    # Create second suffix      
+    backends = Backends(inst)
+    default_backend = backends.get(DEFAULT_SUFFIX)
+    new_backend = backends.create(properties={'nsslapd-suffix': SECOND_SUFFIX,
+                                              'name': 'namingRoot'})
+  
+    # Add encrypted attribute entry under default suffix
+    encrypt_attrs = EncryptedAttrs(inst, basedn='cn=encrypted attributes,{}'.format(default_backend.dn))
+    encrypt_attrs.create(properties={'cn': 'employeeNumber', 'nsEncryptionAlgorithm': 'AES'})
+    
+    # Delete default suffix
+    default_backend.delete()
+    
+    # Check that the default naming context is set to the new/second suffix
+    default_naming_ctx = inst.config.get_attr_val_utf8('nsslapd-defaultnamingcontext')
+    assert default_naming_ctx == SECOND_SUFFIX
+
+    # delete new backend, but the naming context should not change
+    new_backend.delete()
+
+    # Check that the default naming context is still set to the new/second suffix
+    default_naming_ctx = inst.config.get_attr_val_utf8('nsslapd-defaultnamingcontext')
+    assert default_naming_ctx == SECOND_SUFFIX
+
+    # Add new backend
+    new_backend = backends.create(properties={'nsslapd-suffix': THIRD_SUFFIX,
+                                              'name': 'namingRoot2'})
+
+    # manaully set naming context
+    inst.config.set('nsslapd-defaultnamingcontext', THIRD_SUFFIX)
+
+    # Verify naming context is correct
+    default_naming_ctx = inst.config.get_attr_val_utf8('nsslapd-defaultnamingcontext')
+    assert default_naming_ctx == THIRD_SUFFIX
+
+
+if __name__ == '__main__':
+    # Run isolated
+    # -s for DEBUG mode
+    CURRENT_FILE = os.path.realpath(__file__)
+    pytest.main(["-s", CURRENT_FILE])
+
diff --git a/ldap/servers/slapd/mapping_tree.c b/ldap/servers/slapd/mapping_tree.c
index 834949a67..25e9fb80c 100644
--- a/ldap/servers/slapd/mapping_tree.c
+++ b/ldap/servers/slapd/mapping_tree.c
@@ -1521,26 +1521,36 @@ done:
                 strcpy_unescape_value(escaped, suffix);
             }
             if (escaped && (0 == strcasecmp(escaped, default_naming_context))) {
-                int rc = _mtn_update_config_param(LDAP_MOD_DELETE,
-                                                  CONFIG_DEFAULT_NAMING_CONTEXT,
-                                                  NULL);
-                if (rc) {
-                    slapi_log_err(SLAPI_LOG_ERR,
-                                  "mapping_tree_entry_delete_callback",
-                                  "deleting config param %s failed: RC=%d\n",
-                                  CONFIG_DEFAULT_NAMING_CONTEXT, rc);
-                }
-                if (LDAP_SUCCESS == rc) {
-                    char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE] = {0};
-                    /* Removing defaultNamingContext from cn=config entry
-                     * was successful.  The remove does not reset the
-                     * global parameter.  We need to reset it separately. */
-                    if (config_set_default_naming_context(
-                            CONFIG_DEFAULT_NAMING_CONTEXT,
-                            NULL, errorbuf, CONFIG_APPLY)) {
-                        slapi_log_err(SLAPI_LOG_ERR, "mapping_tree_entry_delete_callback",
-                                      "Setting NULL to %s failed. %s\n",
-                                      CONFIG_DEFAULT_NAMING_CONTEXT, errorbuf);
+                /*
+                 * We can not delete the default naming attribute, so instead
+                 * replace it only if there is another suffix available
+                 */
+                void *node = NULL;
+                Slapi_DN *sdn;
+                sdn = slapi_get_first_suffix(&node, 0);
+                if (sdn) {
+                    char *replacement_suffix = (char *)slapi_sdn_get_dn(sdn);
+                    int rc = _mtn_update_config_param(LDAP_MOD_REPLACE,
+                                                      CONFIG_DEFAULT_NAMING_CONTEXT,
+                                                      replacement_suffix);
+                    if (rc) {
+                        slapi_log_err(SLAPI_LOG_ERR,
+                                      "mapping_tree_entry_delete_callback",
+                                      "replacing config param %s failed: RC=%d\n",
+                                      CONFIG_DEFAULT_NAMING_CONTEXT, rc);
+                    }
+                    if (LDAP_SUCCESS == rc) {
+                        char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE] = {0};
+                        /* Replacing defaultNamingContext from cn=config entry
+                         * was successful.  The replace does not reset the
+                         * global parameter.  We need to reset it separately. */
+                        if (config_set_default_naming_context(
+                                CONFIG_DEFAULT_NAMING_CONTEXT,
+                                replacement_suffix, errorbuf, CONFIG_APPLY)) {
+                            slapi_log_err(SLAPI_LOG_ERR, "mapping_tree_entry_delete_callback",
+                                          "Setting %s tp %s failed. %s\n",
+                                          CONFIG_DEFAULT_NAMING_CONTEXT, replacement_suffix, errorbuf);
+                        }
                     }
                 }
             }
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
index 6f4c8694e..4d32038f6 100644
--- a/src/lib389/lib389/backend.py
+++ b/src/lib389/lib389/backend.py
@@ -17,6 +17,7 @@ from lib389 import Entry
 from lib389._mapped_object import DSLdapObjects, DSLdapObject
 from lib389.mappingTree import MappingTrees, MappingTree
 from lib389.exceptions import NoSuchEntryError, InvalidArgumentError
+from lib389.replica import Replicas
 
 # We need to be a factor to the backend monitor
 from lib389.monitor import MonitorBackend
@@ -507,20 +508,24 @@ class Backend(DSLdapObject):
             mt = self._mts.get(selector=bename)
             # Assert the type is "backend"
             # Are these the right types....?
-            if mt.get_attr_val('nsslapd-state') != ensure_bytes('backend'):
+            if mt.get_attr_val('nsslapd-state').lower() != ensure_bytes('backend'):
                 raise ldap.UNWILLING_TO_PERFORM('Can not delete the mapping tree, not for a backend! You may need to delete this backend via cn=config .... ;_; ')
+
+            # Delete replicas first
+            try:
+                Replicas(self._instance).get(mt.get_attr_val_utf8('cn')).delete()
+            except ldap.NO_SUCH_OBJECT:
+                # No replica, no problem
+                pass
+
             # Delete our mapping tree if it exists.
             mt.delete()
         except ldap.NO_SUCH_OBJECT:
             # Righto, it's already gone! Do nothing ...
             pass
-        # Delete all our related indices
-        self._instance.index.delete_all(bename)
 
         # Now remove our children, this is all ldbm config
-        self._instance.delete_branch_s(self._dn, ldap.SCOPE_ONELEVEL)
-        # The super will actually delete ourselves.
-        super(Backend, self).delete()
+        self._instance.delete_branch_s(self._dn, ldap.SCOPE_SUBTREE)
 
     def _lint_mappingtree(self):
         """Backend lint
diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py
index cdd0a9729..7b45683d9 100644
--- a/src/lib389/lib389/replica.py
+++ b/src/lib389/lib389/replica.py
@@ -458,7 +458,7 @@ class ReplicaLegacy(object):
         try:
             self.deleteAgreements(nsuffix)
         except ldap.LDAPError as e:
-            self.log.fatal('Failed to delete replica agreements!')
+            self.log.fatal('Failed to delete replica agreements!  ' + str(e))
             raise
 
         # Delete the replica
-- 
2.21.0