From 29d05cf11426181d4c3d95bf52fbbaab51b500a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:19:07 +0000 Subject: [PATCH 1/8] Initial plan From 2dc005ee50bb835a3c6130d3c1b757cf6ad63061 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:28:55 +0000 Subject: [PATCH 2/8] Fix referential integrity plugin silently bypassing check-references for modify operations Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenDJ/sessions/294a922f-c00a-410a-bc78-529db303e337 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- .../plugins/ReferentialIntegrityPlugin.java | 11 ++++----- .../ReferentialIntegrityPluginTestCase.java | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java index c5b74a008c..a1995b6d36 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java @@ -991,10 +991,9 @@ public PluginResult.PreOperation doPreOperation( } final List mods = modifyOperation.getModifications(); - final Entry entry = modifyOperation.getModifiedEntry(); /* Make sure the entry belongs to one of the configured naming contexts. */ - DN entryDN = entry.getName(); + DN entryDN = modifyOperation.getEntryDN(); DN entryBaseDN = getEntryBaseDN(entryDN); if (entryBaseDN == null) { @@ -1009,14 +1008,14 @@ public PluginResult.PreOperation doPreOperation( if (modType != ModificationType.ADD && modType != ModificationType.REPLACE) { - break; + continue; } - Attribute modifiedAttribute = entry.getAttribute(mod.getAttribute().getAttributeDescription()); - if (modifiedAttribute != null) + Attribute modifiedAttribute = mod.getAttribute(); + if (modifiedAttribute != null && !modifiedAttribute.isEmpty()) { PluginResult.PreOperation result = - isIntegrityMaintained(modifiedAttribute, entryDN, entryBaseDN); + isIntegrityMaintained(modifiedAttribute, entryDN, entryBaseDN); if (result.getResultCode() != ResultCode.SUCCESS) { return result; diff --git a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java index 531e27a969..2a6a312c30 100644 --- a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java +++ b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java @@ -1829,4 +1829,27 @@ public void testEnforceIntegrityModifyGroupAddMemberNC() throws Exception "member", "uid=user.1,ou=people,ou=dept,o=test"); assertEquals(modOperation.getResultCode(), ResultCode.SUCCESS); } + + @Test + public void testEnforceIntegrityModifyGroupAddMissingUniqueMember() throws Exception + { + replaceAttrEntry(configDN, "ds-cfg-enabled", "false"); + replaceAttrEntry(configDN, dsConfigPluginType, + "postoperationdelete", + "postoperationmodifydn", + "subordinatemodifydn", + "subordinatedelete", + "preoperationadd", + "preoperationmodify"); + addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com"); + replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true"); + replaceAttrEntry(configDN, dsConfigAttrType, "uniquemember"); + addAttrEntry(configDN, dsConfigAttrFiltMapping, + "uniquemember:(objectclass=person)"); + replaceAttrEntry(configDN, "ds-cfg-enabled", "true"); + + ModifyOperation modOperation = addAttrEntry(DN.valueOf(ugroup), + "uniquemember", "uid=user.100,ou=people,ou=dept,dc=example,dc=com"); + assertEquals(modOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION); + } } From c24b5842bd9cbb110b1cb3751575eb96124ef105 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 12:42:35 +0000 Subject: [PATCH 3/8] Add Portions copyright 2026 3A Systems, LLC to modified files Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenDJ/sessions/ab34f013-4824-4541-b086-32d55e871020 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- .../org/opends/server/plugins/ReferentialIntegrityPlugin.java | 1 + .../server/plugins/ReferentialIntegrityPluginTestCase.java | 1 + 2 files changed, 2 insertions(+) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java index a1995b6d36..7fe35534ab 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java @@ -14,6 +14,7 @@ * Copyright 2008-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. * Portions copyright 2011 profiq s.r.o. + * Portions copyright 2026 3A Systems, LLC. */ package org.opends.server.plugins; diff --git a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java index 2a6a312c30..671ec9efc5 100644 --- a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java +++ b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java @@ -14,6 +14,7 @@ * Copyright 2008-2010 Sun Microsystems, Inc. * Portions copyright 2011 profiq s.r.o. * Portions Copyright 2014-2016 ForgeRock AS. + * Portions copyright 2026 3A Systems, LLC. */ package org.opends.server.plugins; From 6f5f5e77ef25e4d4951b1cf16a7300ff19ea16b8 Mon Sep 17 00:00:00 2001 From: Valery Kharseko Date: Mon, 30 Mar 2026 10:52:36 +0300 Subject: [PATCH 4/8] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../plugins/ReferentialIntegrityPlugin.java | 21 +++++++++++++ .../ReferentialIntegrityPluginTestCase.java | 31 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java index 7fe35534ab..53852eb2c1 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java @@ -1015,6 +1015,27 @@ public PluginResult.PreOperation doPreOperation( Attribute modifiedAttribute = mod.getAttribute(); if (modifiedAttribute != null && !modifiedAttribute.isEmpty()) { + // Only enforce referential integrity on attributes that this plugin is configured to manage. + final AttributeType modifiedAttrType = modifiedAttribute.getAttributeType(); + boolean isManagedAttributeType = false; + if (modifiedAttrType != null && attributeTypes != null) + { + for (AttributeType configuredType : attributeTypes) + { + if (modifiedAttrType.equals(configuredType) + || modifiedAttrType.isSubTypeOf(configuredType)) + { + isManagedAttributeType = true; + break; + } + } + } + + if (!isManagedAttributeType) + { + // Skip integrity checks for attributes not configured for this plugin. + continue; + } PluginResult.PreOperation result = isIntegrityMaintained(modifiedAttribute, entryDN, entryBaseDN); if (result.getResultCode() != ResultCode.SUCCESS) diff --git a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java index 671ec9efc5..95d3235758 100644 --- a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java +++ b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java @@ -1853,4 +1853,35 @@ public void testEnforceIntegrityModifyGroupAddMissingUniqueMember() throws Excep "uniquemember", "uid=user.100,ou=people,ou=dept,dc=example,dc=com"); assertEquals(modOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION); } + + @Test + public void testEnforceIntegrityModifyGroupAddMissingUniqueMemberWithPriorDelete() throws Exception + { + // Configure the plugin in the same way as for the single-ADD test. + replaceAttrEntry(configDN, "ds-cfg-enabled", "false"); + replaceAttrEntry(configDN, dsConfigPluginType, + "postoperationdelete", + "postoperationmodifydn", + "subordinatemodifydn", + "subordinatedelete", + "preoperationadd", + "preoperationmodify"); + addAttrEntry(configDN, dsConfigBaseDN, "dc=example,dc=com"); + replaceAttrEntry(configDN, dsConfigEnforceIntegrity, "true"); + replaceAttrEntry(configDN, dsConfigAttrType, "uniquemember"); + addAttrEntry(configDN, dsConfigAttrFiltMapping, + "uniquemember:(objectclass=person)"); + replaceAttrEntry(configDN, "ds-cfg-enabled", "true"); + + // Build a modify request with a non-ADD/REPLACE modification first, + // followed by an ADD of a uniquemember referencing a missing DN. + final ModifyRequest modifyRequest = newModifyRequest(DN.valueOf(ugroup)); + modifyRequest.addModification(DELETE, "description"); + modifyRequest.addModification(ADD, "uniquemember", + "uid=user.100,ou=people,ou=dept,dc=example,dc=com"); + + final InternalClientConnection connection = getRootConnection(); + final ModifyOperation multiModOperation = connection.processModify(modifyRequest); + assertEquals(multiModOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION); + } } From 943ae0d0dc4f982ad869acb43a6438c26813882e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:03:25 +0000 Subject: [PATCH 5/8] Fix compilation error: use getAttributeDescription().getAttributeType() instead of getAttributeType() Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenDJ/sessions/e4c3f74e-8d96-4b8f-bdcc-91c814ffb791 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com> --- .../org/opends/server/plugins/ReferentialIntegrityPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java index 53852eb2c1..0413aa9dfc 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java @@ -1016,7 +1016,7 @@ public PluginResult.PreOperation doPreOperation( if (modifiedAttribute != null && !modifiedAttribute.isEmpty()) { // Only enforce referential integrity on attributes that this plugin is configured to manage. - final AttributeType modifiedAttrType = modifiedAttribute.getAttributeType(); + final AttributeType modifiedAttrType = modifiedAttribute.getAttributeDescription().getAttributeType(); boolean isManagedAttributeType = false; if (modifiedAttrType != null && attributeTypes != null) { From 148028b123154f856a5a7bc4afa46e3f7e664599 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Mon, 30 Mar 2026 16:03:12 +0300 Subject: [PATCH 6/8] FIX ReferentialIntegrityPluginTestCase.java:[1878,40] error: cannot find symbol symbol: method newModifyRequest( --- .../server/plugins/ReferentialIntegrityPluginTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java index 95d3235758..211ecd7042 100644 --- a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java +++ b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java @@ -1875,7 +1875,7 @@ public void testEnforceIntegrityModifyGroupAddMissingUniqueMemberWithPriorDelete // Build a modify request with a non-ADD/REPLACE modification first, // followed by an ADD of a uniquemember referencing a missing DN. - final ModifyRequest modifyRequest = newModifyRequest(DN.valueOf(ugroup)); + final ModifyRequest modifyRequest = Requests.newModifyRequest(DN.valueOf(ugroup)); modifyRequest.addModification(DELETE, "description"); modifyRequest.addModification(ADD, "uniquemember", "uid=user.100,ou=people,ou=dept,dc=example,dc=com"); From 1c70d6e179b5a2fde2a9b7a42e4047573f527ba1 Mon Sep 17 00:00:00 2001 From: Valery Kharseko Date: Mon, 30 Mar 2026 19:57:56 +0300 Subject: [PATCH 7/8] The cleanest fix is to add a description attribute to ugroup before the multi-modify Added a description attribute to ugroup to ensure DELETE modification succeeds. --- .../server/plugins/ReferentialIntegrityPluginTestCase.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java index 211ecd7042..85e70f09f2 100644 --- a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java +++ b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java @@ -1872,7 +1872,10 @@ public void testEnforceIntegrityModifyGroupAddMissingUniqueMemberWithPriorDelete addAttrEntry(configDN, dsConfigAttrFiltMapping, "uniquemember:(objectclass=person)"); replaceAttrEntry(configDN, "ds-cfg-enabled", "true"); - + + // Ensure 'description' exists on ugroup so the DELETE modification succeeds. + addAttrEntry(DN.valueOf(ugroup), "description", "test description"); + // Build a modify request with a non-ADD/REPLACE modification first, // followed by an ADD of a uniquemember referencing a missing DN. final ModifyRequest modifyRequest = Requests.newModifyRequest(DN.valueOf(ugroup)); From c7cafb500e854265534423c7a0fa676265be85c5 Mon Sep 17 00:00:00 2001 From: Valery Kharseko Date: Tue, 31 Mar 2026 10:45:15 +0300 Subject: [PATCH 8/8] Update opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../server/plugins/ReferentialIntegrityPluginTestCase.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java index 85e70f09f2..068e30833a 100644 --- a/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java +++ b/opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java @@ -1872,10 +1872,9 @@ public void testEnforceIntegrityModifyGroupAddMissingUniqueMemberWithPriorDelete addAttrEntry(configDN, dsConfigAttrFiltMapping, "uniquemember:(objectclass=person)"); replaceAttrEntry(configDN, "ds-cfg-enabled", "true"); - + // Ensure 'description' exists on ugroup so the DELETE modification succeeds. addAttrEntry(DN.valueOf(ugroup), "description", "test description"); - // Build a modify request with a non-ADD/REPLACE modification first, // followed by an ADD of a uniquemember referencing a missing DN. final ModifyRequest modifyRequest = Requests.newModifyRequest(DN.valueOf(ugroup));