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..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 @@ -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; @@ -991,10 +992,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 +1009,35 @@ 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()) { + // Only enforce referential integrity on attributes that this plugin is configured to manage. + final AttributeType modifiedAttrType = modifiedAttribute.getAttributeDescription().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); + 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..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 @@ -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; @@ -1829,4 +1830,60 @@ 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); + } + + @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"); + + // 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)); + 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); + } }