This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 5188d47b5489 CAMEL-25509: camel-ldif - Apply newsuperior of modrdn,
and keep the parent of moddn without it
5188d47b5489 is described below
commit 5188d47b548927db164bd5a0b631224c219a96df
Author: allthingssecurity <[email protected]>
AuthorDate: Sat Oct 10 12:40:48 2026 +0530
CAMEL-25509: camel-ldif - Apply newsuperior of modrdn, and keep the parent
of moddn without it
RFC 2849 defines changetype modrdn and moddn as the same change: the
entry gets the new RDN, and moves under newsuperior only when it is
given. LdifProducer handled them as two operations: a modrdn with
newsuperior renamed the entry in place without moving it, and a moddn
without newsuperior tried to move the entry to a one-RDN DN directly
under the root, which the server rejects.
Both change types now move the entry with moveAndRename when newsuperior
is given, and rename it in place otherwise. The requests for moddn with
newsuperior and modrdn without it are unchanged.
Closes #27642
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../apache/camel/component/ldif/LdifProducer.java | 23 ++--
.../apache/camel/component/ldif/LdifModDnTest.java | 122 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 6 +
3 files changed, 141 insertions(+), 10 deletions(-)
diff --git
a/components/camel-ldif/src/main/java/org/apache/camel/component/ldif/LdifProducer.java
b/components/camel-ldif/src/main/java/org/apache/camel/component/ldif/LdifProducer.java
index 8b73240ea138..0476b13821b8 100644
---
a/components/camel-ldif/src/main/java/org/apache/camel/component/ldif/LdifProducer.java
+++
b/components/camel-ldif/src/main/java/org/apache/camel/component/ldif/LdifProducer.java
@@ -158,17 +158,20 @@ public class LdifProducer extends DefaultProducer {
LOG.debug("attempting delete of {}", ldifEntry);
}
conn.delete(ldifEntry.getDn());
- } else if (ldifEntry.isChangeModDn()) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("attempting DN move of {}", ldifEntry);
- }
- conn.moveAndRename(ldifEntry.getDn(), new
Dn(ldifEntry.getNewRdn(), ldifEntry.getNewSuperior()),
- ldifEntry.isDeleteOldRdn());
- } else if (ldifEntry.isChangeModRdn()) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("attempting RDN move of {}", ldifEntry);
+ } else if (ldifEntry.isChangeModDn() ||
ldifEntry.isChangeModRdn()) {
+ // RFC 2849: modrdn and moddn are the same change type; the
entry moves only with a newsuperior
+ if (ldifEntry.getNewSuperior() != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("attempting DN move of {}", ldifEntry);
+ }
+ conn.moveAndRename(ldifEntry.getDn(), new
Dn(ldifEntry.getNewRdn(), ldifEntry.getNewSuperior()),
+ ldifEntry.isDeleteOldRdn());
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("attempting RDN move of {}", ldifEntry);
+ }
+ conn.rename(ldifEntry.getDn(), new
Rdn(ldifEntry.getNewRdn()), ldifEntry.isDeleteOldRdn());
}
- conn.rename(ldifEntry.getDn(), new Rdn(ldifEntry.getNewRdn()),
ldifEntry.isDeleteOldRdn());
}
LOG.debug("ldif success");
diff --git
a/components/camel-ldif/src/test/java/org/apache/camel/component/ldif/LdifModDnTest.java
b/components/camel-ldif/src/test/java/org/apache/camel/component/ldif/LdifModDnTest.java
new file mode 100644
index 000000000000..43ea3dea3346
--- /dev/null
+++
b/components/camel-ldif/src/test/java/org/apache/camel/component/ldif/LdifModDnTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.ldif;
+
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.name.Rdn;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * RFC 2849: "modrdn" and "moddn" are the same change type. The entry gets the
new RDN, and moves under the new superior
+ * only when "newsuperior" is given. The connection is a stub that records the
resulting DN, so this test needs no LDAP
+ * server.
+ */
+class LdifModDnTest extends CamelTestSupport {
+
+ private final List<String> renames = new ArrayList<>();
+
+ @BindToRegistry("conn")
+ private final LdapConnection conn = (LdapConnection)
Proxy.newProxyInstance(
+ LdapConnection.class.getClassLoader(), new Class<?>[] {
LdapConnection.class },
+ (proxy, method, args) -> {
+ switch (method.getName()) {
+ case "hashCode" -> {
+ return System.identityHashCode(proxy);
+ }
+ case "equals" -> {
+ return proxy == args[0];
+ }
+ case "toString" -> {
+ return "LdapConnection stub";
+ }
+ case "rename" -> {
+ Dn dn = (Dn) args[0];
+ renames.add(new Dn((Rdn) args[1], dn.getParent()) + "
deleteOldRdn=" + args[2]);
+ }
+ case "moveAndRename" ->
+ renames.add(args[1] + " deleteOldRdn=" + args[2]);
+ case "close" -> {
+ // the producer closes the connection after the LDIF
is processed
+ }
+ default ->
+ // the producer calls no other method: record it so
that the assertion shows it
+ renames.add("unexpected call: " + method.getName());
+ }
+ return method.getReturnType() == boolean.class ? Boolean.FALSE
: null;
+ });
+
+ @BeforeEach
+ void clear() {
+ renames.clear();
+ }
+
+ @Test
+ void modRdnWithNewSuperiorMovesTheEntry() {
+ assertEquals("uid=b,ou=new,dc=example,dc=org deleteOldRdn=true",
+ rename("modrdn", "1", "newsuperior:
ou=new,dc=example,dc=org\n"));
+ }
+
+ @Test
+ void modDnWithoutNewSuperiorKeepsTheParent() {
+ assertEquals("uid=b,ou=old,dc=example,dc=org deleteOldRdn=true",
rename("moddn", "1", ""));
+ }
+
+ @Test
+ void modDnWithNewSuperiorMovesTheEntry() {
+ assertEquals("uid=b,ou=new,dc=example,dc=org deleteOldRdn=false",
+ rename("moddn", "0", "newsuperior:
ou=new,dc=example,dc=org\n"));
+ }
+
+ @Test
+ void modRdnWithoutNewSuperiorKeepsTheParent() {
+ assertEquals("uid=b,ou=old,dc=example,dc=org deleteOldRdn=false",
rename("modrdn", "0", ""));
+ }
+
+ private String rename(String changeType, String deleteOldRdn, String
newSuperior) {
+ String ldif = "version: 1\n"
+ + "dn: uid=a,ou=old,dc=example,dc=org\n"
+ + "changetype: " + changeType + "\n"
+ + "newrdn: uid=b\n"
+ + "deleteoldrdn: " + deleteOldRdn + "\n"
+ + newSuperior;
+ List<?> result = template.requestBody("direct:ldif", ldif, List.class);
+ assertEquals(List.of("success"), result);
+ assertEquals(1, renames.size(), "calls on the connection: " + renames);
+ return renames.get(0);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:ldif").to("ldif:conn");
+ }
+ };
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index fb3a926c22c2..5a2073eb349b 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -1004,6 +1004,12 @@ Before, the first five fell back to the charset of the
exchange (UTF-8 by defaul
unmarshal sets `CamelCharsetName` to it. A message whose MSH-18 names one of
these charsets but that is actually
encoded in another one (such as UTF-8) is now decoded in the charset it names.
+=== camel-ldif - modrdn and moddn records
+
+A `changetype: modrdn` record with a `newsuperior` line now moves the entry
under the new superior, as RFC 2849
+defines; before, the entry was only renamed in place. A `changetype: moddn`
record without `newsuperior` now renames
+the entry in place; before, it was moved directly under the root (which
servers normally refuse).
+
=== camel-servlet and camel-jetty - String response in the charset of the
Content-Type
The servlet and Jetty consumers converted a `String` response body with the
charset of the exchange (UTF-8 by