From ba2b247fb296f883e9efcd46308a64dfd6cca0a4 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 16 Jul 2026 05:17:57 +0000
Subject: [PATCH] [#737] Fix cn=changelog search failing when aliases are dereferenced (#740)
---
opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java | 80 ++++++++++++++++++++++++++++++++++++++++
opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java | 14 ++++++
2 files changed, 93 insertions(+), 1 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java
index 60c2f37..249f7b0 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/ChangelogBackend.java
@@ -297,6 +297,14 @@
return true;
}
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Only the base changelog entry can be retrieved by DN. Change records are streamed by
+ * {@link #search(SearchOperation)}, which needs the search filter and the change number range to
+ * position its cursors, so they cannot be looked up individually and {@code null} is returned for
+ * them. None of them is ever an alias, so callers dereferencing aliases get the answer they need.
+ */
@Override
public Entry getEntry(final DN entryDN) throws DirectoryException
{
@@ -305,7 +313,11 @@
throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(),
ERR_BACKEND_GET_ENTRY_NULL.get(getBackendID()));
}
- throw new RuntimeException("Not implemented");
+ if (CHANGELOG_BASE_DN.equals(entryDN))
+ {
+ return buildBaseChangelogEntry();
+ }
+ return null;
}
@Override
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java
index 8e60c19..d909e5b 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/ChangelogBackendTestCase.java
@@ -12,6 +12,7 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2014-2016 ForgeRock AS.
+ * Portions Copyright 2026 3A Systems, LLC.
*/
package org.opends.server.backends;
@@ -47,6 +48,7 @@
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
import org.forgerock.opendj.ldap.RDN;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
@@ -783,6 +785,84 @@
debugInfo(testName, "Ending test successfully");
}
+ /**
+ * The alias dereferencing policies which make the server look the search base entry up before
+ * handing the search over to the backend.
+ */
+ @DataProvider
+ Object[][] getBaseEntryLookupDerefPolicies()
+ {
+ return new Object[][] {
+ { DereferenceAliasesPolicy.ALWAYS },
+ { DereferenceAliasesPolicy.FINDING_BASE },
+ // only looks the base entry up for subtree searches, which newSearchRequest() uses
+ { DereferenceAliasesPolicy.IN_SEARCHING },
+ };
+ }
+
+ /**
+ * Dereferencing aliases makes the server read the base entry before searching, so the changelog
+ * backend must be able to return it rather than failing the whole search.
+ *
+ * @see <a href="https://github.com/OpenIdentityPlatform/OpenDJ/issues/737">issue #737</a>
+ */
+ @Test(dataProvider = "getBaseEntryLookupDerefPolicies")
+ public void searchWhenNoChangesShouldReturnRootEntryOnlyWhenDereferencingAliases(
+ DereferenceAliasesPolicy derefPolicy) throws Exception
+ {
+ String testName = "EmptyRSDeref/" + derefPolicy;
+ debugInfo(testName, "Starting test\n\n");
+
+ SearchRequest request = newSearchRequest("(objectclass=*)").setDereferenceAliasesPolicy(derefPolicy);
+ searchChangelog(request, 1, SUCCESS, testName);
+
+ debugInfo(testName, "Ending test successfully");
+ }
+
+ /**
+ * Dereferencing aliases used to fail change number searches with an unexpected error, which broke
+ * every client defaulting to it, JNDI-based ones in particular.
+ *
+ * @see <a href="https://github.com/OpenIdentityPlatform/OpenDJ/issues/737">issue #737</a>
+ */
+ @Test(dataProvider = "getBaseEntryLookupDerefPolicies")
+ public void searchInChangeNumberModeWhenDereferencingAliases(DereferenceAliasesPolicy derefPolicy) throws Exception
+ {
+ String testName = "ChangeNumberDeref/" + derefPolicy;
+ debugInfo(testName, "Starting test\n\n");
+
+ CSN[] csns = generateAndPublishUpdateMsgForEachOperationType(testName, false);
+
+ SearchRequest request = newSearchRequest("(changenumber>=1)").setDereferenceAliasesPolicy(derefPolicy);
+ InternalSearchOperation searchOp = searchChangelog(request, csns.length, SUCCESS, testName);
+
+ assertEntriesForEachOperationType(searchOp.getSearchEntries(), 1, testName, USER1_ENTRY_UUID, csns);
+
+ debugInfo(testName, "Ending test successfully");
+ }
+
+ /**
+ * The base changelog entry is the only one the backend can return by DN: change records need the
+ * search parameters to be located, so they are only reachable through a search.
+ */
+ @Test
+ public void getEntryShouldReturnBaseEntryOnly() throws Exception
+ {
+ String testName = "getEntry";
+ debugInfo(testName, "Starting test\n\n");
+
+ generateAndPublishUpdateMsgForEachOperationType(testName, false);
+
+ final LocalBackend<?> backend = TestCaseUtils.getServerContext().getBackendConfigManager()
+ .getLocalBackendById(ChangelogBackend.BACKEND_ID);
+ assertEquals(backend.getEntry(ChangelogBackend.CHANGELOG_BASE_DN).getName(),
+ ChangelogBackend.CHANGELOG_BASE_DN);
+ assertNull(backend.getEntry(DN.valueOf("changeNumber=1,cn=changelog")));
+ assertNull(backend.getEntry(DN.valueOf("changeNumber=1000,cn=changelog")));
+
+ debugInfo(testName, "Ending test successfully");
+ }
+
@Test
public void operationalAndVirtualAttributesShouldNotBeVisibleOutsideRootDSE() throws Exception
{
--
Gitblit v1.10.0