shashank created CAMEL-25509:
--------------------------------

             Summary: camel-ldif - "changetype: modrdn" ignores newsuperior, 
and "changetype: moddn" without newsuperior moves the entry to the root
                 Key: CAMEL-25509
                 URL: https://issues.apache.org/jira/browse/CAMEL-25509
             Project: Camel
          Issue Type: Bug
            Reporter: shashank


RFC 2849 defines {{modrdn}} and {{moddn}} as the same change type 
({{change-moddn = ("modrdn" / "moddn") SEP "newrdn:" ... "deleteoldrdn:" ... 
[SEP "newsuperior:" ...]}}): the entry gets the new RDN, and moves under the 
new superior only when {{newsuperior}} is given (RFC 4511 4.9). 
{{LdifProducer.processLdifEntry}} (lines 161-172 at main c578a42a776d) treats 
them as two different operations:

{code:java}
} else if (ldifEntry.isChangeModDn()) {
    conn.moveAndRename(ldifEntry.getDn(), new Dn(ldifEntry.getNewRdn(), 
ldifEntry.getNewSuperior()), ...);
} else if (ldifEntry.isChangeModRdn()) {
    conn.rename(ldifEntry.getDn(), new Rdn(ldifEntry.getNewRdn()), ...);
}
{code}

* A {{modrdn}} record with {{newsuperior}} renames the entry in place: it is 
not moved, and the result for the record is still {{success}}.
* A {{moddn}} record without {{newsuperior}} (a plain rename, valid per RFC 
2849) builds {{new Dn(newRdn, null)}}, which is the one-RDN DN {{uid=b}}, so 
the request moves the entry directly under the root and the server rejects it.

The Apache LDAP API's {{LdifReader}} keeps the two keywords apart 
({{ChangeType.ModRdn}}/{{ModDn}}) and reads {{newsuperior}} for both.

h3. Reproduction

New {{LdifModDnTest}}, with a stub {{LdapConnection}} (a 
{{java.lang.reflect.Proxy}}) that records the DN each rename/move gives the 
entry, so no LDAP server is needed. On main:
{noformat}
modRdnWithNewSuperiorMovesTheEntry: expected: <uid=b,ou=new,dc=example,dc=org 
deleteOldRdn=true> but was: <uid=b,ou=old,dc=example,dc=org deleteOldRdn=true>
modDnWithoutNewSuperiorKeepsTheParent: expected: 
<uid=b,ou=old,dc=example,dc=org deleteOldRdn=true> but was: <uid=b 
deleteOldRdn=true>
{noformat}
The controls ({{moddn}} with {{newsuperior}}, {{modrdn}} without it, as in 
{{LdifRouteIT}}) pass.

h3. Proposed fix

One branch for both change types: {{moveAndRename(dn, new Dn(newRdn, 
newSuperior), deleteOldRdn)}} when {{newsuperior}} is given, {{rename(dn, new 
Rdn(newRdn), deleteOldRdn)}} otherwise. The requests for the two cases that 
worked are unchanged; a short upgrade guide note for 4.23 describes the two 
changed cases. camel-ldif unit tests with the fix: 5, 0 failures 
({{LdifRouteIT}} needs the OpenLDAP container and was not run; its modDn/modRdn 
cases send the same requests as before).

Found with a Lean 4 model of the dispatch (DNs as lists of RDNs, the effect of 
rename and moveAndRename on the entry's DN): "the new DN is newrdn under 
newsuperior if given, else under the current parent, for both keywords" fails 
for the two records above, and is proved to fail for every {{modrdn}} with a 
{{newsuperior}} other than the current parent and every {{moddn}} without 
{{newsuperior}} of an entry not directly under the root. The fix is proved 
correct for all records, and to send exactly main's request in the two cases 
main got right. Then confirmed with the real producer and LDAP API as above.

Affected: main, camel-4.18.x, camel-4.14.x (same code, GitHub contents API). 
The component was added in 2017 (CAMEL-11778).

Duplicate check (2026-10-09): JIRA text "ldif" (9 hits: component creation, URL 
opt-in CAMEL-24297, upgrades), "modrdn" (none); GitHub pull requests "ldif 
modrdn", "ldif newsuperior", "LdifProducer": none (only the CVE pin #25622).

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to