Fix ReferentialIntegrityPlugin silently bypassing check-references on modify operations#600
Fix ReferentialIntegrityPlugin silently bypassing check-references on modify operations#600
Conversation
…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>
Agent-Logs-Url: https://github.com/OpenIdentityPlatform/OpenDJ/sessions/ab34f013-4824-4541-b086-32d55e871020 Co-authored-by: vharseko <6818498+vharseko@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes Referential Integrity Plugin enforcement for check-references: true during modify operations so missing referenced entries (e.g., deleted users added to group membership attributes) correctly yield CONSTRAINT_VIOLATION instead of being silently accepted.
Changes:
- Fix
doPreOperation(PreOperationModifyOperation)to continue scanning modifications (not break early) and to validate the attribute values being added/replaced. - Add a regression test ensuring adding a missing
uniqueMembervia modify fails withCONSTRAINT_VIOLATION. - Add 2026 “Portions copyright …” headers to the modified source and test files.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java |
Corrects modify pre-op integrity checks to properly validate added/replaced reference values. |
opendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.java |
Adds coverage for missing uniqueMember on modify when check-references is enabled. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (modifiedAttribute != null) | ||
| Attribute modifiedAttribute = mod.getAttribute(); | ||
| if (modifiedAttribute != null && !modifiedAttribute.isEmpty()) | ||
| { |
There was a problem hiding this comment.
doPreOperation(PreOperationModifyOperation) now validates referential integrity for every ADD/REPLACE modification using mod.getAttribute(), regardless of whether the modified attribute is one of the plugin’s configured attribute-types. This can cause unintended failures (e.g., DN parsing exceptions / ResultCode.OTHER) when clients add/replace a non-DN attribute that the plugin is not meant to manage. Consider skipping modifications whose attribute type is not in the configured attributeTypes set (or not a subtype of one) before calling isIntegrityMaintained.
| { | |
| { | |
| // 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; | |
| } |
| ModifyOperation modOperation = addAttrEntry(DN.valueOf(ugroup), | ||
| "uniquemember", "uid=user.100,ou=people,ou=dept,dc=example,dc=com"); | ||
| assertEquals(modOperation.getResultCode(), ResultCode.CONSTRAINT_VIOLATION); | ||
| } |
There was a problem hiding this comment.
The added test covers the “attribute absent on entry” path for uniqueMember, but it doesn’t exercise the other reported bypass where a non-ADD/REPLACE modification appears before an ADD in the same modify request (the break→continue fix). Consider adding a modify test that sends multiple modifications (e.g., DELETE some unrelated attribute first, then ADD uniquemember with a missing DN) and asserts 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); | |
| } |
Portions copyright 2026 3A Systems, LLCtoReferentialIntegrityPlugin.java(modified file)Portions copyright 2026 3A Systems, LLCtoReferentialIntegrityPluginTestCase.java(modified file)Original prompt
Problem
Fixes issue #579: When
check-references: trueis configured in the Referential Integrity Plugin, adding a deleted (non-existent) user to a group vialdapmodify(modifyinguniqueMemberormember) succeeds without throwing aCONSTRAINT_VIOLATIONerror. The check is silently bypassed.Root Cause
There are two bugs in the
doPreOperation(PreOperationModifyOperation modifyOperation)method in:opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.javaBug 1:
breakinstead ofcontinue(lines 1009–1013)If the first modification in the list is not
ADDorREPLACE(e.g.DELETE), the loop exits entirely and no integrity check is performed for any subsequentADDmodifications.Fix: Replace
breakwithcontinue.Bug 2: Reading attribute from the pre-modification entry instead of from the modification itself (line 1015)
Here
entry = modifyOperation.getModifiedEntry()is the current state of the entry before modifications are applied. If the attribute (e.g.uniqueMember) does not yet exist on the entry (first-time add to a group),getAttribute()returnsnull, theif (modifiedAttribute != null)check fails silently, and the integrity check is skipped entirely.Fix: Read the attribute directly from the modification (
mod.getAttribute()), which contains exactly the values being added/replaced.Required Fix
In
doPreOperation(PreOperationModifyOperation modifyOperation), replace:With:
Additional Notes
entryvariable (obtained viamodifyOperation.getModifiedEntry()) is no longer needed after this fix and can be removed (thoughentryDNstill needs to be obtained — it can come from the modification's attribute or from the operation itself viamodifyOperation.getEntryDN()).testEnforceIntegrityModifyGroupAddMissingMemberverified) that:uniqueMemberof a group.CONSTRAINT_VIOLATION.testEnforceIntegrityModifyGroupAddMissingMemberinReferentialIntegrityPluginTestCase.javaalready covers thememberattribute but should be extended to coveruniqueMemberas well, matching the user's configuration.Files to Modify
opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.javaopendj-server-legacy/src/test/java/org/opends/server/plugins/ReferentialIntegrityPluginTestCase.javaThe following is the prior conversation context from the user's chat exploration (may be truncated):
User: прокомментируй
Assistant: Вот подробный анализ баг-репорта на основе изучения исходного кода.
Анализ Issue #579
Суть проблемы
Пользователь настроил Referential Integrity Plugin с
check-references: true, ожидая, что при попытке добавить несуществующего пользователя в группу (uniqueMember) операция будет отклонена с ошибкой. Однакоldapmodifyзавершается успешно, и "мёртвая" ссылка попадает в атрибут.Причина: баг в
doPreOperation(PreOperationModifyOperation)В коде плагина есть критическая ошибка в обработке
modify-операций: