From 60be91d8768178e3c4bbd8f01b713be382b9e9cf Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Tue, 06 Oct 2026 07:13:14 +0000
Subject: [PATCH] [#1172] Refuse a second check-references-filter-criteria value for an attribute type, and enforce all the filters of a configuration that already has one (#1174)
---
opendj-server-legacy/src/main/java/org/opends/server/plugins/ReferentialIntegrityPlugin.java | 69 +++++++++++++++++++++++++++++++---
1 files changed, 62 insertions(+), 7 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 2030243..635f08b 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
@@ -32,6 +32,7 @@
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
@@ -172,7 +173,7 @@
{
LinkedList<LocalizableMessage> unacceptableReasons = new LinkedList<>();
- if (!isConfigurationAcceptableIgnoringCheckReferencesPluginTypes(pluginCfg, unacceptableReasons))
+ if (!isConfigurationLoadable(pluginCfg, unacceptableReasons))
{
throw new ConfigException(unacceptableReasons.getFirst());
}
@@ -188,6 +189,13 @@
{
logger.warn(WARN_PLUGIN_REFERENT_CHECK_REFERENCES_WITHOUT_PLUGIN_TYPE.get(pluginCfg.dn(), t, t));
}
+ // Likewise for two filter criteria of one attribute type (a configuration stored before issue #1172): the
+ // plugin is loaded, and enforces all of their filters.
+ for (Map.Entry<AttributeType, List<String>> e : getDuplicateFilterCriteria(pluginCfg).entrySet())
+ {
+ logger.warn(WARN_PLUGIN_REFERENT_DUPLICATE_FILTER_CRITERIA.get(
+ pluginCfg.dn(), e.getKey().getNameOrOID(), String.join(", ", e.getValue())));
+ }
applyConfigurationChange(pluginCfg);
@@ -216,8 +224,9 @@
LinkedHashSet<AttributeType> newAttributeTypes =
new LinkedHashSet<>(newConfiguration.getAttributeType());
- // Load the attribute-filter mapping
- LinkedHashMap<AttributeType, SearchFilter> newAttrFiltMap = new LinkedHashMap<>();
+ // Load the attribute-filter mapping. An attribute type has more than one filter only in a configuration stored
+ // before issue #1172, and a reference held by it then has to match all of them.
+ LinkedHashMap<AttributeType, List<SearchFilter>> newAttrFilters = new LinkedHashMap<>();
for (String attrFilt : newConfiguration.getCheckReferencesFilterCriteria())
{
@@ -228,7 +237,8 @@
AttributeType attrType = DirectoryServer.getInstance().getServerContext().getSchema().getAttributeType(attr);
try
{
- newAttrFiltMap.put(attrType, SearchFilter.createFilterFromString(filtStr));
+ SearchFilter filter = SearchFilter.createFilterFromString(filtStr);
+ newAttrFilters.computeIfAbsent(attrType, k -> new ArrayList<>()).add(filter);
}
catch (DirectoryException unexpected)
{
@@ -237,6 +247,13 @@
}
}
+ LinkedHashMap<AttributeType, SearchFilter> newAttrFiltMap = new LinkedHashMap<>();
+ for (Map.Entry<AttributeType, List<SearchFilter>> e : newAttrFilters.entrySet())
+ {
+ List<SearchFilter> filters = e.getValue();
+ newAttrFiltMap.put(e.getKey(), filters.size() == 1 ? filters.get(0) : SearchFilter.createANDFilter(filters));
+ }
+
//User is not allowed to change the logfile name, append a message that the
//server needs restarting for change to take effect.
// The first time the plugin is initialised the 'logFileName' is
@@ -272,17 +289,45 @@
{
ReferentialIntegrityPluginCfg pluginCfg =
(ReferentialIntegrityPluginCfg) configuration;
- boolean isAcceptable = isConfigurationAcceptableIgnoringCheckReferencesPluginTypes(pluginCfg, unacceptableReasons);
+ boolean isAcceptable = isConfigurationLoadable(pluginCfg, unacceptableReasons);
for (PluginCfgDefn.PluginType t : getMissingCheckReferencesPluginTypes(pluginCfg))
{
isAcceptable = false;
unacceptableReasons.add(ERR_PLUGIN_REFERENT_CHECK_REFERENCES_WITHOUT_PLUGIN_TYPE.get(t, t));
}
+ for (Map.Entry<AttributeType, List<String>> e : getDuplicateFilterCriteria(pluginCfg).entrySet())
+ {
+ isAcceptable = false;
+ unacceptableReasons.add(ERR_PLUGIN_REFERENT_DUPLICATE_FILTER_CRITERIA.get(
+ e.getKey().getNameOrOID(), String.join(", ", e.getValue())));
+ }
return isAcceptable;
}
/**
+ * Returns the check-references-filter-criteria values of each attribute type that more than one of them names,
+ * whether with the same spelling, with another name or the OID of the type, or with a space before the colon.
+ * Before issue #1172 only one of them was enforced, the one that sorts last.
+ */
+ private static Map<AttributeType, List<String>> getDuplicateFilterCriteria(ReferentialIntegrityPluginCfg pluginCfg)
+ {
+ Map<AttributeType, List<String>> valuesByType = new LinkedHashMap<>();
+ for (String attrFilt : pluginCfg.getCheckReferencesFilterCriteria())
+ {
+ AttributeType attrType = DirectoryServer.getInstance().getServerContext().getSchema()
+ .getAttributeType(splitFilterCriteria(attrFilt)[0]);
+ // An attribute missing from the schema is refused as not listed in attribute-type.
+ if (!attrType.isPlaceHolder())
+ {
+ valuesByType.computeIfAbsent(attrType, k -> new ArrayList<>()).add(attrFilt);
+ }
+ }
+ valuesByType.values().removeIf(values -> values.size() < 2);
+ return valuesByType;
+ }
+
+ /**
* Returns the plugin types {@code check-references} needs that the configuration does not list. The references
* are checked in the pre-operation add and modify hooks, which the plugin manager only calls for the plugin types
* listed in the configuration.
@@ -299,7 +344,13 @@
return missing;
}
- private boolean isConfigurationAcceptableIgnoringCheckReferencesPluginTypes(
+ /**
+ * Checks what a configuration must satisfy for the plugin to load it. Unlike
+ * {@link #isConfigurationAcceptable(PluginCfg, List)}, it lets through a configuration that lacks the plugin types
+ * {@code check-references} needs (issue #1118) or has two filter criteria for one attribute type (issue #1172):
+ * one stored before those checks existed is loaded with a warning instead.
+ */
+ private boolean isConfigurationLoadable(
ReferentialIntegrityPluginCfg pluginCfg, List<LocalizableMessage> unacceptableReasons)
{
boolean isAcceptable = true;
@@ -418,7 +469,11 @@
ReferentialIntegrityPluginCfg configuration,
List<LocalizableMessage> unacceptableReasons)
{
- return isConfigurationAcceptable(configuration, unacceptableReasons);
+ // A disabled plugin checks no references: let a configuration loaded with a warning be disabled, as
+ // PluginConfigManager does, and refuse it again when it is enabled.
+ return configuration.isEnabled()
+ ? isConfigurationAcceptable(configuration, unacceptableReasons)
+ : isConfigurationLoadable(configuration, unacceptableReasons);
}
@SuppressWarnings("unchecked")
--
Gitblit v1.10.0