From 076c78aa32f39fe76d74dca79b550f3049e2baa5 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 25 Nov 2016 18:00:18 +0000
Subject: [PATCH] OPENDJ-3189: Implement EL expression support in cn=config
---
opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java | 99 ++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 85 insertions(+), 14 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java
index 6517cc8..42fea94 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java
@@ -15,6 +15,7 @@
*/
package org.opends.server.config;
+import static org.forgerock.opendj.ldap.Entries.unmodifiableEntry;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.extensions.ExtensionsConstants.*;
@@ -48,6 +49,7 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageBuilder;
+import org.forgerock.i18n.LocalizableMessageDescriptor.Arg4;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.adapter.server3x.Converters;
import org.forgerock.opendj.config.ConfigurationFramework;
@@ -57,6 +59,7 @@
import org.forgerock.opendj.config.server.spi.ConfigChangeListener;
import org.forgerock.opendj.config.server.spi.ConfigDeleteListener;
import org.forgerock.opendj.config.server.spi.ConfigurationRepository;
+import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.CancelRequestListener;
import org.forgerock.opendj.ldap.CancelledResultException;
@@ -66,6 +69,8 @@
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.LdapResultHandler;
+import org.forgerock.opendj.ldap.LinkedAttribute;
+import org.forgerock.opendj.ldap.LinkedHashMapEntry;
import org.forgerock.opendj.ldap.MemoryBackend;
import org.forgerock.opendj.ldap.RequestContext;
import org.forgerock.opendj.ldap.ResultCode;
@@ -341,12 +346,8 @@
@Override
public Entry getEntry(final DN dn) throws ConfigException
{
- Entry entry = backend.get(dn);
- if (entry != null)
- {
- entry = Entries.unmodifiableEntry(entry);
- }
- return entry;
+ final Entry entry = backend.get(dn);
+ return entry == null ? null : unmodifiableEntry(evaluateEntryIfPossible(entry));
}
/**
@@ -451,12 +452,15 @@
final DN parentDN = retrieveParentDNForAdd(entryDN);
+ // If the entry contains any expressions then these must be evaluated before passing to listeners.
+ final Entry evaluatedEntry = evaluateEntry(entry, ERR_CONFIG_FILE_ADD_REJECTED_DUE_TO_EVALUATION_FAILURE);
+
// Iterate through add listeners to make sure the new entry is acceptable.
final List<ConfigAddListener> addListeners = getAddListeners(parentDN);
final LocalizableMessageBuilder unacceptableReason = new LocalizableMessageBuilder();
for (final ConfigAddListener listener : addListeners)
{
- if (!listener.configAddIsAcceptable(entry, unacceptableReason))
+ if (!listener.configAddIsAcceptable(evaluatedEntry, unacceptableReason))
{
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, ERR_CONFIG_FILE_ADD_REJECTED_BY_LISTENER.get(
entryDN, parentDN, unacceptableReason));
@@ -479,7 +483,7 @@
final ConfigChangeResult ccr = new ConfigChangeResult();
for (final ConfigAddListener listener : addListeners)
{
- final ConfigChangeResult result = listener.applyConfigurationAdd(entry);
+ final ConfigChangeResult result = listener.applyConfigurationAdd(evaluatedEntry);
ccr.aggregate(result);
handleConfigChangeResult(result, entry.getName(), listener.getClass().getName(), "applyConfigurationAdd");
}
@@ -532,12 +536,16 @@
final List<ConfigDeleteListener> deleteListeners = getDeleteListeners(parentDN);
final LocalizableMessageBuilder unacceptableReason = new LocalizableMessageBuilder();
final Entry entry = backend.get(dn);
+
+ // If the entry contains any expressions then these must be evaluated before passing to listeners.
+ final Entry evaluatedEntry = evaluateEntry(entry, ERR_CONFIG_FILE_DELETE_REJECTED_DUE_TO_EVALUATION_FAILURE);
+
for (final ConfigDeleteListener listener : deleteListeners)
{
- if (!listener.configDeleteIsAcceptable(entry, unacceptableReason))
+ if (!listener.configDeleteIsAcceptable(evaluatedEntry, unacceptableReason))
{
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
- ERR_CONFIG_FILE_DELETE_REJECTED_BY_LISTENER.get(entry, parentDN, unacceptableReason));
+ ERR_CONFIG_FILE_DELETE_REJECTED_BY_LISTENER.get(dn, parentDN, unacceptableReason));
}
}
@@ -558,7 +566,7 @@
final ConfigChangeResult ccr = new ConfigChangeResult();
for (final ConfigDeleteListener listener : deleteListeners)
{
- final ConfigChangeResult result = listener.applyConfigurationDelete(entry);
+ final ConfigChangeResult result = listener.applyConfigurationDelete(evaluatedEntry);
ccr.aggregate(result);
handleConfigChangeResult(result, dn, listener.getClass().getName(), "applyConfigurationDelete");
}
@@ -600,12 +608,15 @@
ERR_CONFIG_FILE_MODIFY_STRUCTURAL_CHANGE_NOT_ALLOWED.get(oldEntry.getName()));
}
+ // If the entry contains any expressions then these must be evaluated before passing to listeners.
+ final Entry evaluatedNewEntry = evaluateEntry(newEntry, ERR_CONFIG_FILE_MODIFY_REJECTED_DUE_TO_EVALUATION_FAILURE);
+
// Iterate through change listeners to make sure the change is acceptable.
final List<ConfigChangeListener> changeListeners = getChangeListeners(newEntryDN);
final LocalizableMessageBuilder unacceptableReason = new LocalizableMessageBuilder();
for (ConfigChangeListener listeners : changeListeners)
{
- if (!listeners.configChangeIsAcceptable(newEntry, unacceptableReason))
+ if (!listeners.configChangeIsAcceptable(evaluatedNewEntry, unacceptableReason))
{
throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
ERR_CONFIG_FILE_MODIFY_REJECTED_BY_CHANGE_LISTENER.get(newEntryDN, unacceptableReason));
@@ -634,7 +645,7 @@
// some listeners may have de-registered themselves due to previous changes, ignore them
continue;
}
- final ConfigChangeResult result = listener.applyConfigurationChange(newEntry);
+ final ConfigChangeResult result = listener.applyConfigurationChange(evaluatedNewEntry);
ccr.aggregate(result);
handleConfigChangeResult(result, newEntryDN, listener.getClass().getName(), "applyConfigurationChange");
}
@@ -1077,7 +1088,7 @@
@Override
public boolean handleEntry(SearchResultEntry entry)
{
- org.opends.server.types.Entry serverEntry = Converters.to(entry);
+ org.opends.server.types.Entry serverEntry = Converters.to(evaluateEntryIfPossible(entry));
serverEntry.processVirtualAttributes();
return !filterMatchesEntry(serverEntry) || searchOperation.returnEntry(serverEntry, null);
}
@@ -1783,4 +1794,64 @@
logger.debug(INFO_CONFIG_CHANGE_RESULT_MESSAGES, className, methodName, entryDN, messages);
}
}
+
+ private static Entry evaluateEntryIfPossible(final Entry entry)
+ {
+ try
+ {
+ return evaluateEntry(entry, ERR_CONFIG_FILE_READ_FAILED_DUE_TO_EVALUATION_FAILURE);
+ }
+ catch (final DirectoryException e)
+ {
+ // The entry contained an invalid expression. Fall-back to returning the original entry.
+ logger.traceException(e);
+ return entry;
+ }
+ }
+
+ private static Entry evaluateEntry(final Entry entry, final Arg4<Object, Object, Object, Object> errMsg)
+ throws DirectoryException
+ {
+ final Entry evaluatedEntry = new LinkedHashMapEntry(entry.getName());
+ for (final Attribute attribute : entry.getAllAttributes())
+ {
+ evaluatedEntry.addAttribute(evaluateAttribute(entry.getName(), attribute, errMsg));
+ }
+ return evaluatedEntry;
+ }
+
+ private static Attribute evaluateAttribute(final DN dn, final Attribute attribute,
+ final Arg4<Object, Object, Object, Object> errMsg)
+ throws DirectoryException
+ {
+ // Skip any attributes which are not config related.
+ if (!attribute.getAttributeDescriptionAsString().startsWith("ds-cfg-"))
+ {
+ return attribute;
+ }
+ final Attribute evaluatedAttribute = new LinkedAttribute(attribute.getAttributeDescription());
+ for (final ByteString value : attribute)
+ {
+ ByteString evaluatedValue = value;
+ for (int i = 0; i < value.length(); i++)
+ {
+ if (value.byteAt(i) == '$')
+ {
+ // Potential expression.
+ try
+ {
+ evaluatedValue = ByteString.valueOfUtf8(Expression.eval(value.toString(), String.class));
+ }
+ catch (final Exception e)
+ {
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+ errMsg.get(dn, attribute.getAttributeDescription(), value, e.getMessage()));
+ }
+ break;
+ }
+ }
+ evaluatedAttribute.add(evaluatedValue);
+ }
+ return evaluatedAttribute;
+ }
}
--
Gitblit v1.10.0