Bump version to 2.8.0-4

- Resolves: RHEL-117050 - Replication online reinitialization of a large database gets stalled. [rhel-9]
- Resolves: RHEL-123279 - The new ipahealthcheck test ipahealthcheck.ds.backends.BackendsCheck raises CRITICAL issue [rhel-9]
- Resolves: RHEL-140275 - ipa-healthcheck is complaining about missing or incorrectly configured system indexes. [rhel-9]
- Resolves: RHEL-142980 - Scalability issue of replication online initialization with large database [rhel-9]
- Resolves: RHEL-146899 - memory corruption in alias entry plugin [rhel-9]
- Resolves: RHEL-147212 - Access logs are not getting deleted as configured. [rhel-9]
- Resolves: RHEL-150907 - Remove memberof_del_dn_from_groups from MemberOf plugin [rhel-9]
This commit is contained in:
Viktor Ashirov 2026-02-20 15:52:44 +01:00
parent d354d3760f
commit 1ef7deadc0
30 changed files with 5500 additions and 70 deletions

View File

@ -1,31 +0,0 @@
From 4eea3051931f552ee1df36e70c7278aaa36124a1 Mon Sep 17 00:00:00 2001
From: progier389 <progier@redhat.com>
Date: Mon, 5 Jan 2026 14:38:38 +0100
Subject: [PATCH] Issue 7166 - db_config_set asserts because of dynamic list
(#7167)
Avoid assertion in db_config_set when args does not contains dynamic list attributes
Issue: #7166
Reviewed by: @tbordaz (Thanks!)
---
src/lib389/lib389/cli_conf/backend.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py
index dcf57d006..f8735e08e 100644
--- a/src/lib389/lib389/cli_conf/backend.py
+++ b/src/lib389/lib389/cli_conf/backend.py
@@ -542,7 +542,7 @@ def db_config_set(inst, basedn, log, args):
did_something = False
replace_list = []
- if args.enable_dynamic_lists and args.disable_dynamic_lists:
+ if getattr(args,'enable_dynamic_lists', None) and getattr(args, 'disable_dynamic_lists', None):
raise ValueError("You can not enable and disable dynamic lists at the same time")
for attr, value in list(attrs.items()):
--
2.52.0

View File

@ -1,4 +1,4 @@
From ed02c78055406b020089947459954142df3b1fea Mon Sep 17 00:00:00 2001
From c5aab4f8ba822572daa9ef69a0109577bf2147e1 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Tue, 20 Jan 2026 09:52:47 +0100
Subject: [PATCH] Issue 7189 - DSBLE0007 generates incorrect remediation

View File

@ -0,0 +1,48 @@
From 327ebf93decaa19bd3919afaeba0dc122eef1990 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Mon, 12 Jan 2026 13:53:05 -0500
Subject: [PATCH] Issue 7184 - argparse.HelpFormatter _format_actions_usage()
is deprecated
Description:
_format_actions_usage() was removed in python 3.15. Instead we can use
_get_actions_usage_parts() but it also behaves differently between
python 3.14 and 3.15 so we need special handling.
Relates: https://github.com/389ds/389-ds-base/issues/7184
Reviewed by: spichugi(Thanks!)
---
src/lib389/lib389/cli_base/__init__.py | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/lib389/lib389/cli_base/__init__.py b/src/lib389/lib389/cli_base/__init__.py
index 06b8f9964..f1055aadc 100644
--- a/src/lib389/lib389/cli_base/__init__.py
+++ b/src/lib389/lib389/cli_base/__init__.py
@@ -413,7 +413,20 @@ class CustomHelpFormatter(argparse.HelpFormatter):
def _format_usage(self, usage, actions, groups, prefix):
usage = super(CustomHelpFormatter, self)._format_usage(usage, actions, groups, prefix)
- formatted_options = self._format_actions_usage(parent_arguments, [])
+
+ if sys.version_info < (3, 13):
+ # Use _format_actions_usage() for Python 3.12 and earlier
+ formatted_options = self._format_actions_usage(parent_arguments, [])
+ else:
+ # Use _get_actions_usage_parts() for Python 3.13 and later
+ action_parts = self._get_actions_usage_parts(parent_arguments, [])
+ if sys.version_info >= (3, 15):
+ # Python 3.15 returns a tuple (list of actions, count of actions)
+ formatted_options = ' '.join(action_parts[0])
+ else:
+ # Python 3.13 and 3.14 return a list of actions
+ formatted_options = ' '.join(action_parts)
+
# If formatted_options already in usage - remove them
if formatted_options in usage:
usage = usage.replace(f' {formatted_options}', '')
--
2.52.0

View File

@ -1,4 +1,4 @@
From 819a5898d1a4d913f786086f374693c306446abb Mon Sep 17 00:00:00 2001
From dbabbe6a3c42b979d14960b355925c9748371cad Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Fri, 30 Jan 2026 12:00:13 +0100
Subject: [PATCH] Issue 7027 - (2nd) 389-ds-base OpenScanHub Leaks Detected

View File

@ -0,0 +1,197 @@
From 39d47ca91e866d30b35dca41d477ad4ee07f8a14 Mon Sep 17 00:00:00 2001
From: progier389 <progier@redhat.com>
Date: Mon, 2 Feb 2026 15:39:18 +0100
Subject: [PATCH] Issue 7213 - MDB_BAD_VALSIZE error while handling VLV (#7214)
* Issue 7213 - MDB_BAD_VALSIZE error while handling VLV
Avoid failing lmdb operation when handling VLV index by truncating the key so that key+data is small enough.
Issue: #7213
Reviewed by: @mreynolds389 , @vashirov (Thanks!)
Assisted by: Claude A/I
(cherry picked from commit 5ebce22d4214bec5ed94ad84c4448164be99389a)
---
.../tests/suites/vlv/regression_test.py | 110 ++++++++++++++++++
.../slapd/back-ldbm/db-mdb/mdb_layer.c | 5 +
ldap/servers/slapd/back-ldbm/vlv.c | 7 +-
3 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/dirsrvtests/tests/suites/vlv/regression_test.py b/dirsrvtests/tests/suites/vlv/regression_test.py
index c8db94b19..1cc03c303 100644
--- a/dirsrvtests/tests/suites/vlv/regression_test.py
+++ b/dirsrvtests/tests/suites/vlv/regression_test.py
@@ -1178,6 +1178,116 @@ def test_vlv_with_mr(vlv_setup_with_uid_mr):
+def test_vlv_long_attribute_value(topology_st, request):
+ """
+ Test VLV with an entry containing a very long attribute value (2K).
+
+ :id: 99126fa4-003e-11f1-b7d6-c85309d5c3e3
+ :setup: Standalone instance.
+ :steps:
+ 1. Cleanup leftover from previous tests
+ 2. Create VLV search and index on cn attribute
+ 3. Reindex VLV
+ 4. Add an entry with a cn attribute having 2K character value
+ 5. Verify the entry was added successfully
+ 6. Perform a VLV search to ensure it still works
+ 7. Add another entry with a cn attribute having 2K character value
+ 8. Verify the entry was added successfully
+ 9. Perform a VLV search to ensure it still works
+ :expectedresults:
+ 1. Should Success.
+ 2. Should Success.
+ 3. Should Success.
+ 4. Should Success.
+ 5. Should Success.
+ 6. Should Success.
+ 7. Should Success.
+ 8. Should Success.
+ 9. Should Success.
+ """
+ inst = topology_st.standalone
+ reindex_task = Tasks(inst)
+
+ users_to_delete = []
+
+ def fin():
+ cleanup(inst)
+ # Clean the added users
+ for user in users_to_delete:
+ user.delete()
+
+ if not DEBUGGING:
+ request.addfinalizer(fin)
+
+ # Clean previous tests leftover
+ fin()
+
+ # Create VLV search and index
+ vlv_search, vlv_index = create_vlv_search_and_index(inst)
+ assert reindex_task.reindex(
+ suffix=DEFAULT_SUFFIX,
+ attrname=vlv_index.rdn,
+ args={TASK_WAIT: True},
+ vlv=True
+ ) == 0
+
+ # Add a few regular users first
+ add_users(inst, 10)
+
+ # Create a very long cn value (2K characters)
+ long_cn_value = 'a' * 2048 + '1'
+
+ # Add an entry with the long cn attribute
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
+ user_properties = {
+ 'uid': 'longcnuser1',
+ 'cn': long_cn_value,
+ 'sn': 'user1',
+ 'uidNumber': '99999',
+ 'gidNumber': '99999',
+ 'homeDirectory': '/home/longcnuser1'
+ }
+ user = users.create(properties=user_properties)
+ users_to_delete.append(user);
+
+ # Verify the entry was created and has the long cn value
+ entry = user.get_attr_vals_utf8('cn')
+ assert entry[0] == long_cn_value
+ log.info(f'Successfully created user with cn length: {len(entry[0])}')
+
+ # Perform VLV search to ensure VLV still works with long attribute values
+ conn = open_new_ldapi_conn(inst.serverid)
+ count = len(conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=*)"))
+ assert count > 0
+ log.info(f'VLV search successful with {count} entries including entry with 2K cn value')
+
+ # Add another entry with the long cn attribute
+ long_cn_value = 'a' * 2048 + '2'
+
+ user_properties = {
+ 'uid': 'longcnuser2',
+ 'cn': long_cn_value,
+ 'sn': 'user2',
+ 'uidNumber': '99998',
+ 'gidNumber': '99998',
+ 'homeDirectory': '/home/longcnuser2'
+ }
+ user = users.create(properties=user_properties)
+ users_to_delete.append(user);
+
+ # Verify the entry was created and has the long cn value
+ entry = user.get_attr_vals_utf8('cn')
+ assert entry[0] == long_cn_value
+ log.info(f'Successfully created user with cn length: {len(entry[0])}')
+
+ # Perform VLV search to ensure VLV still works with long attribute values
+ conn = open_new_ldapi_conn(inst.serverid)
+ count = len(conn.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=*)"))
+ assert count > 1
+ log.info(f'VLV search successful with {count} entries including entry with 2K cn value')
+
+
+
if __name__ == "__main__":
# Run isolated
# -s for DEBUG mode
diff --git a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
index c6e9f8b01..ffbd6609f 100644
--- a/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
+++ b/ldap/servers/slapd/back-ldbm/db-mdb/mdb_layer.c
@@ -2138,10 +2138,15 @@ void *dbmdb_recno_cache_build(void *arg)
recno = 1;
}
while (rc == 0) {
+ struct ldbminfo *li = (struct ldbminfo *)rcctx->cursor->be->be_database->plg_private;
slapi_log_err(SLAPI_LOG_DEBUG, "dbmdb_recno_cache_build", "recno=%d\n", recno);
if (recno % RECNO_CACHE_INTERVAL == 1) {
/* Prepare the cache data */
len = sizeof(*rce) + data.mv_size + key.mv_size;
+ if (len > li->li_max_key_len) {
+ key.mv_size = li->li_max_key_len - data.mv_size - sizeof(*rce);
+ len = li->li_max_key_len;
+ }
rce = (dbmdb_recno_cache_elmt_t*)slapi_ch_malloc(len);
rce->len = len;
rce->recno = recno;
diff --git a/ldap/servers/slapd/back-ldbm/vlv.c b/ldap/servers/slapd/back-ldbm/vlv.c
index 18431799c..a1cd226f4 100644
--- a/ldap/servers/slapd/back-ldbm/vlv.c
+++ b/ldap/servers/slapd/back-ldbm/vlv.c
@@ -866,6 +866,7 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
struct vlv_key *key = NULL;
dbi_val_t data = {0};
dblayer_private *priv = NULL;
+ size_t key_size_limit = li->li_max_key_len - sizeof(entry->ep_id);
slapi_pblock_get(pb, SLAPI_BACKEND, &be);
priv = (dblayer_private *)li->li_dblayer_private;
@@ -886,6 +887,10 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
return rc;
}
+ /* Truncate the key if it is too long */
+ if (key->key.size > key_size_limit) {
+ key->key.size = key_size_limit;
+ }
if (NULL != txn) {
db_txn = txn->back_txn_txn;
} else {
@@ -930,7 +935,7 @@ do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct
if (txn && txn->back_special_handling_fn) {
rc = txn->back_special_handling_fn(be, BTXNACT_VLV_DEL, db, &key->key, &data, txn);
} else {
- rc = dblayer_db_op(be, db, db_txn, DBI_OP_DEL, &key->key, NULL);
+ rc = dblayer_db_op(be, db, db_txn, DBI_OP_DEL, &key->key, &data);
}
if (rc == 0) {
if (txn && txn->back_special_handling_fn) {
--
2.52.0

View File

@ -0,0 +1,43 @@
From 70d323d4d9ca4d1d682ab4a273178946922c6929 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Fri, 24 Jan 2025 11:10:45 +0100
Subject: [PATCH] Issue 6542 - RPM build errors on Fedora 42
Bug Description:
Fedora 42 has unified `/bin` and `/sbin`: https://fedoraproject.org/wiki/Changes/Unify_bin_and_sbin
https://docs.fedoraproject.org/en-US/packaging-guidelines/#_merged_file_system_layout
This change causes RPM build to fail with:
```
RPM build errors:
File not found: /builddir/build/BUILD/389-ds-base-3.1.2-build/BUILDROOT/usr/bin/openldap_to_ds
```
Fix Description:
Patch `setup.py.in` based on Fedora or RHEL version that support unified
`/bin` and `/sbin`.
Fixes: https://github.com/389ds/389-ds-base/issues/6542
Reviewed by: @mreynolds389, @droideck (Thanks!)
---
rpm/389-ds-base.spec.in | 3 +++
1 file changed, 3 insertions(+)
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
index 258d94698..44a158ce5 100644
--- a/rpm/389-ds-base.spec.in
+++ b/rpm/389-ds-base.spec.in
@@ -522,6 +522,9 @@ autoreconf -fiv
%if 0%{?rhel} > 7 || 0%{?fedora}
# lib389
+%if 0%{?fedora} >= 42 || 0%{?rhel} >= 11
+ sed -i "/prefix/s@sbin@bin@g" src/lib389/setup.py.in
+%endif
make src/lib389/setup.py
pushd ./src/lib389
%py3_build
--
2.52.0

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
From 41c72bf9900a7e28e0f589384f6e0ce292607e9d Mon Sep 17 00:00:00 2001
From 7a45b50bf3d9cfc8f7e805379fcb39d5669d6c8b Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Thu, 5 Feb 2026 12:17:06 +0100
Subject: [PATCH] Issue 7223 - Revert index scan limits for system indexes
@ -729,7 +729,7 @@ index d3c9ccf35..1d9be4683 100644
'nsslapd-import-cachesize',
'nsslapd-idl-switch',
diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py
index f8735e08e..ec9b5debe 100644
index dcf57d006..80008d22d 100644
--- a/src/lib389/lib389/cli_conf/backend.py
+++ b/src/lib389/lib389/cli_conf/backend.py
@@ -39,7 +39,6 @@ arg_to_attr = {

View File

@ -1,4 +1,4 @@
From 450015057d84bb032996e0bd76941b1ab6e1c08c Mon Sep 17 00:00:00 2001
From a58212943da2604091e7d5a20829ab54970d41c9 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Thu, 5 Feb 2026 12:17:06 +0100
Subject: [PATCH] Issue 7223 - Add upgrade function to remove

View File

@ -0,0 +1,313 @@
From 9a9446b9bafe25eacf97039e66d6ef19d366315c Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Thu, 5 Feb 2026 12:17:06 +0100
Subject: [PATCH] Issue 7223 - Add upgrade function to remove ancestorid index
config entry
Description:
Add `upgrade_remove_ancestorid_index_config()` function that removes:
* ancestorid from `cn=default indexes`
* ancestorid index config entries from each backend's `cn=index`
Also remove ancestorid index configuration from template-dse.ldif.
Relates: https://github.com/389ds/389-ds-base/issues/7223
Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
---
.../healthcheck/health_system_indexes_test.py | 85 +++++++++++
ldap/ldif/template-dse.ldif.in | 8 --
ldap/servers/slapd/upgrade.c | 133 +++++++++++++++++-
3 files changed, 214 insertions(+), 12 deletions(-)
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
index b0d7a99ec..4b0c58835 100644
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
@@ -501,6 +501,91 @@ def test_upgrade_removes_parentid_scanlimit(topology_st):
log.info("Upgrade successfully removed nsIndexIDListScanLimit from parentid index")
+ # Verify idempotency - restart again and ensure no errors
+ log.info("Restart server again to verify idempotency (no errors on second run)")
+ standalone.restart()
+ # Verify the attribute is still absent
+ scanlimit_after_second = parentid_index.get_attr_vals_utf8("nsIndexIDListScanLimit")
+ assert not scanlimit_after_second, \
+ f"nsIndexIDListScanLimit should still be absent after second restart but found: {scanlimit_after_second}"
+ log.info("Idempotency verified - no issues on second restart")
+
+
+def test_upgrade_removes_ancestorid_index_config(topology_st):
+ """Check if upgrade function removes ancestorid index config entry
+
+ :id: 3f3d6e9b-75ac-4f0d-b2ce-7204e6eacd0a
+ :setup: Standalone instance
+ :steps:
+ 1. Create DS instance
+ 2. Stop the server
+ 3. Use DSEldif to add an ancestorid index config entry
+ 4. Start the server (triggers upgrade)
+ 5. Verify ancestorid index config entry is removed
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ 4. Success
+ 5. ancestorid index config entry is no longer present
+ """
+ from lib389.dseldif import DSEldif
+
+ standalone = topology_st.standalone
+ ANCESTORID_DN = "cn=ancestorid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
+
+ log.info("Stop the server")
+ standalone.stop()
+
+ log.info("Add ancestorid index config entry using DSEldif")
+ dse_ldif = DSEldif(standalone)
+
+ # Create a fake ancestorid index entry
+ ancestorid_entry = [
+ "dn: {}\n".format(ANCESTORID_DN),
+ "objectClass: top\n",
+ "objectClass: nsIndex\n",
+ "cn: ancestorid\n",
+ "nsSystemIndex: true\n",
+ "nsIndexType: eq\n",
+ "nsMatchingRule: integerOrderingMatch\n",
+ "\n"
+ ]
+ dse_ldif.add_entry(ancestorid_entry)
+
+ # Verify it was added by re-reading dse.ldif
+ dse_ldif2 = DSEldif(standalone)
+ cn_value = dse_ldif2.get(ANCESTORID_DN, "cn")
+ assert cn_value is not None, "Failed to add ancestorid index config entry"
+ log.info(f"Added ancestorid index entry with cn: {cn_value}")
+
+ log.info("Start the server (triggers upgrade)")
+ standalone.start()
+
+ log.info("Verify ancestorid index config entry was removed by upgrade")
+ # Check via LDAP - the upgrade should have removed the entry
+ try:
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
+ # If we can get the entry, it wasn't removed - this is a failure
+ cn_after = ancestorid_index.get_attr_vals_utf8("cn")
+ assert False, f"ancestorid index config entry should have been removed but still exists: {cn_after}"
+ except Exception as e:
+ # Entry should not exist - this is expected
+ log.info(f"ancestorid index config entry correctly removed (got exception: {e})")
+
+ log.info("Upgrade successfully removed ancestorid index config entry")
+
+ # Verify idempotency - restart again and ensure no errors
+ log.info("Restart server again to verify idempotency (no errors on second run)")
+ standalone.restart()
+ # Verify the entry is still absent
+ try:
+ ancestorid_index = Index(standalone, ANCESTORID_DN)
+ cn_after_second = ancestorid_index.get_attr_vals_utf8("cn")
+ assert False, f"ancestorid index config entry should still be absent after second restart but found: {cn_after_second}"
+ except Exception as e:
+ log.info(f"Idempotency verified - ancestorid still absent after second restart (got exception: {e})")
+
if __name__ == "__main__":
# Run isolated
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index 6f97d492b..70e7a85ac 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -990,14 +990,6 @@ cn: aci
nssystemindex: true
nsindextype: pres
-dn: cn=ancestorid,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
-objectclass: top
-objectclass: nsIndex
-cn: ancestorid
-nssystemindex: true
-nsindextype: eq
-nsmatchingrule: integerOrderingMatch
-
dn: cn=cn,cn=default indexes, cn=config,cn=ldbm database,cn=plugins,cn=config
objectclass: top
objectclass: nsIndex
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
index adfec63de..d9156cae9 100644
--- a/ldap/servers/slapd/upgrade.c
+++ b/ldap/servers/slapd/upgrade.c
@@ -380,6 +380,126 @@ upgrade_remove_index_scanlimit(void)
return uresult;
}
+/*
+ * Remove ancestorid index configuration entry if present.
+ *
+ * The ancestorid index is special - it has no corresponding attribute type
+ * and should not have a DSE config entry. If an entry exists, remove it.
+ *
+ * This function removes:
+ * 1. The ancestorid entry from cn=default indexes (to prevent re-creation on startup)
+ * 2. The ancestorid entry from each backend's cn=index (if it exists)
+ */
+static upgrade_status
+upgrade_remove_ancestorid_index_config(void)
+{
+ struct slapi_pblock *pb = slapi_pblock_new();
+ Slapi_Entry **backends = NULL;
+ const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
+ const char *be_filter = "(objectclass=nsBackendInstance)";
+ upgrade_status uresult = UPGRADE_SUCCESS;
+ int rc;
+
+ /*
+ * First, remove ancestorid from cn=default indexes to prevent
+ * ldbm_instance_create_default_user_indexes() from re-creating it.
+ */
+ {
+ Slapi_PBlock *def_pb = slapi_pblock_new();
+ char *def_idx_dn = slapi_create_dn_string(
+ "cn=ancestorid,cn=default indexes,cn=config,%s", be_base_dn);
+
+ if (def_idx_dn) {
+ slapi_delete_internal_set_pb(
+ def_pb, def_idx_dn, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(def_pb);
+ slapi_pblock_get(def_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+
+ if (rc == LDAP_SUCCESS) {
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
+ "Removed 'ancestorid' from default indexes.\n");
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
+ "Failed to remove 'ancestorid' from default indexes: error %d\n", rc);
+ }
+
+ slapi_ch_free_string(&def_idx_dn);
+ }
+ slapi_pblock_destroy(def_pb);
+ }
+
+ /* Search for all backend instances */
+ slapi_search_internal_set_pb(
+ pb, be_base_dn,
+ LDAP_SCOPE_ONELEVEL,
+ be_filter, NULL, 0, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
+
+ if (backends) {
+ for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
+ const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
+ if (!be_dn || !be_name) {
+ continue;
+ }
+
+ struct slapi_pblock *idx_pb = slapi_pblock_new();
+ Slapi_Entry **idx_entries = NULL;
+ char *idx_dn = slapi_create_dn_string("cn=ancestorid,cn=index,%s",
+ be_dn);
+ char *idx_filter = "(objectclass=nsIndex)";
+
+ if (!idx_dn) {
+ slapi_pblock_destroy(idx_pb);
+ continue;
+ }
+
+ slapi_search_internal_set_pb(
+ idx_pb, idx_dn,
+ LDAP_SCOPE_BASE,
+ idx_filter, NULL, 0, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(idx_pb);
+ slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
+
+ if (idx_entries && idx_entries[0]) {
+ /* ancestorid index entry exists - delete it */
+ Slapi_PBlock *del_pb = slapi_pblock_new();
+
+ slapi_delete_internal_set_pb(
+ del_pb, idx_dn, NULL, NULL,
+ plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(del_pb);
+ slapi_pblock_get(del_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+
+ if (rc == LDAP_SUCCESS) {
+ slapi_log_err(SLAPI_LOG_NOTICE, "upgrade_remove_ancestorid_index_config",
+ "Removed 'ancestorid' index config entry in backend '%s'.\n",
+ be_name);
+ } else if (rc != LDAP_NO_SUCH_OBJECT) {
+ slapi_log_err(SLAPI_LOG_ERR, "upgrade_remove_ancestorid_index_config",
+ "Failed to remove 'ancestorid' index config entry in backend '%s': error %d\n",
+ be_name, rc);
+ }
+
+ slapi_pblock_destroy(del_pb);
+ }
+
+ slapi_ch_free_string(&idx_dn);
+ slapi_free_search_results_internal(idx_pb);
+ slapi_pblock_destroy(idx_pb);
+ }
+ }
+
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ return uresult;
+}
+
/*
* Check if parentid/ancestorid indexes are missing the integerOrderingMatch
* matching rule.
@@ -394,7 +514,7 @@ upgrade_check_id_index_matching_rule(void)
Slapi_Entry **backends = NULL;
const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
const char *be_filter = "(objectclass=nsBackendInstance)";
- const char *attrs_to_check[] = {"parentid", "ancestorid", NULL};
+ const char *attrs_to_check[] = {"parentid", NULL};
upgrade_status uresult = UPGRADE_SUCCESS;
/* Search for all backend instances */
@@ -408,8 +528,9 @@ upgrade_check_id_index_matching_rule(void)
if (backends) {
for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
+ const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
- if (!be_name) {
+ if (!be_dn || !be_name) {
continue;
}
@@ -418,8 +539,8 @@ upgrade_check_id_index_matching_rule(void)
const char *attr_name = attrs_to_check[attr_idx];
struct slapi_pblock *idx_pb = slapi_pblock_new();
Slapi_Entry **idx_entries = NULL;
- char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,cn=%s,%s",
- attr_name, be_name, be_base_dn);
+ char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
+ attr_name, be_dn);
char *idx_filter = "(objectclass=nsIndex)";
PRBool has_matching_rule = PR_FALSE;
@@ -512,6 +633,10 @@ upgrade_server(void)
return UPGRADE_FAILURE;
}
+ if (upgrade_remove_ancestorid_index_config() != UPGRADE_SUCCESS) {
+ return UPGRADE_FAILURE;
+ }
+
if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
return UPGRADE_FAILURE;
}
--
2.52.0

View File

@ -1,4 +1,4 @@
From 46154ef03c7543a452b17540460dc808805bd4b7 Mon Sep 17 00:00:00 2001
From b5bee921b7f4cfbf7a2edbe55d4291f487f4a140 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Thu, 5 Feb 2026 12:17:06 +0100
Subject: [PATCH] Issue 7223 - Detect and log index ordering mismatch during

View File

@ -1,4 +1,4 @@
From c741eceaf0ba751d95153f032826c20249838de5 Mon Sep 17 00:00:00 2001
From 04eca1fe36480561bc2f59440d971e000133d213 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Thu, 5 Feb 2026 12:17:06 +0100
Subject: [PATCH] Issue 7223 - Add dsctl index-check command for offline index
@ -24,11 +24,11 @@ Reviewed by: @progier389, @tbordaz, @droideck (Thanks!)
4 files changed, 1068 insertions(+), 29 deletions(-)
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
index b0d7a99ec..55b9d2f6d 100644
index 4b0c58835..571465562 100644
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
@@ -502,6 +502,599 @@ def test_upgrade_removes_parentid_scanlimit(topology_st):
log.info("Upgrade successfully removed nsIndexIDListScanLimit from parentid index")
@@ -587,6 +587,599 @@ def test_upgrade_removes_ancestorid_index_config(topology_st):
log.info(f"Idempotency verified - ancestorid still absent after second restart (got exception: {e})")
+def test_index_check_basic(topology_st):
@ -628,10 +628,10 @@ index b0d7a99ec..55b9d2f6d 100644
# Run isolated
# -s for DEBUG mode
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
index 258d94698..94d8c04c9 100644
index 44a158ce5..0175dfa7c 100644
--- a/rpm/389-ds-base.spec.in
+++ b/rpm/389-ds-base.spec.in
@@ -642,42 +642,45 @@ if ! getent passwd $USERNAME >/dev/null ; then
@@ -645,42 +645,45 @@ if ! getent passwd $USERNAME >/dev/null ; then
fi
# Reload our sysctl before we restart (if we can)

View File

@ -1,4 +1,4 @@
From d1886fbc7d97e49ac0b8bc5bd6ae7e3263bb0cfb Mon Sep 17 00:00:00 2001
From c1f0756b453b7ea219add236f60a72b3fc660af0 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Tue, 27 Jan 2026 14:26:29 +0100
Subject: [PATCH] Issue 7096 - (2nd) During replication online total init the

View File

@ -1,4 +1,4 @@
From 42c38a7a95e898a6185ac7c71ad89119ef406509 Mon Sep 17 00:00:00 2001
From 35237d57daf6fdf20a42c3cef27130ba70f0cdc2 Mon Sep 17 00:00:00 2001
From: Akshay Adhikari <aadhikar@redhat.com>
Date: Tue, 18 Nov 2025 21:57:10 +0530
Subject: [PATCH] Issue 7076, 6992, 6784, 6214 - Fix CI test failures (#7077)
@ -53,7 +53,7 @@ index be825efe9..e9b611439 100644
log.info(f'Backup directory is {backup_dir}')
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
index 55b9d2f6d..da7673283 100644
index 571465562..4cd4d0c70 100644
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
@@ -172,6 +172,7 @@ def test_missing_parentid(topology_st, log_buffering_enabled):

View File

@ -1,4 +1,4 @@
From b5a8ab96ec5c2a0cc0d478c5a906f3d2bfb4f6c5 Mon Sep 17 00:00:00 2001
From 04a0f5560ea741972b9c264af23b377893ab7133 Mon Sep 17 00:00:00 2001
From: Akshay Adhikari <aadhikar@redhat.com>
Date: Thu, 5 Feb 2026 15:41:09 +0530
Subject: [PATCH] Issue 7076 - Fix revert_cache() never called in modrdn

View File

@ -1,4 +1,4 @@
From cd530816544b9a583adc517db2ff34cdfa84fc43 Mon Sep 17 00:00:00 2001
From fb8acf6141b069e250be3821c1e70ee7ed448720 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Wed, 11 Feb 2026 19:53:44 +0100
Subject: [PATCH] Issue 6947 - Fix health_system_indexes_test.py
@ -8,7 +8,7 @@ Subject: [PATCH] Issue 6947 - Fix health_system_indexes_test.py
1 file changed, 1 insertion(+)
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
index da7673283..932857dd6 100644
index 4cd4d0c70..3b3651b38 100644
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
@@ -99,6 +99,7 @@ def run_healthcheck_and_flush_log(topology, instance, searched_code, json, searc

View File

@ -1,4 +1,4 @@
From 64dcdbcbba3e8e2239a2e099787c40713c8a0a7e Mon Sep 17 00:00:00 2001
From 8761b28fd2f2cf3d2e2119bdf1b348bc7d2d1834 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Mon, 9 Feb 2026 13:18:09 +0100
Subject: [PATCH] Issue 7121 - (2nd) LeakSanitizer: various leaks during

View File

@ -1,4 +1,4 @@
From eaa2077433bc3a38dee60ae5255f067aa3113691 Mon Sep 17 00:00:00 2001
From 86b15d688949197a9efe3d5fc7a7eadcdd46e115 Mon Sep 17 00:00:00 2001
From: Simon Pichugin <spichugi@redhat.com>
Date: Tue, 16 Dec 2025 15:48:35 -0800
Subject: [PATCH] Issue 7150 - Compressed access log rotations skipped,

View File

@ -1,4 +1,4 @@
From 8c7de629b83fe12a36cabe89e2b19124dcd6c937 Mon Sep 17 00:00:00 2001
From 524cae18721234e1bc8d958c89684008d62e6b30 Mon Sep 17 00:00:00 2001
From: James Chapman <jachapma@redhat.com>
Date: Thu, 5 Feb 2026 15:33:08 +0000
Subject: [PATCH] Issue 7224 - CI Test - Simplify

View File

@ -0,0 +1,92 @@
From 91e03f431d8ee0b1d316ebd7add232bf49360db2 Mon Sep 17 00:00:00 2001
From: James Chapman <jachapma@redhat.com>
Date: Thu, 12 Feb 2026 10:42:09 +0000
Subject: [PATCH] Issue 7231 - Sync repl tests fail in FIPS mode due to non
FIPS compliant crypto (#7232)
Description:
Several sync_repl tests fail when running on a FIPS enabled system. The failures
are caused by the sync repl client (Sync_persist), using TLS options and ciphers
that are not FIPS compatible.
Fix:
Update the sync repl client to use FIPS approved TLS version.
Fixes: https://github.com/389ds/389-ds-base/issues/7231
Reviewed by: @progier389, @droideck (Thank you)
---
.../tests/suites/syncrepl_plugin/basic_test.py | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
index c22331eb5..1e9a530bb 100644
--- a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
+++ b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
@@ -20,7 +20,7 @@ from lib389.idm.group import Groups
from lib389.topologies import topology_st as topology
from lib389.topologies import topology_m2 as topo_m2
from lib389.paths import Paths
-from lib389.utils import ds_is_older
+from lib389.utils import ds_is_older, is_fips
from lib389.plugins import RetroChangelogPlugin, ContentSyncPlugin, AutoMembershipPlugin, MemberOfPlugin, MemberOfSharedConfig, AutoMembershipDefinitions, MEPTemplates, MEPConfigs, ManagedEntriesPlugin, MEPTemplate
from lib389._constants import *
@@ -214,6 +214,12 @@ class Sync_persist(threading.Thread, ReconnectLDAPObject, SyncreplConsumer):
def run(self):
"""Start a sync repl client"""
ldap_connection = TestSyncer(self.inst.toLDAPURL())
+ ldap_connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
+ ldap_connection.set_option(ldap.OPT_X_TLS_CACERTFILE, os.path.join(self.inst.get_config_dir(), "ca.crt"))
+ if is_fips():
+ ldap_connection.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_X_TLS_PROTOCOL_TLS1_2)
+ ldap_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
+
ldap_connection.simple_bind_s('cn=directory manager', 'password')
ldap_search = ldap_connection.syncrepl_search(
"dc=example,dc=com",
@@ -253,6 +259,7 @@ def test_sync_repl_mep(topology, request):
5. Success
"""
inst = topology[0]
+ inst.enable_tls()
# Enable/configure retroCL
plugin = RetroChangelogPlugin(inst)
@@ -338,6 +345,7 @@ def test_sync_repl_cookie(topology, init_sync_repl_plugins, request):
5.: succeeds
"""
inst = topology[0]
+ inst.enable_tls()
# create a sync repl client and wait 5 seconds to be sure it is running
sync_repl = Sync_persist(inst)
@@ -404,6 +412,8 @@ def test_sync_repl_cookie_add_del(topology, init_sync_repl_plugins, request):
6.: succeeds
"""
inst = topology[0]
+ inst.enable_tls()
+
# create a sync repl client and wait 5 seconds to be sure it is running
sync_repl = Sync_persist(inst)
sync_repl.start()
@@ -547,6 +557,7 @@ def test_sync_repl_cenotaph(topo_m2, request):
5. Should succeeds
"""
m1 = topo_m2.ms["supplier1"]
+ m1.enable_tls()
# Enable/configure retroCL
plugin = RetroChangelogPlugin(m1)
plugin.disable()
@@ -605,7 +616,7 @@ def test_sync_repl_dynamic_plugin(topology, request):
3. Should succeeds
4. Should succeeds
"""
-
+ topology.standalone.enable_tls()
# Reset the instance in a default config
# Disable content sync plugin
topology.standalone.plugins.disable(name=PLUGIN_REPL_SYNC)
--
2.52.0

View File

@ -0,0 +1,33 @@
From bd57467f6efd8e398eb2e608331711bdd571d3ac Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Thu, 12 Feb 2026 09:58:54 -0500
Subject: [PATCH] Issue 7248 - CLI - attribute uniqueness - fix usage for
exclude subtree option
Description:
Fix typo in usage message for the exclude subtree option
relates: https://github.com/389ds/389-ds-base/issues/7248
Reviewed by: progier (Thanks!)
---
src/lib389/lib389/cli_conf/plugins/attruniq.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib389/lib389/cli_conf/plugins/attruniq.py b/src/lib389/lib389/cli_conf/plugins/attruniq.py
index bc925eb1c..26ca5d819 100644
--- a/src/lib389/lib389/cli_conf/plugins/attruniq.py
+++ b/src/lib389/lib389/cli_conf/plugins/attruniq.py
@@ -127,7 +127,7 @@ def _add_parser_args(parser):
help='Sets the DN under which the plug-in checks for uniqueness of '
'the attributes value. This attribute is multi-valued (uniqueness-subtrees)')
parser.add_argument('--exclude-subtree', nargs='+',
- help='Sets subtrees that should not excludedfrom attribute uniqueness. '
+ help='Sets subtrees that should be excluded from attribute uniqueness checks. '
'This attribute is multi-valued (uniqueness-exclude-subtrees)')
parser.add_argument('--across-all-subtrees', choices=['on', 'off'], type=str.lower,
help='If enabled (on), the plug-in checks that the attribute is unique across all subtrees '
--
2.52.0

View File

@ -0,0 +1,201 @@
From 180329d9ca672306f8e90d599890be3a6a8aa95c Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Thu, 12 Feb 2026 11:13:45 -0500
Subject: [PATCH] Issue - CLI - dsctl db2index needs some hardening with MBD
Description:
The usage for dsctl db2index was confusing. The way the attr options and
backend name were displayed it looks like the backend name could come after
the attributes, but instead the backend name was treated as an attribute.
Instead make the backend name required, and change the attribute naming to
require individual options instead of a list of values.
Relates: https://github.com/389ds/389-ds-base/issues/7250
Reviewed by: progier(Thanks!)
---
.../tests/suites/import/import_test.py | 2 +-
src/lib389/lib389/__init__.py | 40 ++++++-------------
src/lib389/lib389/cli_ctl/dbtasks.py | 37 +++++++----------
3 files changed, 27 insertions(+), 52 deletions(-)
diff --git a/dirsrvtests/tests/suites/import/import_test.py b/dirsrvtests/tests/suites/import/import_test.py
index 18caec633..058a4b3c7 100644
--- a/dirsrvtests/tests/suites/import/import_test.py
+++ b/dirsrvtests/tests/suites/import/import_test.py
@@ -512,7 +512,7 @@ def test_entry_with_escaped_characters_fails_to_import_and_index(topo, _import_c
count += 1
# Now re-index the database
topo.standalone.stop()
- topo.standalone.db2index()
+ topo.standalone.db2index(bename="userroot")
topo.standalone.start()
# Should not return error.
assert not topo.standalone.searchErrorsLog('error')
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
index 39a2852e5..ec47987be 100644
--- a/src/lib389/lib389/__init__.py
+++ b/src/lib389/lib389/__init__.py
@@ -2985,7 +2985,7 @@ class DirSrv(SimpleLDAPObject, object):
return True
- def db2index(self, bename=None, suffixes=None, attrs=None, vlvTag=None):
+ def db2index(self, bename, suffixes=None, attrs=None, vlvTag=None):
"""
@param bename - The backend name to reindex
@param suffixes - List/tuple of suffixes to reindex, currently unused
@@ -2998,34 +2998,18 @@ class DirSrv(SimpleLDAPObject, object):
if self.status():
self.log.error("db2index: Can not operate while directory server is running")
return False
- cmd = [prog, ]
- # No backend specified, do an upgrade on all backends
- # Backend and no attrs specified, reindex with all backend indexes
- # Backend and attr/s specified, reindex backend with attr/s
- if bename:
- cmd.append('db2index')
- cmd.append('-n')
- cmd.append(bename)
- if attrs:
- for attr in attrs:
- cmd.append('-t')
- cmd.append(attr)
- else:
- dse_ldif = DSEldif(self)
- indexes = dse_ldif.get_indexes(bename)
- if indexes:
- for idx in indexes:
- cmd.append('-t')
- cmd.append(idx)
+ cmd = [prog, 'db2index', '-n', bename, '-D', self.get_config_dir()]
+ if attrs:
+ for attr in attrs:
+ cmd.append('-t')
+ cmd.append(attr)
else:
- cmd.append('upgradedb')
- cmd.append('-a')
- now = datetime.now().isoformat()
- cmd.append(os.path.join(self.get_bak_dir(), 'reindex_%s' % now))
- cmd.append('-f')
-
- cmd.append('-D')
- cmd.append(self.get_config_dir())
+ dse_ldif = DSEldif(self)
+ indexes = dse_ldif.get_indexes(bename)
+ if indexes:
+ for idx in indexes:
+ cmd.append('-t')
+ cmd.append(idx)
try:
result = subprocess.check_output(cmd, encoding='utf-8')
diff --git a/src/lib389/lib389/cli_ctl/dbtasks.py b/src/lib389/lib389/cli_ctl/dbtasks.py
index 16da966d1..cd96cdaf7 100644
--- a/src/lib389/lib389/cli_ctl/dbtasks.py
+++ b/src/lib389/lib389/cli_ctl/dbtasks.py
@@ -26,32 +26,18 @@ class IndexOrdering(Enum):
def dbtasks_db2index(inst, log, args):
- rtn = False
- if not args.backend:
- if not inst.db2index():
- rtn = False
- else:
- rtn = True
- elif args.backend and not args.attr:
- if not inst.db2index(bename=args.backend):
- rtn = False
- else:
- rtn = True
+ inst.log = log
+ if not inst.db2index(bename=args.backend, attrs=args.attr):
+ log.fatal("db2index failed")
+ return False
else:
- if not inst.db2index(bename=args.backend, attrs=args.attr):
- rtn = False
- else:
- rtn = True
- if rtn:
log.info("db2index successful")
- return rtn
- else:
- log.fatal("db2index failed")
- return rtn
+ return True
def dbtasks_db2bak(inst, log, args):
# Needs an output name?
+ inst.log = log
if not inst.db2bak(args.archive):
log.fatal("db2bak failed")
return False
@@ -61,6 +47,7 @@ def dbtasks_db2bak(inst, log, args):
def dbtasks_bak2db(inst, log, args):
# Needs the archive to restore.
+ inst.log = log
if not inst.bak2db(args.archive):
log.fatal("bak2db failed")
return False
@@ -70,6 +57,7 @@ def dbtasks_bak2db(inst, log, args):
def dbtasks_db2ldif(inst, log, args):
# If export filename is provided, check if file path exists
+ inst.log = log
if args.ldif:
path = Path(args.ldif)
parent = path.parent.absolute()
@@ -88,6 +76,7 @@ def dbtasks_db2ldif(inst, log, args):
def dbtasks_ldif2db(inst, log, args):
# Check if ldif file exists
+ inst.log = log
if not os.path.exists(args.ldif):
raise ValueError("The LDIF file does not exist: " + args.ldif)
@@ -103,6 +92,7 @@ def dbtasks_ldif2db(inst, log, args):
def dbtasks_backups(inst, log, args):
+ inst.log = log
if args.delete:
# Delete backup
inst.del_backup(args.delete[0])
@@ -117,6 +107,7 @@ def dbtasks_backups(inst, log, args):
def dbtasks_ldifs(inst, log, args):
+ inst.log = log
if args.delete:
# Delete LDIF file
inst.del_ldif(args.delete[0])
@@ -131,6 +122,7 @@ def dbtasks_ldifs(inst, log, args):
def dbtasks_verify(inst, log, args):
+ inst.log = log
if not inst.dbverify(bename=args.backend):
log.fatal("dbverify failed")
return False
@@ -521,9 +513,8 @@ def dbtasks_index_check(inst, log, args):
def create_parser(subcommands):
db2index_parser = subcommands.add_parser('db2index', help="Initialise a reindex of the server database. The server must be stopped for this to proceed.", formatter_class=CustomHelpFormatter)
- # db2index_parser.add_argument('suffix', help="The suffix to reindex. IE dc=example,dc=com.")
- db2index_parser.add_argument('backend', nargs="?", help="The backend to reindex. IE userRoot", default=False)
- db2index_parser.add_argument('--attr', nargs="*", help="The attribute's to reindex. IE --attr aci cn givenname", default=False)
+ db2index_parser.add_argument('backend', help="The backend to reindex. IE userRoot")
+ db2index_parser.add_argument('--attr', action='append', help="An attribute to reindex. IE: --attr member --attr cn ...")
db2index_parser.set_defaults(func=dbtasks_db2index)
db2bak_parser = subcommands.add_parser('db2bak', help="Initialise a BDB backup of the database. The server must be stopped for this to proceed.", formatter_class=CustomHelpFormatter)
--
2.52.0

View File

@ -0,0 +1,41 @@
From c831a7af2afb7cfd2eb958476a7a8d421ac5d339 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Fri, 13 Feb 2026 15:38:52 +0100
Subject: [PATCH] Issue 7184 - (2nd) argparse.HelpFormatter
_format_actions_usage() is deprecated (#7257)
Description:
`_format_actions_usage()` was also removed in Python 3.14.3.
Replace version check with `isinstance()` to handle the return type of
`_get_actions_usage_parts()` more robustly across Python versions.
Relates: https://github.com/389ds/389-ds-base/issues/7184
Fixes: https://github.com/389ds/389-ds-base/issues/7253
Reviewed by: @progier389 (Thanks!)
---
src/lib389/lib389/cli_base/__init__.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/lib389/lib389/cli_base/__init__.py b/src/lib389/lib389/cli_base/__init__.py
index f1055aadc..3af8a46e6 100644
--- a/src/lib389/lib389/cli_base/__init__.py
+++ b/src/lib389/lib389/cli_base/__init__.py
@@ -420,11 +420,11 @@ class CustomHelpFormatter(argparse.HelpFormatter):
else:
# Use _get_actions_usage_parts() for Python 3.13 and later
action_parts = self._get_actions_usage_parts(parent_arguments, [])
- if sys.version_info >= (3, 15):
- # Python 3.15 returns a tuple (list of actions, count of actions)
+ if isinstance(action_parts, tuple):
+ # Python 3.14.3+ and 3.15+ return a tuple (list of actions, count of actions)
formatted_options = ' '.join(action_parts[0])
else:
- # Python 3.13 and 3.14 return a list of actions
+ # Earlier versions return a list of actions
formatted_options = ' '.join(action_parts)
# If formatted_options already in usage - remove them
--
2.52.0

View File

@ -0,0 +1,32 @@
From a7f8eb79dde03ac771de2335a2acb4aed48483a2 Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Fri, 13 Feb 2026 16:27:25 +0100
Subject: [PATCH] Issue 7213 - (2nd) MDB_BAD_VALSIZE error while handling VLV
(#7258)
Decription:
Disable test_vlv_long_attribute_value on BDB as it hangs sometimes in
CI, blocking other pipelines.
Relates: https://github.com/389ds/389-ds-base/issues/7213
Reviewed by: @progier389 (Thanks!)
---
dirsrvtests/tests/suites/vlv/regression_test.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/dirsrvtests/tests/suites/vlv/regression_test.py b/dirsrvtests/tests/suites/vlv/regression_test.py
index 1cc03c303..94001af62 100644
--- a/dirsrvtests/tests/suites/vlv/regression_test.py
+++ b/dirsrvtests/tests/suites/vlv/regression_test.py
@@ -1178,6 +1178,7 @@ def test_vlv_with_mr(vlv_setup_with_uid_mr):
+@pytest.mark.skipif(get_default_db_lib() == "bdb", reason="Hangs on BDB")
def test_vlv_long_attribute_value(topology_st, request):
"""
Test VLV with an entry containing a very long attribute value (2K).
--
2.52.0

View File

@ -0,0 +1,34 @@
From 75b1213dfb94495160a735c2688cee03d6be5a9a Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Fri, 13 Feb 2026 16:58:24 +0100
Subject: [PATCH] Issue 7223 - Use lexicographical order for ancestorid (#7256)
Description:
`ldbm_instance_create_default_indexes()` configured ancestorid with
integerOrderingMatch in the in-memory attrinfo, but ancestorid on disk
might be using lexicographic ordering (data before the upgrade or after
ldif2db import).
Relates: https://github.com/389ds/389-ds-base/issues/7223
Reviewed by: @tbordaz (Thanks!)
---
ldap/servers/slapd/back-ldbm/instance.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
index 3fcdb5554..563cf96db 100644
--- a/ldap/servers/slapd/back-ldbm/instance.c
+++ b/ldap/servers/slapd/back-ldbm/instance.c
@@ -239,7 +239,7 @@ ldbm_instance_create_default_indexes(backend *be)
* ancestorid is special, there is actually no such attr type
* but we still want to use the attr index file APIs.
*/
- e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, "integerOrderingMatch");
+ e = ldbm_instance_init_config_entry(LDBM_ANCESTORID_STR, "eq", 0, 0, 0, 0);
attr_index_config(be, "ldbm index init", 0, e, 1, 0, NULL);
slapi_entry_free(e);
}
--
2.52.0

View File

@ -0,0 +1,89 @@
From 7809104b9edb2d05ceee7ca819152f365cc1beb0 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Wed, 11 Feb 2026 15:51:47 -0500
Subject: [PATCH] Issue 7066/7052 - allow password history to be set to zero
and remove history
Description:
For local password policies the server was incorrectly rejecting updates that
set the value to zero. When password history is set to zero the old passwords
in the entry history are not cleaned as expected.
relates: https://github.com/389ds/389-ds-base/issues/7052
relates: https://github.com/389ds/389-ds-base/issues/7066
Reviewed by: progier(Thanks!)
---
.../tests/suites/password/pwp_history_test.py | 9 ++++++---
ldap/servers/slapd/modify.c | 2 +-
ldap/servers/slapd/pw.c | 13 ++++++++++++-
3 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/dirsrvtests/tests/suites/password/pwp_history_test.py b/dirsrvtests/tests/suites/password/pwp_history_test.py
index 8a15a1986..387824bc5 100644
--- a/dirsrvtests/tests/suites/password/pwp_history_test.py
+++ b/dirsrvtests/tests/suites/password/pwp_history_test.py
@@ -108,8 +108,12 @@ def test_history_is_not_overwritten(topology_st, user):
user.set('userpassword', USER_PWD)
-def test_basic(topology_st, user):
- """Test basic password policy history feature functionality
+@pytest.mark.parametrize('policy',
+ [(pytest.param('global')),
+ (pytest.param('subtree')),
+ (pytest.param('user'))])
+def test_basic(topology_st, user, policy):
+ """Test basic password policy history feature functionality with dynamic count reduction
:id: 83d74f7d-3036-4944-8839-1b40bbf265ff
:setup: Standalone instance, a test user
@@ -238,7 +242,6 @@ def test_basic(topology_st, user):
#
# Reset password by Directory Manager(admin reset)
- #
dm = DirectoryManager(topology_st.standalone)
dm.rebind()
time.sleep(.5)
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
index b0066faf8..d76427886 100644
--- a/ldap/servers/slapd/modify.c
+++ b/ldap/servers/slapd/modify.c
@@ -87,7 +87,7 @@ static struct attr_value_check
{CONFIG_PW_WARNING_ATTRIBUTE, check_pw_duration_value, 0, -1},
{CONFIG_PW_MINLENGTH_ATTRIBUTE, attr_check_minmax, 2, 512},
{CONFIG_PW_MAXFAILURE_ATTRIBUTE, attr_check_minmax, 1, 32767},
- {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 1, 24},
+ {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 0, 24},
{CONFIG_PW_LOCKDURATION_ATTRIBUTE, check_pw_duration_value, -1, -1},
{CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE, check_pw_resetfailurecount_value, -1, -1},
{CONFIG_PW_GRACELIMIT_ATTRIBUTE, attr_check_minmax, 0, -1},
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
index cda1c404f..12cf759ce 100644
--- a/ldap/servers/slapd/pw.c
+++ b/ldap/servers/slapd/pw.c
@@ -1532,7 +1532,18 @@ update_pw_history(Slapi_PBlock *pb, const Slapi_DN *sdn, char *old_pw)
pwpolicy = new_passwdPolicy(pb, dn);
if (pwpolicy->pw_inhistory == 0){
- /* We are only enforcing the current password, just return */
+ /* We are only enforcing the current password, just return but first
+ * cleanup any old passwords in the history */
+ attribute.mod_type = "passwordHistory";
+ attribute.mod_op = LDAP_MOD_REPLACE;
+ attribute.mod_values = NULL;
+ list_of_mods[0] = &attribute;
+ list_of_mods[1] = NULL;
+ mod_pb = slapi_pblock_new();
+ slapi_modify_internal_set_pb_ext(mod_pb, sdn, list_of_mods, NULL, NULL, pw_get_componentID(), 0);
+ slapi_modify_internal_pb(mod_pb);
+ slapi_pblock_destroy(mod_pb);
+
return res;
}
--
2.52.0

View File

@ -0,0 +1,260 @@
From 4209ff65310c54b6ab55594984201b1214fb75b2 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Mon, 16 Feb 2026 16:52:52 -0500
Subject: [PATCH] Issue 7243 - UI - fix certificate table and modal
Description:
The certificate table was not handling sorting and pagination correctly, and
the add cert modal was not correctly selecting certs on disk
This was fixed upstream for the hot cetificate features, but only backporting
the table/modal changes to older branches
relates: https://github.com/389ds/389-ds-base/issues/7243
Reviewed by: spichugi(Thanks!)
---
.../src/lib/security/securityModals.jsx | 4 +-
.../src/lib/security/securityTables.jsx | 85 ++++++-------------
2 files changed, 29 insertions(+), 60 deletions(-)
diff --git a/src/cockpit/389-console/src/lib/security/securityModals.jsx b/src/cockpit/389-console/src/lib/security/securityModals.jsx
index a42aa50d6..c4af753c0 100644
--- a/src/cockpit/389-console/src/lib/security/securityModals.jsx
+++ b/src/cockpit/389-console/src/lib/security/securityModals.jsx
@@ -298,7 +298,9 @@ export class SecurityAddCertModal extends React.Component {
<FormSelect
value={selectCertName}
id="selectCertName"
- onChange={handleCertSelect}
+ onChange={(e, str) => {
+ handleCertSelect(str);
+ }}
aria-label="FormSelect Input"
className="ds-cert-select"
validated={selectValidated}
diff --git a/src/cockpit/389-console/src/lib/security/securityTables.jsx b/src/cockpit/389-console/src/lib/security/securityTables.jsx
index fce4cb04e..a6c64e6c1 100644
--- a/src/cockpit/389-console/src/lib/security/securityTables.jsx
+++ b/src/cockpit/389-console/src/lib/security/securityTables.jsx
@@ -4,7 +4,6 @@ import {
Grid,
GridItem,
Pagination,
- PaginationVariant,
SearchInput,
Tooltip,
} from '@patternfly/react-core';
@@ -94,10 +93,10 @@ class KeyTable extends React.Component {
];
handleSort(_event, index, direction) {
- const sortedRows = [...this.state.rows].sort((a, b) =>
+ const sortedRows = [...this.state.rows].sort((a, b) =>
(a[index] < b[index] ? -1 : a[index] > b[index] ? 1 : 0)
);
-
+
this.setState({
sortBy: {
index,
@@ -127,7 +126,7 @@ class KeyTable extends React.Component {
>
<a className="ds-font-size-sm">{_("What is an orphan key?")}</a>
</Tooltip>
- <Table
+ <Table
className="ds-margin-top"
aria-label="orph key table"
variant="compact"
@@ -135,7 +134,7 @@ class KeyTable extends React.Component {
<Thead>
<Tr>
{columns.map((column, idx) => (
- <Th
+ <Th
key={idx}
sort={column.sortable ? {
sortBy,
@@ -163,7 +162,7 @@ class KeyTable extends React.Component {
)}
{hasRows && (
<Td isActionCell>
- <ActionsColumn
+ <ActionsColumn
items={this.getActionsForRow(row)}
/>
</Td>
@@ -224,7 +223,7 @@ class CSRTable extends React.Component {
}
handleSort(_event, index, direction) {
- const sortedRows = [...this.state.rows].sort((a, b) =>
+ const sortedRows = [...this.state.rows].sort((a, b) =>
(a[index] < b[index] ? -1 : a[index] > b[index] ? 1 : 0)
);
this.setState({
@@ -316,7 +315,7 @@ class CSRTable extends React.Component {
onClear={(evt) => this.handleSearchChange(evt, '')}
/>
}
- <Table
+ <Table
className="ds-margin-top"
aria-label="csr table"
variant="compact"
@@ -324,7 +323,7 @@ class CSRTable extends React.Component {
<Thead>
<Tr>
{columns.map((column, idx) => (
- <Th
+ <Th
key={idx}
sort={column.sortable ? {
sortBy,
@@ -352,7 +351,7 @@ class CSRTable extends React.Component {
)}
{hasRows && (
<Td isActionCell>
- <ActionsColumn
+ <ActionsColumn
items={this.getActionsForRow(row)}
/>
</Td>
@@ -418,41 +417,11 @@ class CertTable extends React.Component {
}
handleSort(_event, columnIndex, direction) {
- const sorted_rows = [];
- const rows = [];
- let count = 0;
-
- // Convert the rows pairings into a sortable array
- for (let idx = 0; idx < this.state.rows.length; idx += 2) {
- sorted_rows.push({
- expandedRow: this.state.rows[idx + 1],
- 1: this.state.rows[idx].cells[0].content,
- 2: this.state.rows[idx].cells[1].content,
- 3: this.state.rows[idx].cells[2].content,
- issuer: this.state.rows[idx].issuer,
- flags: this.state.rows[idx].flags
- });
- }
+ const rows = [...this.state.rows];
- sorted_rows.sort((a, b) => (a[columnIndex + 1] > b[columnIndex + 1]) ? 1 : -1);
+ rows.sort((a, b) => (a.cells[columnIndex].content > b.cells[columnIndex].content) ? 1 : -1);
if (direction !== SortByDirection.asc) {
- sorted_rows.reverse();
- }
-
- for (const srow of sorted_rows) {
- rows.push({
- isOpen: false,
- cells: [
- { content: srow[1] },
- { content: srow[2] },
- { content: srow[3] }
- ],
- issuer: srow.issuer,
- flags: srow.flags,
- });
- srow.expandedRow.parent = count;
- rows.push(srow.expandedRow);
- count += 2;
+ rows.reverse();
}
this.setState({
@@ -534,18 +503,16 @@ class CertTable extends React.Component {
rows.push(
{
isOpen: false,
- cells: [cert.attrs.nickname, cert.attrs.subject, cert.attrs.expires],
+ cells: [
+ { content: cert.attrs.nickname },
+ { content: cert.attrs.subject },
+ { content: cert.attrs.expires }
+ ],
issuer: cert.attrs.issuer,
flags: cert.attrs.flags,
-
- },
- {
- parent: count,
- fullWidth: true,
- cells: [{ title: this.getExpandedRow(cert.attrs.issuer, cert.attrs.flags) }]
- },
+ }
);
- count += 2;
+ count += 1;
}
this.setState({
@@ -587,7 +554,7 @@ class CertTable extends React.Component {
onChange={this.handleSearchChange}
onClear={(evt) => this.handleSearchChange(evt, '')}
/>}
- <Table
+ <Table
aria-label="cert table"
variant='compact'
>
@@ -626,7 +593,7 @@ class CertTable extends React.Component {
</Td>
))}
<Td isActionCell>
- <ActionsColumn
+ <ActionsColumn
items={this.getActionsForRow(row)}
/>
</Td>
@@ -646,7 +613,7 @@ class CertTable extends React.Component {
</Table>
{hasRows &&
<Pagination
- itemCount={this.state.rows.length / 2}
+ itemCount={this.state.rows.length}
widgetId="pagination-options-menu-bottom"
perPage={perPage}
page={page}
@@ -728,7 +695,7 @@ class CRLTable extends React.Component {
if (direction !== SortByDirection.asc) {
sorted_rows.reverse();
}
-
+
for (const srow of sorted_rows) {
rows.push({
isOpen: false,
@@ -806,14 +773,14 @@ class CRLTable extends React.Component {
onChange={this.handleSearchChange}
onClear={(evt) => this.handleSearchChange(evt, '')}
/>
- <Table
+ <Table
aria-label="CRL Table"
variant="compact"
>
<Thead>
<Tr>
{this.state.columns.map((column, idx) => (
- <Th
+ <Th
key={idx}
sort={column.sortable ? {
sortBy: this.state.sortBy,
@@ -836,7 +803,7 @@ class CRLTable extends React.Component {
))}
{this.state.hasRows && (
<Td isActionCell>
- <ActionsColumn
+ <ActionsColumn
items={this.getActionsForRow(row)}
/>
</Td>
--
2.52.0

View File

@ -0,0 +1,538 @@
From 09b6f770f15ee8d333377b7902e18b78f4f7008c Mon Sep 17 00:00:00 2001
From: Viktor Ashirov <vashirov@redhat.com>
Date: Wed, 18 Feb 2026 09:26:57 +0100
Subject: [PATCH] Issue 7223 - Remove integerOrderingMatch requirement for
parentid (#7264)
Description:
integerOrderingMatch was introduced as a requirement for parentid and
ancestorid indexes for performance reasons. But after #7096 the order
for parentid doesn't make a lot of difference.
Fix Description:
* Remove integerOrderingMatch requirement for parentid.
* Read only first 100 keys from dbscan in index ordering check
* Do not run dsctl index-check during RPM upgrade
Relates: https://github.com/389ds/389-ds-base/pull/7223
Reviewed by: @progier389, @tbordaz (Thanks!)
---
.../healthcheck/health_system_indexes_test.py | 83 ++++----------
ldap/servers/slapd/upgrade.c | 105 ------------------
rpm/389-ds-base.spec.in | 3 -
src/lib389/lib389/backend.py | 5 +-
src/lib389/lib389/cli_ctl/dbtasks.py | 99 ++++++++---------
5 files changed, 73 insertions(+), 222 deletions(-)
diff --git a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
index 3b3651b38..71a9f590a 100644
--- a/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
+++ b/dirsrvtests/tests/suites/healthcheck/health_system_indexes_test.py
@@ -180,7 +180,8 @@ def test_missing_parentid(topology_st, log_buffering_enabled):
def test_missing_matching_rule(topology_st, log_buffering_enabled):
- """Check if healthcheck returns DSBLE0007 code when parentId index is missing integerOrderingMatch
+ """Check that healthcheck does NOT report DSBLE0007 when parentId index is missing integerOrderingMatch.
+ Both lexicographic and integer orderings are valid for parentid.
:id: 7ffa71db-8995-430a-bed8-59bce944221c
:setup: Standalone instance
@@ -190,19 +191,14 @@ def test_missing_matching_rule(topology_st, log_buffering_enabled):
3. Use healthcheck without --json option
4. Use healthcheck with --json option
5. Re-add the matching rule
- 6. Use healthcheck without --json option
- 7. Use healthcheck with --json option
:expectedresults:
1. Success
2. Success
- 3. healthcheck reports DSBLE0007 code and related details
- 4. healthcheck reports DSBLE0007 code and related details
+ 3. healthcheck reports no issues found
+ 4. healthcheck reports no issues found
5. Success
- 6. healthcheck reports no issues found
- 7. healthcheck reports no issues found
"""
- RET_CODE = "DSBLE0007"
PARENTID_DN = "cn=parentid,cn=index,cn=userroot,cn=ldbm database,cn=plugins,cn=config"
standalone = topology_st.standalone
@@ -211,17 +207,14 @@ def test_missing_matching_rule(topology_st, log_buffering_enabled):
parentid_index = Index(standalone, PARENTID_DN)
parentid_index.remove("nsMatchingRule", "integerOrderingMatch")
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=RET_CODE)
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=RET_CODE)
+ run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
+ run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
log.info("Re-add the integerOrderingMatch matching rule")
parentid_index = Index(standalone, PARENTID_DN)
parentid_index.add("nsMatchingRule", "integerOrderingMatch")
standalone.restart()
- run_healthcheck_and_flush_log(topology_st, standalone, json=False, searched_code=CMD_OUTPUT)
- run_healthcheck_and_flush_log(topology_st, standalone, json=True, searched_code=JSON_OUTPUT)
-
def test_usn_plugin_missing_entryusn(topology_st, usn_plugin_enabled, log_buffering_enabled):
"""Check if healthcheck returns DSBLE0007 code when USN plugin is enabled but entryusn index is missing
@@ -911,7 +904,9 @@ def test_index_check_fixes_ancestorid_config(topology_st):
def test_index_check_fixes_missing_matching_rule(topology_st):
- """Check if dsctl index-check --fix adds missing integerOrderingMatch
+ """Check that removing integerOrderingMatch from parentid config is not
+ flagged as an issue when disk ordering cannot be determined.
+ Both lexicographic and integer orderings are valid for parentid.
:id: 6c1d4e9f-0a3b-4d5c-1e7f-8a9b0c2d3e4f
:setup: Standalone instance
@@ -919,18 +914,14 @@ def test_index_check_fixes_missing_matching_rule(topology_st):
1. Create DS instance
2. Stop the server
3. Remove integerOrderingMatch from parentid index using DSEldif
- 4. Run dsctl index-check (should detect issue)
- 5. Run dsctl index-check --fix
- 6. Verify integerOrderingMatch was added back
- 7. Start the server
+ 4. Run dsctl index-check (should NOT detect issue since disk ordering is unknown)
+ 5. Start the server
:expectedresults:
1. Success
2. Success
3. Success
- 4. index-check returns False and detects missing matching rule
- 5. index-check returns True after fix
- 6. integerOrderingMatch is present
- 7. Success
+ 4. index-check returns True (no issues, disk ordering unknown)
+ 5. Success
"""
from lib389.cli_ctl.dbtasks import dbtasks_index_check
from lib389.dseldif import DSEldif
@@ -964,34 +955,20 @@ def test_index_check_fixes_missing_matching_rule(topology_st):
f"integerOrderingMatch should be removed, but found: {mr}"
log.info("integerOrderingMatch removed from parentid index")
- log.info("Run index-check without --fix (should detect issue)")
+ log.info("Run index-check (should NOT detect issue - disk ordering unknown)")
args = FakeArgs()
args.backend = "userRoot"
args.fix = False
result = dbtasks_index_check(standalone, topology_st.logcap.log, args)
- assert result is False, "index-check should detect missing matching rule"
- assert topology_st.logcap.contains("missing integerOrderingMatch")
+ assert result is True, \
+ "index-check should not flag missing integerOrderingMatch when disk ordering is unknown"
+ assert topology_st.logcap.contains("could not determine disk ordering")
topology_st.logcap.flush()
- log.info("Run index-check with --fix")
- args.fix = True
- result = dbtasks_index_check(standalone, topology_st.logcap.log, args)
- assert result is True, "index-check --fix should succeed"
- assert topology_st.logcap.contains("integerOrderingMatch")
- topology_st.logcap.flush()
-
- log.info("Verify integerOrderingMatch was added back")
- dse_ldif = DSEldif(standalone) # Reload to get fresh data
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
- assert matching_rules is not None, "nsMatchingRule should be present"
- found_int_order = False
- for mr in matching_rules:
- if "integerorderingmatch" in mr.lower():
- found_int_order = True
- break
- assert found_int_order, f"integerOrderingMatch should be present, got: {matching_rules}"
- log.info("integerOrderingMatch successfully added back")
+ log.info("Restore integerOrderingMatch and start the server")
+ dse_ldif = DSEldif(standalone)
+ dse_ldif.add(parentid_dn, "nsMatchingRule", "integerOrderingMatch")
log.info("Start the server")
standalone.start()
@@ -1081,7 +1058,7 @@ def test_index_check_fixes_multiple_issues(topology_st):
:steps:
1. Create DS instance
2. Stop the server
- 3. Add multiple issues: scanlimit, ancestorid config, missing matching rule
+ 3. Add multiple issues: scanlimit and ancestorid config
4. Run dsctl index-check (should detect all issues)
5. Run dsctl index-check --fix
6. Verify all issues were fixed
@@ -1123,14 +1100,6 @@ def test_index_check_fixes_multiple_issues(topology_st):
]
dse_ldif.add_entry(ancestorid_entry)
- log.info("Add issue 3: Remove integerOrderingMatch from parentid")
- dse_ldif = DSEldif(standalone) # Reload
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
- if matching_rules:
- for mr in matching_rules:
- if "integerorderingmatch" in mr.lower():
- dse_ldif.delete(parentid_dn, "nsMatchingRule", mr)
-
log.info("Run index-check without --fix (should detect all issues)")
args = FakeArgs()
args.backend = "userRoot"
@@ -1161,16 +1130,6 @@ def test_index_check_fixes_multiple_issues(topology_st):
cn_value = dse_ldif.get(ancestorid_dn, "cn", single=True)
assert cn_value is None, f"ancestorid config should be removed, got: {cn_value}"
- # Check matching rule added back
- matching_rules = dse_ldif.get(parentid_dn, "nsMatchingRule")
- found_int_order = False
- if matching_rules:
- for mr in matching_rules:
- if "integerorderingmatch" in mr.lower():
- found_int_order = True
- break
- assert found_int_order, f"integerOrderingMatch should be present, got: {matching_rules}"
-
log.info("All issues verified as fixed")
log.info("Run index-check again to confirm all clear")
diff --git a/ldap/servers/slapd/upgrade.c b/ldap/servers/slapd/upgrade.c
index d9156cae9..537c38feb 100644
--- a/ldap/servers/slapd/upgrade.c
+++ b/ldap/servers/slapd/upgrade.c
@@ -500,107 +500,6 @@ upgrade_remove_ancestorid_index_config(void)
return uresult;
}
-/*
- * Check if parentid/ancestorid indexes are missing the integerOrderingMatch
- * matching rule.
- *
- * This function logs a warning if we detect this condition, advising
- * the administrator to reindex the affected attributes.
- */
-static upgrade_status
-upgrade_check_id_index_matching_rule(void)
-{
- struct slapi_pblock *pb = slapi_pblock_new();
- Slapi_Entry **backends = NULL;
- const char *be_base_dn = "cn=ldbm database,cn=plugins,cn=config";
- const char *be_filter = "(objectclass=nsBackendInstance)";
- const char *attrs_to_check[] = {"parentid", NULL};
- upgrade_status uresult = UPGRADE_SUCCESS;
-
- /* Search for all backend instances */
- slapi_search_internal_set_pb(
- pb, be_base_dn,
- LDAP_SCOPE_ONELEVEL,
- be_filter, NULL, 0, NULL, NULL,
- plugin_get_default_component_id(), 0);
- slapi_search_internal_pb(pb);
- slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &backends);
-
- if (backends) {
- for (size_t be_idx = 0; backends[be_idx] != NULL; be_idx++) {
- const char *be_dn = slapi_entry_get_dn_const(backends[be_idx]);
- const char *be_name = slapi_entry_attr_get_ref(backends[be_idx], "cn");
- if (!be_dn || !be_name) {
- continue;
- }
-
- /* Check each attribute that should have integerOrderingMatch */
- for (size_t attr_idx = 0; attrs_to_check[attr_idx] != NULL; attr_idx++) {
- const char *attr_name = attrs_to_check[attr_idx];
- struct slapi_pblock *idx_pb = slapi_pblock_new();
- Slapi_Entry **idx_entries = NULL;
- char *idx_dn = slapi_create_dn_string("cn=%s,cn=index,%s",
- attr_name, be_dn);
- char *idx_filter = "(objectclass=nsIndex)";
- PRBool has_matching_rule = PR_FALSE;
-
- if (!idx_dn) {
- slapi_pblock_destroy(idx_pb);
- continue;
- }
-
- slapi_search_internal_set_pb(
- idx_pb, idx_dn,
- LDAP_SCOPE_BASE,
- idx_filter, NULL, 0, NULL, NULL,
- plugin_get_default_component_id(), 0);
- slapi_search_internal_pb(idx_pb);
- slapi_pblock_get(idx_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &idx_entries);
-
- if (idx_entries && idx_entries[0]) {
- /* Index exists, check if it has integerOrderingMatch */
- Slapi_Attr *mr_attr = NULL;
- if (slapi_entry_attr_find(idx_entries[0], "nsMatchingRule", &mr_attr) == 0) {
- Slapi_Value *sval = NULL;
- int idx;
- for (idx = slapi_attr_first_value(mr_attr, &sval);
- idx != -1;
- idx = slapi_attr_next_value(mr_attr, idx, &sval)) {
- const struct berval *bval = slapi_value_get_berval(sval);
- if (bval && bval->bv_val &&
- strcasecmp(bval->bv_val, "integerOrderingMatch") == 0) {
- has_matching_rule = PR_TRUE;
- break;
- }
- }
- }
-
- if (!has_matching_rule) {
- /* Index exists but doesn't have integerOrderingMatch, log a warning */
- slapi_log_err(SLAPI_LOG_ERR, "upgrade_check_id_index_matching_rule",
- "Index '%s' in backend '%s' is missing 'nsMatchingRule: integerOrderingMatch'. "
- "Incorrectly configured system indexes can lead to poor search performance, replication issues, and other operational problems. "
- "To fix this, add the matching rule and reindex: "
- "dsconf <instance> backend index set --add-mr integerOrderingMatch --attr %s %s && "
- "dsconf <instance> backend index reindex --attr %s %s. "
- "WARNING: Reindexing can be resource-intensive and may impact server performance on a live system. "
- "Consider scheduling reindexing during maintenance windows or periods of low activity.\n",
- attr_name, be_name, attr_name, be_name, attr_name, be_name);
- }
- }
-
- slapi_ch_free_string(&idx_dn);
- slapi_free_search_results_internal(idx_pb);
- slapi_pblock_destroy(idx_pb);
- }
- }
- }
-
- slapi_free_search_results_internal(pb);
- slapi_pblock_destroy(pb);
-
- return uresult;
-}
upgrade_status
upgrade_server(void)
@@ -637,10 +536,6 @@ upgrade_server(void)
return UPGRADE_FAILURE;
}
- if (upgrade_check_id_index_matching_rule() != UPGRADE_SUCCESS) {
- return UPGRADE_FAILURE;
- }
-
return UPGRADE_SUCCESS;
}
diff --git a/rpm/389-ds-base.spec.in b/rpm/389-ds-base.spec.in
index 0175dfa7c..f78258f6a 100644
--- a/rpm/389-ds-base.spec.in
+++ b/rpm/389-ds-base.spec.in
@@ -667,9 +667,6 @@ for dir in "$instbase"/slapd-* ; do
else
echo "instance $inst is not running" >> "$output" 2>&1 || :
fi
- # Run index-check on all instances (running or not)
- # This fixes index ordering mismatches from older versions
- dsctl "$inst_name" index-check --fix >> "$output2" 2>&1 || :
ninst=$((ninst + 1))
done
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
index 1d9be4683..29a1796ac 100644
--- a/src/lib389/lib389/backend.py
+++ b/src/lib389/lib389/backend.py
@@ -617,9 +617,10 @@ class Backend(DSLdapObject):
# Default system indexes taken from ldap/servers/slapd/back-ldbm/instance.c
# Note: entryrdn and ancestorid are internal system indexes that are not
# exposed in cn=config - they are managed internally by the server.
- # Only parentid has a DSE config entry (for the integerOrderingMatch rule).
+ # parentid works correctly with both lexicographic and integer ordering,
+ # so integerOrderingMatch is not required.
expected_system_indexes = {
- 'parentid': {'types': ['eq'], 'matching_rule': 'integerOrderingMatch'},
+ 'parentid': {'types': ['eq'], 'matching_rule': None},
'objectClass': {'types': ['eq'], 'matching_rule': None},
'aci': {'types': ['pres'], 'matching_rule': None},
'nscpEntryDN': {'types': ['eq'], 'matching_rule': None},
diff --git a/src/lib389/lib389/cli_ctl/dbtasks.py b/src/lib389/lib389/cli_ctl/dbtasks.py
index cd96cdaf7..b02de203f 100644
--- a/src/lib389/lib389/cli_ctl/dbtasks.py
+++ b/src/lib389/lib389/cli_ctl/dbtasks.py
@@ -10,6 +10,7 @@
import glob
import os
import re
+import signal
import subprocess
from enum import Enum
from lib389._constants import TaskWarning
@@ -263,45 +264,53 @@ def _check_disk_ordering(db_dir, backend, index_name, dbscan_path, is_mdb, log):
if not index_file:
return IndexOrdering.UNKNOWN
+ # Only read the first 100 lines from dbscan to avoid scanning the
+ # entire index (which can take hours on large databases).
try:
- result = subprocess.run(
+ proc = subprocess.Popen(
[dbscan_path, "-f", index_file],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
- timeout=60,
)
- if result.returncode != 0:
- log.warning(" dbscan returned non-zero exit code for %s", index_file)
- return IndexOrdering.UNKNOWN
-
- # Parse keys from dbscan output
keys = []
- for line in result.stdout.split("\n"):
+ line_count = 0
+ assert proc.stdout is not None
+ for line in proc.stdout:
+ line_count += 1
+ if line_count > 100:
+ break
line = line.strip()
if line.startswith("="):
match = re.match(r"^=(\d+)", line)
if match:
keys.append(int(match.group(1)))
+ proc.terminate()
+ try:
+ proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ proc.kill()
+ proc.wait()
+
+ if proc.returncode not in (0, -signal.SIGTERM):
+ log.warning(" dbscan returned non-zero exit code for %s", index_file)
+ return IndexOrdering.UNKNOWN
+
if len(keys) < 2:
return IndexOrdering.UNKNOWN
# Check if keys are in integer order by looking for decreasing numeric values
# (which would indicate lexicographic ordering, e.g., "3" < "30" < "4")
prev_id = keys[0]
- for i in range(1, min(len(keys), 100)):
- current_id = keys[i]
+ for current_id in keys[1:]:
if prev_id > current_id:
return IndexOrdering.LEXICOGRAPHIC
prev_id = current_id
return IndexOrdering.INTEGER
- except subprocess.TimeoutExpired:
- log.warning(" dbscan timed out for %s", index_file)
- return IndexOrdering.UNKNOWN
except OSError as e:
log.warning(" Error running dbscan: %s", e)
return IndexOrdering.UNKNOWN
@@ -375,8 +384,7 @@ def dbtasks_index_check(inst, log, args):
# Track all issues found
all_ok = True
- mismatches = [] # (backend, index_name) tuples needing reindex
- missing_matching_rules = [] # (backend, index_name) tuples missing integerOrderingMatch
+ config_fixes = [] # (backend, index_name, action) tuples: action is "add_mr" or "remove_mr"
scan_limits_to_remove = [] # (backend, index_name) tuples with nsIndexIDListScanLimit
ancestorid_configs_to_remove = [] # backend names with ancestorid config entries
remove_ancestorid_from_defaults = False # Flag to remove from cn=default indexes
@@ -409,13 +417,6 @@ def dbtasks_index_check(inst, log, args):
if disk_ordering == IndexOrdering.UNKNOWN:
log.info(" %s - could not determine disk ordering, skipping", index_name)
- # For parentid, still check if matching rule is missing
- if index_name == "parentid":
- config_has_int_order = _has_integer_ordering_match(dse_ldif, backend, index_name)
- if not config_has_int_order:
- log.warning(" %s - missing integerOrderingMatch in config", index_name)
- missing_matching_rules.append((backend, index_name))
- all_ok = False
continue
config_has_int_order = _has_integer_ordering_match(dse_ldif, backend, index_name)
@@ -423,18 +424,15 @@ def dbtasks_index_check(inst, log, args):
log.info(" %s - config: %s, disk: %s",
index_name, config_desc, disk_ordering.value)
- # For parentid, the desired state is always integer ordering
+ # Both orderings are valid for parentid, but config must match disk.
if index_name == "parentid":
- if not config_has_int_order:
- log.warning(" %s - missing integerOrderingMatch in config", index_name)
- if (backend, index_name) not in missing_matching_rules:
- missing_matching_rules.append((backend, index_name))
+ if config_has_int_order and disk_ordering == IndexOrdering.LEXICOGRAPHIC:
+ log.warning(" %s - MISMATCH: config has integerOrderingMatch but disk is lexicographic", index_name)
+ config_fixes.append((backend, index_name, "remove_mr"))
all_ok = False
-
- if disk_ordering == IndexOrdering.LEXICOGRAPHIC:
- log.warning(" %s - disk ordering is lexicographic, needs reindex", index_name)
- if (backend, index_name) not in mismatches:
- mismatches.append((backend, index_name))
+ elif not config_has_int_order and disk_ordering == IndexOrdering.INTEGER:
+ log.warning(" %s - MISMATCH: config is lexicographic but disk has integer ordering", index_name)
+ config_fixes.append((backend, index_name, "add_mr"))
all_ok = False
# Handle issues
@@ -480,26 +478,27 @@ def dbtasks_index_check(inst, log, args):
log.error(" Failed to remove ancestorid config from backend %s: %s", backend, e)
return False
- # Add missing matching rules to dse.ldif
- for backend, index_name in missing_matching_rules:
+ # Fix config-vs-disk ordering mismatches by adjusting config to match disk
+ for backend, index_name, action in config_fixes:
index_dn = "cn={},cn=index,cn={},cn=ldbm database,cn=plugins,cn=config".format(
index_name, backend
)
- log.info(" Adding integerOrderingMatch to %s in backend %s...", index_name, backend)
- try:
- dse_ldif.add(index_dn, "nsMatchingRule", "integerOrderingMatch")
- log.info(" Updated dse.ldif with integerOrderingMatch for %s", index_name)
- except Exception as e:
- log.error(" Failed to update dse.ldif for %s: %s", index_name, e)
- return False
-
- # Reindex indexes with disk ordering issues
- for backend, index_name in mismatches:
- log.info(" Reindexing %s in backend %s...", index_name, backend)
- if not inst.db2index(bename=backend, attrs=[index_name]):
- log.error(" Failed to reindex %s", index_name)
- return False
- log.info(" Reindex of %s completed successfully", index_name)
+ if action == "add_mr":
+ log.info(" Adding integerOrderingMatch to %s in backend %s...", index_name, backend)
+ try:
+ dse_ldif.add(index_dn, "nsMatchingRule", "integerOrderingMatch")
+ log.info(" Updated dse.ldif with integerOrderingMatch for %s", index_name)
+ except Exception as e:
+ log.error(" Failed to update dse.ldif for %s: %s", index_name, e)
+ return False
+ elif action == "remove_mr":
+ log.info(" Removing integerOrderingMatch from %s in backend %s...", index_name, backend)
+ try:
+ dse_ldif.delete(index_dn, "nsMatchingRule", "integerOrderingMatch")
+ log.info(" Removed integerOrderingMatch from %s", index_name)
+ except Exception as e:
+ log.error(" Failed to remove integerOrderingMatch from %s: %s", index_name, e)
+ return False
log.info("All issues fixed")
return True
@@ -563,5 +562,5 @@ def create_parser(subcommands):
index_check_parser.add_argument('backend', nargs='?', default=None,
help="Backend to check. If not specified, all backends are checked.")
index_check_parser.add_argument('--fix', action='store_true', default=False,
- help="Fix mismatches by reindexing affected indexes")
+ help="Fix mismatches by adjusting config to match on-disk data")
index_check_parser.set_defaults(func=dbtasks_index_check)
--
2.52.0

View File

@ -0,0 +1,202 @@
From 31bef3852f7064c73f66974d7c41262d2a724eab Mon Sep 17 00:00:00 2001
From: Alex Kulberg <vectinx@yandex.ru>
Date: Tue, 9 Dec 2025 17:11:56 +0300
Subject: [PATCH] Issue 7053 - Remove memberof_del_dn_from_groups from MemberOf
plugin (#7064)
Bug Description:
The member plugin creates redundant changes to the member attribute
in groups when deleting a user, although the referential integrity
of the member attribute should be controlled by the Referential Integrity plugin.
Furthermore, memberof doesn't take replication of operations into account
and performs the change on every server instance in the topology.
Fix Description:
Remove the `memberof_del_dn_from_groups` function from the MemberOf plugin,
completely transferring responsibility for deleting users from groups
to the Referential Integrity plugin.
Relates: https://github.com/389ds/389-ds-base/issues/7053
Reviewed by: @tbordaz
---
.../memberof_include_scopes_test.py | 13 ++-
ldap/servers/plugins/memberof/memberof.c | 79 -------------------
2 files changed, 9 insertions(+), 83 deletions(-)
diff --git a/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py b/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
index e1d3b0a96..347eb880f 100644
--- a/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
+++ b/dirsrvtests/tests/suites/memberof_plugin/memberof_include_scopes_test.py
@@ -13,7 +13,7 @@ import time
from lib389.utils import ensure_str
from lib389.topologies import topology_st as topo
from lib389._constants import *
-from lib389.plugins import MemberOfPlugin
+from lib389.plugins import MemberOfPlugin, ReferentialIntegrityPlugin
from lib389.idm.user import UserAccount, UserAccounts
from lib389.idm.group import Group, Groups
from lib389.idm.nscontainer import nsContainers
@@ -77,6 +77,13 @@ def test_multiple_scopes(topo):
inst = topo.standalone
+ EXCLUDED_SUBTREE = 'cn=exclude,%s' % SUFFIX
+ # enable Referential Integrity plugin
+ # to correctly process the 'member' attribute
+ refint = ReferentialIntegrityPlugin(inst)
+ refint.add_excludescope(EXCLUDED_SUBTREE)
+ refint.enable()
+
# configure plugin
memberof = MemberOfPlugin(inst)
memberof.enable()
@@ -106,7 +113,6 @@ def test_multiple_scopes(topo):
check_membership(inst, f'uid=test_m3,{SUBTREE_3}', f'cn=g3,{SUBTREE_3}', False)
# Set exclude scope
- EXCLUDED_SUBTREE = 'cn=exclude,%s' % SUFFIX
EXCLUDED_USER = f"uid=test_m1,{EXCLUDED_SUBTREE}"
INCLUDED_USER = f"uid=test_m1,{SUBTREE_1}"
GROUP_DN = f'cn=g1,{SUBTREE_1}'
@@ -122,9 +128,8 @@ def test_multiple_scopes(topo):
# Check memberOf and group are cleaned up
check_membership(inst, EXCLUDED_USER, GROUP_DN, False)
group = Group(topo.standalone, dn=GROUP_DN)
- assert not group.present("member", EXCLUDED_USER)
assert not group.present("member", INCLUDED_USER)
-
+ assert not group.present("member", EXCLUDED_USER)
if __name__ == '__main__':
# Run isolated
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
index c4b5afee7..5de2e9e72 100644
--- a/ldap/servers/plugins/memberof/memberof.c
+++ b/ldap/servers/plugins/memberof/memberof.c
@@ -151,7 +151,6 @@ static void memberof_set_plugin_id(void *plugin_id);
static int memberof_compare(MemberOfConfig *config, const void *a, const void *b);
static int memberof_qsort_compare(const void *a, const void *b);
static void memberof_load_array(Slapi_Value **array, Slapi_Attr *attr);
-static int memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_Entry *e, Slapi_DN *sdn);
static int memberof_call_foreach_dn(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_DN *sdn, MemberOfConfig *config, char **types, plugin_search_entry_callback callback, void *callback_data, int *cached, PRBool use_grp_cache);
static int memberof_is_direct_member(MemberOfConfig *config, Slapi_Value *groupdn, Slapi_Value *memberdn);
static int memberof_is_grouping_attr(char *type, MemberOfConfig *config);
@@ -540,21 +539,6 @@ deferred_modrdn_func(MemberofDeferredModrdnTask *task)
* attributes to refer to the new name. */
if (ret == LDAP_SUCCESS && pre_sdn && post_sdn) {
if (!memberof_entry_in_scope(&configCopy, &post_entry_info)) {
- /*
- * After modrdn the group contains both the pre and post DN's as
- * members, so we need to cleanup both in this case.
- */
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, pre_e, pre_sdn))) {
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
- "deferred_modrdn_func - Delete dn failed for preop entry(%s), error (%d)\n",
- slapi_sdn_get_dn(pre_sdn), ret);
- }
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, post_e, post_sdn))) {
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
- "deferred_modrdn_func - Delete dn failed for postop entry(%s), error (%d)\n",
- slapi_sdn_get_dn(post_sdn), ret);
- }
-
if (ret == LDAP_SUCCESS && pre_e && configCopy.group_filter &&
0 == slapi_filter_test_simple(pre_e, configCopy.group_filter))
{
@@ -638,16 +622,6 @@ deferred_del_func(MemberofDeferredDelTask *task)
free_configCopy = PR_TRUE;
memberof_unlock_config();
- /* remove this DN from the
- * membership lists of groups
- */
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, e, sdn))) {
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
- "deferred_del_func - Error deleting dn (%s) from group. Error (%d)\n",
- slapi_sdn_get_dn(sdn), ret);
- goto bail;
- }
-
/* is the entry of interest as a group? */
if (e && configCopy.group_filter && 0 == slapi_filter_test_simple(e, configCopy.group_filter)) {
Slapi_Attr *attr = 0;
@@ -1456,16 +1430,6 @@ memberof_postop_del(Slapi_PBlock *pb)
memberof_copy_config(&configCopy, memberof_get_config());
memberof_unlock_config();
- /* remove this DN from the
- * membership lists of groups
- */
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, e, sdn))) {
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
- "memberof_postop_del - Error deleting dn (%s) from group. Error (%d)\n",
- slapi_sdn_get_dn(sdn), ret);
- goto bail;
- }
-
/* is the entry of interest as a group? */
if (e && configCopy.group_filter && 0 == slapi_filter_test_simple(e, configCopy.group_filter)) {
Slapi_Attr *attr = 0;
@@ -1496,34 +1460,6 @@ done:
}
-/* Deletes a member dn from all groups that refer to it. */
-static int
-memberof_del_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_Entry *e, Slapi_DN *sdn)
-{
- char *groupattrs[2] = {0, 0};
- int rc = LDAP_SUCCESS;
- int cached = 0;
-
- /* Loop through each grouping attribute to find groups that have
- * dn as a member. For any matches, delete the dn value from the
- * same grouping attribute. */
- for (size_t i = 0; config->groupattrs && config->groupattrs[i] && rc == LDAP_SUCCESS; i++) {
- memberof_del_dn_data data = {(char *)slapi_sdn_get_dn(sdn),
- config->groupattrs[i], config};
-
- groupattrs[0] = config->groupattrs[i];
-
- slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM,
- "memberof_del_dn_from_groups: Ancestors of %s attr: %s\n",
- slapi_sdn_get_dn(sdn),
- groupattrs[0]);
- rc = memberof_call_foreach_dn(pb, e, sdn, config, groupattrs,
- memberof_del_dn_type_callback, &data, &cached, PR_FALSE);
- }
-
- return rc;
-}
-
int
memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
{
@@ -1904,21 +1840,6 @@ memberof_postop_modrdn(Slapi_PBlock *pb)
* attributes to refer to the new name. */
if (ret == LDAP_SUCCESS && pre_sdn && post_sdn) {
if (!memberof_entry_in_scope(&configCopy, &post_entry_info)) {
- /*
- * After modrdn the group contains both the pre and post DN's as
- * members, so we need to cleanup both in this case.
- */
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, pre_e, pre_sdn))) {
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
- "memberof_postop_modrdn - Delete dn failed for preop entry(%s), error (%d)\n",
- slapi_sdn_get_dn(pre_sdn), ret);
- }
- if ((ret = memberof_del_dn_from_groups(pb, &configCopy, post_e, post_sdn))) {
- slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,
- "memberof_postop_modrdn - Delete dn failed for postop entry(%s), error (%d)\n",
- slapi_sdn_get_dn(post_sdn), ret);
- }
-
if (ret == LDAP_SUCCESS && pre_e && configCopy.group_filter &&
0 == slapi_filter_test_simple(pre_e, configCopy.group_filter)) {
/* is the entry of interest as a group? */
--
2.52.0

View File

@ -47,7 +47,7 @@ ExcludeArch: i686
Summary: 389 Directory Server (base)
Name: 389-ds-base
Version: 2.8.0
Release: 3%{?dist}
Release: 4%{?dist}
License: GPL-3.0-or-later WITH GPL-3.0-389-ds-base-exception AND (Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT) AND (Apache-2.0 OR LGPL-2.1-or-later OR MIT) AND (Apache-2.0 OR MIT) AND (CC-BY-4.0 AND MIT) AND (MIT OR Apache-2.0) AND Unicode-3.0 AND (MIT OR CC0-1.0) AND (MIT OR Unlicense) AND 0BSD AND Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT AND MIT AND ISC AND MPL-2.0 AND PSF-2.0 AND Zlib
URL: https://www.port389.org
Conflicts: selinux-policy-base < 3.9.8
@ -471,20 +471,34 @@ Patch: 0002-Issue-7096-During-replication-online-total-init-the-.patc
Patch: 0003-Issue-Revise-paged-result-search-locking.patch
Patch: 0004-Issue-7172-Index-ordering-mismatch-after-upgrade-717.patch
Patch: 0005-Issue-7172-2nd-Index-ordering-mismatch-after-upgrade.patch
Patch: 0006-Issue-7166-db_config_set-asserts-because-of-dynamic-.patch
Patch: 0007-Issue-7224-CI-Test-Simplify-test_reserve_descriptor_.patch
Patch: 0008-Issue-7189-DSBLE0007-generates-incorrect-remediation.patch
Patch: 0009-Issue-7027-2nd-389-ds-base-OpenScanHub-Leaks-Detecte.patch
Patch: 0010-Issue-7223-Revert-index-scan-limits-for-system-index.patch
Patch: 0011-Issue-7223-Add-upgrade-function-to-remove-nsIndexIDL.patch
Patch: 0012-Issue-7223-Detect-and-log-index-ordering-mismatch-du.patch
Patch: 0013-Issue-7223-Add-dsctl-index-check-command-for-offline.patch
Patch: 0014-Issue-7096-2nd-During-replication-online-total-init-.patch
Patch: 0015-Issue-7076-6992-6784-6214-Fix-CI-test-failures-7077.patch
Patch: 0016-Issue-7076-Fix-revert_cache-never-called-in-modrdn-7.patch
Patch: 0017-Issue-6947-Fix-health_system_indexes_test.py.patch
Patch: 0018-Issue-7121-2nd-LeakSanitizer-various-leaks-during-re.patch
Patch: 0019-Issue-7150-Compressed-access-log-rotations-skipped-a.patch
Patch: 0006-Issue-7189-DSBLE0007-generates-incorrect-remediation.patch
Patch: 0007-Issue-7184-argparse.HelpFormatter-_format_actions_us.patch
Patch: 0008-Issue-7027-2nd-389-ds-base-OpenScanHub-Leaks-Detecte.patch
Patch: 0009-Issue-7213-MDB_BAD_VALSIZE-error-while-handling-VLV-.patch
Patch: 0010-Issue-6542-RPM-build-errors-on-Fedora-42.patch
Patch: 0011-Issue-6476-Fix-build-failure-with-GCC-15.patch
Patch: 0012-Issue-7223-Revert-index-scan-limits-for-system-index.patch
Patch: 0013-Issue-7223-Add-upgrade-function-to-remove-nsIndexIDL.patch
Patch: 0014-Issue-7223-Add-upgrade-function-to-remove-ancestorid.patch
Patch: 0015-Issue-7223-Detect-and-log-index-ordering-mismatch-du.patch
Patch: 0016-Issue-7223-Add-dsctl-index-check-command-for-offline.patch
Patch: 0017-Issue-7096-2nd-During-replication-online-total-init-.patch
Patch: 0018-Issue-7076-6992-6784-6214-Fix-CI-test-failures-7077.patch
Patch: 0019-Issue-7076-Fix-revert_cache-never-called-in-modrdn-7.patch
Patch: 0020-Issue-6947-Fix-health_system_indexes_test.py.patch
Patch: 0021-Issue-7121-2nd-LeakSanitizer-various-leaks-during-re.patch
Patch: 0022-Issue-7150-Compressed-access-log-rotations-skipped-a.patch
Patch: 0023-Issue-7224-CI-Test-Simplify-test_reserve_descriptor_.patch
Patch: 0024-Issue-7231-Sync-repl-tests-fail-in-FIPS-mode-due-to-.patch
Patch: 0025-Issue-7248-CLI-attribute-uniqueness-fix-usage-for-ex.patch
Patch: 0026-Issue-CLI-dsctl-db2index-needs-some-hardening-with-M.patch
Patch: 0027-Issue-7184-2nd-argparse.HelpFormatter-_format_action.patch
Patch: 0028-Issue-7213-2nd-MDB_BAD_VALSIZE-error-while-handling-.patch
Patch: 0029-Issue-7223-Use-lexicographical-order-for-ancestorid-.patch
Patch: 0030-Issue-7066-7052-allow-password-history-to-be-set-to-.patch
Patch: 0031-Issue-7243-UI-fix-certificate-table-and-modal.patch
Patch: 0032-Issue-7223-Remove-integerOrderingMatch-requirement-f.patch
Patch: 0033-Issue-7053-Remove-memberof_del_dn_from_groups-from-M.patch
%description
389 Directory Server is an LDAPv3 compliant server. The base package includes
@ -782,9 +796,6 @@ for dir in "$instbase"/slapd-* ; do
else
echo "instance $inst is not running" >> "$output" 2>&1 || :
fi
# Run index-check on all instances (running or not)
# This fixes index ordering mismatches from older versions
dsctl "$inst_name" index-check --fix >> "$output2" 2>&1 || :
ninst=$((ninst + 1))
done
@ -937,6 +948,15 @@ exit 0
%endif
%changelog
* Fri Feb 20 2026 Viktor Ashirov <vashirov@redhat.com> - 2.8.0-4
- Resolves: RHEL-117050 - Replication online reinitialization of a large database gets stalled. [rhel-9]
- Resolves: RHEL-123279 - The new ipahealthcheck test ipahealthcheck.ds.backends.BackendsCheck raises CRITICAL issue [rhel-9]
- Resolves: RHEL-140275 - ipa-healthcheck is complaining about missing or incorrectly configured system indexes. [rhel-9]
- Resolves: RHEL-142980 - Scalability issue of replication online initialization with large database [rhel-9]
- Resolves: RHEL-146899 - memory corruption in alias entry plugin [rhel-9]
- Resolves: RHEL-147212 - Access logs are not getting deleted as configured. [rhel-9]
- Resolves: RHEL-150907 - Remove memberof_del_dn_from_groups from MemberOf plugin [rhel-9]
* Thu Feb 12 2026 Viktor Ashirov <vashirov@redhat.com> - 2.8.0-3
- Resolves: RHEL-117050 - Replication online reinitialization of a large database gets stalled. [rhel-9]
- Resolves: RHEL-123244 - Attribute uniqueness is not enforced upon modrdn operation [rhel-9]