From 89454950e49290222563e937cfaf7aa9dfa1ff33 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Tue, 15 Sep 2026 14:07:59 +0000
Subject: [PATCH] [#913] Declare the crypto manager's SSL properties as requiring a server restart (#979)
---
opendj-config/src/test/java/org/forgerock/opendj/config/CryptoManagerCfgDefnTest.java | 50 +++++++
opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java | 104 ++++++++++++++
opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml | 36 ++---
opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java | 188 ++++++++++++++++++++++++++
opendj-doc-generated-ref/src/main/asciidoc/admin-guide/chap-change-certs.adoc | 2
opendj-server-legacy/src/messages/org/opends/messages/core.properties | 15 ++
6 files changed, 371 insertions(+), 24 deletions(-)
diff --git a/opendj-config/src/test/java/org/forgerock/opendj/config/CryptoManagerCfgDefnTest.java b/opendj-config/src/test/java/org/forgerock/opendj/config/CryptoManagerCfgDefnTest.java
new file mode 100644
index 0000000..ac3ef50
--- /dev/null
+++ b/opendj-config/src/test/java/org/forgerock/opendj/config/CryptoManagerCfgDefnTest.java
@@ -0,0 +1,50 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2026 3A Systems, LLC.
+ */
+package org.forgerock.opendj.config;
+
+import static org.forgerock.opendj.config.AdministratorAction.Type.SERVER_RESTART;
+import static org.testng.Assert.assertEquals;
+
+import org.forgerock.opendj.server.config.meta.CryptoManagerCfgDefn;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@SuppressWarnings("javadoc")
+public class CryptoManagerCfgDefnTest extends ConfigTestCase {
+
+ @DataProvider
+ public Object[][] sslProperties() {
+ CryptoManagerCfgDefn defn = CryptoManagerCfgDefn.getInstance();
+ return new Object[][] {
+ { "ssl-cert-nickname", defn.getSSLCertNicknamePropertyDefinition() },
+ { "ssl-protocol", defn.getSSLProtocolPropertyDefinition() },
+ { "ssl-cipher-suite", defn.getSSLCipherSuitePropertyDefinition() },
+ { "ssl-encryption", defn.getSSLEncryptionPropertyDefinition() },
+ };
+ }
+
+ /**
+ * The crypto manager reads its SSL properties once, when it is created, and replication
+ * keeps what it read for the life of the session, so a change to any of them only takes
+ * effect once the server is restarted. A component restart is not an option either: the
+ * crypto manager has no enabled property, so it cannot be disabled and re-enabled.
+ */
+ @Test(dataProvider = "sslProperties")
+ public void sslPropertiesRequireAServerRestart(String name, PropertyDefinition<?> property) {
+ assertEquals(property.getAdministratorAction().getType(), SERVER_RESTART,
+ "unexpected administrator action for " + name);
+ }
+}
diff --git a/opendj-doc-generated-ref/src/main/asciidoc/admin-guide/chap-change-certs.adoc b/opendj-doc-generated-ref/src/main/asciidoc/admin-guide/chap-change-certs.adoc
index 21010de..8a12955 100644
--- a/opendj-doc-generated-ref/src/main/asciidoc/admin-guide/chap-change-certs.adoc
+++ b/opendj-doc-generated-ref/src/main/asciidoc/admin-guide/chap-change-certs.adoc
@@ -541,6 +541,8 @@
--set ssl-cert-nickname:repl-cert \
--no-prompt
----
++
+The property is declared as requiring a server restart, which `dsconfig list-properties` shows, and the server writes the change to `logs/errors` saying that the new value is stored but not in force. That warning also names a nickname the `ads-truststore` does not hold, so a mistyped alias is caught while the server is still presenting the certificate it started with; if the `ads-truststore` cannot be read, the warning says so in place of the nicknames it would have named. The warning is repeated on every later change to the crypto manager until the server is restarted, as the value it compares against is the one the server started with.
.. Restart the server for the change to take effect.
+
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml
index 5a93a89..a317878 100644
--- a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml
@@ -14,6 +14,7 @@
Copyright 2007-2008 Sun Microsystems, Inc.
Portions Copyright 2011 ForgeRock AS.
+ Portions Copyright 2026 3A Systems, LLC.
! -->
<adm:managed-object name="crypto-manager" plural-name="crypto-managers"
package="org.forgerock.opendj.server.config"
@@ -213,13 +214,7 @@
use in SSL or TLS communication.
</adm:synopsis>
<adm:requires-admin-action>
- <adm:none>
- <adm:synopsis>
- Changes to this property take effect immediately but
- only impact new SSL/TLS-based sessions created after the
- change.
- </adm:synopsis>
- </adm:none>
+ <adm:server-restart />
</adm:requires-admin-action>
<adm:default-behavior>
<adm:alias>
@@ -244,13 +239,7 @@
for use in SSL or TLS communication.
</adm:synopsis>
<adm:requires-admin-action>
- <adm:none>
- <adm:synopsis>
- Changes to this property take effect immediately but
- only impact new SSL/TLS-based sessions created after the
- change.
- </adm:synopsis>
- </adm:none>
+ <adm:server-restart />
</adm:requires-admin-action>
<adm:default-behavior>
<adm:alias>
@@ -275,13 +264,7 @@
communication between two <adm:product-name /> server components.
</adm:synopsis>
<adm:requires-admin-action>
- <adm:none>
- <adm:synopsis>
- Changes to this property take effect immediately but
- only impact new SSL/TLS-based sessions created after the
- change.
- </adm:synopsis>
- </adm:none>
+ <adm:server-restart />
</adm:requires-admin-action>
<adm:default-behavior>
<adm:defined>
@@ -297,5 +280,14 @@
</ldap:attribute>
</adm:profile>
</adm:property>
- <adm:property-reference name="ssl-cert-nickname" />
+ <adm:property-reference name="ssl-cert-nickname">
+ <!--
+ The component restart declared by the common property definition does not apply
+ here: the crypto manager has no enabled property, so it cannot be disabled and
+ re-enabled, and it reads this property once, when it is created.
+ -->
+ <adm:requires-admin-action>
+ <adm:server-restart />
+ </adm:requires-admin-action>
+ </adm:property-reference>
</adm:managed-object>
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java b/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java
index 84c3896..c15db1c 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/crypto/CryptoManagerImpl.java
@@ -39,6 +39,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.UUID;
@@ -67,6 +68,7 @@
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.PropertyDefinition;
import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.config.server.ConfigException;
import org.forgerock.opendj.config.server.ConfigurationChangeListener;
@@ -81,6 +83,7 @@
import org.forgerock.opendj.ldap.schema.CoreSchema;
import org.forgerock.opendj.ldap.schema.ObjectClass;
import org.forgerock.opendj.ldap.schema.Schema;
+import org.forgerock.opendj.server.config.meta.CryptoManagerCfgDefn;
import org.forgerock.opendj.server.config.server.CryptoManagerCfg;
import org.forgerock.util.Reject;
import org.opends.admin.ads.ADSContext;
@@ -293,7 +296,7 @@
if (! isConfigurationChangeAcceptable(config, why)) {
throw new InitializationException(why.get(0));
}
- applyConfigurationChange(config);
+ applyCryptoConfiguration(config);
// Secure replication related...
sslCertNicknames = config.getSSLCertNickname();
@@ -430,13 +433,110 @@
@Override
public ConfigChangeResult applyConfigurationChange(CryptoManagerCfg cfg)
{
+ final ConfigChangeResult ccr = new ConfigChangeResult();
+ applyCryptoConfiguration(cfg);
+ reportSslPropertiesWhichNeedARestart(cfg, ccr);
+ return ccr;
+ }
+
+ /**
+ * Applies the cryptographic properties, the ones this crypto manager re-reads whenever
+ * they change. The SSL properties are not among them: they are read once, in the
+ * constructor, which is why this method is called from there rather than
+ * {@link #applyConfigurationChange(CryptoManagerCfg)}.
+ *
+ * @param cfg
+ * The configuration to apply.
+ */
+ private void applyCryptoConfiguration(CryptoManagerCfg cfg)
+ {
preferredDigestAlgorithm = cfg.getDigestAlgorithm();
preferredMACAlgorithm = cfg.getMacAlgorithm();
preferredMACAlgorithmKeyLengthBits = cfg.getMacKeyLength();
preferredCipherTransformation = cfg.getCipherTransformation();
preferredCipherTransformationKeyLengthBits = cfg.getCipherKeyLength();
preferredKeyWrappingTransformation = cfg.getKeyWrappingTransformation();
- return new ConfigChangeResult();
+ }
+
+ /**
+ * Reports every SSL property whose new value the running server does not use, so that a
+ * change which is accepted into the configuration without being in force says so instead
+ * of passing for an applied one. The properties are declared as requiring a server
+ * restart, and this is what the administrator is told at the moment of the change.
+ * <p>
+ * The values compared against are the ones the server started with, so a value which
+ * differs is reported on the change which made it differ and again on every later change
+ * to the crypto manager until the server is restarted: a reminder that the value stored
+ * is not the one in force, rather than a report of the one change which stored it.
+ *
+ * @param cfg
+ * The configuration which has just been stored.
+ * @param ccr
+ * The result to report the required administrative action in.
+ */
+ private void reportSslPropertiesWhichNeedARestart(CryptoManagerCfg cfg, ConfigChangeResult ccr)
+ {
+ final CryptoManagerCfgDefn defn = CryptoManagerCfgDefn.getInstance();
+ final SortedSet<String> newCertNicknames = cfg.getSSLCertNickname();
+ if (reportPropertyWhichNeedsARestart(defn.getSSLCertNicknamePropertyDefinition(),
+ sslCertNicknames, newCertNicknames, ccr))
+ {
+ reportCertNicknamesTheTrustStoreDoesNotHold(newCertNicknames, ccr);
+ }
+ reportPropertyWhichNeedsARestart(defn.getSSLProtocolPropertyDefinition(),
+ sslProtocols, cfg.getSSLProtocol(), ccr);
+ reportPropertyWhichNeedsARestart(defn.getSSLCipherSuitePropertyDefinition(),
+ sslCipherSuites, cfg.getSSLCipherSuite(), ccr);
+ reportPropertyWhichNeedsARestart(defn.getSSLEncryptionPropertyDefinition(),
+ sslEncryption, cfg.isSSLEncryption(), ccr);
+ }
+
+ private boolean reportPropertyWhichNeedsARestart(
+ PropertyDefinition<?> property, Object inForce, Object configured, ConfigChangeResult ccr)
+ {
+ if (Objects.equals(inForce, configured))
+ {
+ return false;
+ }
+ ccr.setAdminActionRequired(true);
+ ccr.addMessage(WARN_CRYPTOMGR_SSL_PROPERTY_REQUIRES_RESTART.get(property.getName()));
+ return true;
+ }
+
+ /**
+ * Reports each newly configured certificate nickname which the trust store does not hold.
+ * The same nicknames are looked up again whenever an SSL context is built, but only once
+ * the server has been restarted with them: reporting them here names a nickname which
+ * would present no certificate while the administrator is still making the change.
+ *
+ * @param certNicknames
+ * The certificate nicknames which have just been configured.
+ * @param ccr
+ * The result to report the missing nicknames in.
+ */
+ private void reportCertNicknamesTheTrustStoreDoesNotHold(SortedSet<String> certNicknames, ConfigChangeResult ccr)
+ {
+ try
+ {
+ final TrustStoreBackend trustStoreBackend = getTrustStoreBackend();
+ for (String nickname : certNicknames)
+ {
+ if (!trustStoreBackend.containsKeyWithAlias(nickname))
+ {
+ ccr.addMessage(WARN_CRYPTOMGR_SSL_CERT_NICKNAME_NOT_IN_TRUST_STORE.get(
+ nickname, trustStoreBackend.getTrustStoreFile()));
+ }
+ }
+ }
+ catch (ConfigException | DirectoryException e)
+ {
+ // A trust store which cannot be read now costs the administrator this report and
+ // nothing else: the change is stored either way, and a nickname it does not hold is
+ // reported again, as an error, when the SSL context is built after the restart. The
+ // cost is named next to the restart the change asks for, rather than paid silently.
+ logger.traceException(e);
+ ccr.addMessage(WARN_CRYPTOMGR_SSL_CERT_NICKNAME_LOOKUP_FAILED.get(e.getMessageObject()));
+ }
}
diff --git a/opendj-server-legacy/src/messages/org/opends/messages/core.properties b/opendj-server-legacy/src/messages/org/opends/messages/core.properties
index d4ad789..ddbd5db 100644
--- a/opendj-server-legacy/src/messages/org/opends/messages/core.properties
+++ b/opendj-server-legacy/src/messages/org/opends/messages/core.properties
@@ -1361,3 +1361,18 @@
configured, no certificate is presented at all and peers requiring client \
authentication, replication servers included, reject the connection. Import the \
key pair under that nickname into that trust store, or configure a nickname it holds
+WARN_CRYPTOMGR_SSL_PROPERTY_REQUIRES_RESTART_763=The new value of the %s property \
+ of the crypto manager has been stored in the configuration but is not in force. The \
+ crypto manager reads that property when it is created and replication keeps the value \
+ it read, so server to server connections go on using the value the server started with \
+ until the server is restarted
+WARN_CRYPTOMGR_SSL_CERT_NICKNAME_NOT_IN_TRUST_STORE_764=The certificate nickname \
+ "%s" now configured in the ssl-cert-nickname property of the crypto manager is not held \
+ by the trust store %s used for server to server communication. Import the key pair under \
+ that nickname into that trust store before restarting the server, or the server will \
+ present no certificate under that nickname to its replication peers
+WARN_CRYPTOMGR_SSL_CERT_NICKNAME_LOOKUP_FAILED_765=The certificate nicknames now \
+ configured in the ssl-cert-nickname property of the crypto manager could not be looked \
+ up in the trust store used for server to server communication: %s. A nickname that \
+ trust store does not hold is only reported once the server has been restarted with it, \
+ so check the nicknames against the trust store before restarting the server
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java
index 9958d39..37537a7 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/crypto/CryptoManagerTestCase.java
@@ -21,6 +21,7 @@
import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*;
import static org.forgerock.opendj.ldap.ModificationType.*;
import static org.forgerock.opendj.ldap.SearchScope.*;
+import static org.opends.messages.CoreMessages.*;
import static org.opends.server.TestCaseUtils.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.crypto.CryptoManagerImpl.CERT_NICKNAME_CHECK_INTERVAL_NANOS;
@@ -34,22 +35,29 @@
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Proxy;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.security.MessageDigest;
import java.util.Arrays;
+import java.util.TreeSet;
import java.util.UUID;
import javax.crypto.Mac;
import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.config.server.ConfigChangeResult;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
+import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SSLContextBuilder;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldif.ConnectionEntryReader;
+import org.forgerock.opendj.server.config.server.CryptoManagerCfg;
import org.forgerock.util.Options;
import org.opends.admin.ads.ADSContext;
import org.opends.admin.ads.util.BlindTrustManager;
@@ -131,6 +139,186 @@
.as("and the next interval starts from that look up").isFalse();
}
+ /**
+ The crypto manager reads its SSL properties when it is created and replication keeps
+ what it read, so a change to ssl-cert-nickname is accepted into the configuration
+ without being in force. The change listener says so rather than staying silent.
+ */
+ @Test
+ public void testSslCertNicknameChangeReportsThatARestartIsRequired() throws Exception
+ {
+ final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
+ final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
+
+ final ConfigChangeResult ccr = cm.applyConfigurationChange(withSslCertNicknames(cfg, "no-such-nickname"));
+
+ assertThat(ccr.getResultCode()).as("the change is accepted").isEqualTo(ResultCode.SUCCESS);
+ assertThat(ccr.adminActionRequired()).as("but it is not in force").isTrue();
+ assertThat(ccr.getMessages())
+ .as("and the configuration change is not applied silently")
+ .contains(WARN_CRYPTOMGR_SSL_PROPERTY_REQUIRES_RESTART.get("ssl-cert-nickname"));
+ }
+
+ @DataProvider
+ public Object[][] sslPropertiesWhichNeedARestart()
+ {
+ // Any value the one in force does not equal: the server starts with no protocol and no
+ // cipher suite configured, and with ds-cfg-ssl-encryption: false.
+ return new Object[][] {
+ { "getSSLProtocol", new TreeSet<>(Arrays.asList("TLSv1.3")), "ssl-protocol" },
+ { "getSSLCipherSuite", new TreeSet<>(Arrays.asList("TLS_AES_256_GCM_SHA384")), "ssl-cipher-suite" },
+ { "isSSLEncryption", true, "ssl-encryption" },
+ };
+ }
+
+ /**
+ The other SSL properties are read once as well, and used to say that no action was
+ required. A change to any of them is reported the same way and, unlike a nickname, is
+ looked up nowhere: the restart is the whole report.
+ */
+ @Test(dataProvider = "sslPropertiesWhichNeedARestart")
+ public void testSslPropertyChangeReportsThatARestartIsRequired(
+ final String getter, final Object value, final String property) throws Exception
+ {
+ final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
+ final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
+
+ final ConfigChangeResult ccr = cm.applyConfigurationChange(withProperty(cfg, getter, value));
+
+ assertThat(ccr.getResultCode()).isEqualTo(ResultCode.SUCCESS);
+ assertThat(ccr.adminActionRequired()).isTrue();
+ assertThat(ccr.getMessages()).containsExactly(WARN_CRYPTOMGR_SSL_PROPERTY_REQUIRES_RESTART.get(property));
+ }
+
+ /**
+ The report is about what changed: a change to another property of the crypto manager,
+ or a re-read of the configuration as it stands, asks for no restart.
+ */
+ @Test
+ public void testUnchangedSslPropertiesRequireNoRestart() throws Exception
+ {
+ final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
+ final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
+
+ final ConfigChangeResult ccr = cm.applyConfigurationChange(cfg);
+
+ assertThat(ccr.adminActionRequired()).isFalse();
+ assertThat(ccr.getMessages()).isEmpty();
+ }
+
+ /**
+ The cryptographic properties are the ones the change listener does apply: a new digest
+ algorithm is in use as soon as the change is made, and asks for nothing.
+ */
+ @Test
+ public void testCryptoPropertyChangeIsApplied() throws Exception
+ {
+ final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
+ final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
+ final String inForce = cm.getPreferredMessageDigestAlgorithm();
+ final String changed = "SHA-512";
+ assertThat(changed).as("the change is a change").isNotEqualTo(inForce);
+ try
+ {
+ final ConfigChangeResult ccr = cm.applyConfigurationChange(withProperty(cfg, "getDigestAlgorithm", changed));
+
+ assertThat(cm.getPreferredMessageDigestAlgorithm()).as("the new algorithm is in use").isEqualTo(changed);
+ assertThat(ccr.adminActionRequired()).as("and no action is asked for").isFalse();
+ assertThat(ccr.getMessages()).isEmpty();
+ }
+ finally
+ {
+ // The crypto manager is the one every test of the running server shares.
+ cm.applyConfigurationChange(cfg);
+ }
+ }
+
+ /**
+ A nickname which the trust store does not hold presents no certificate to the
+ replication peers, and until the server is restarted the mistake shows up nowhere. The
+ nicknames are looked up when the change is made, and only the missing one is reported.
+ */
+ @Test
+ public void testSslCertNicknameChangeReportsTheNicknamesTheTrustStoreDoesNotHold() throws Exception
+ {
+ final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
+ final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
+ final TrustStoreBackend trustStore = (TrustStoreBackend) getServerContext()
+ .getBackendConfigManager().getLocalBackendById(ID_ADS_TRUST_STORE_BACKEND);
+
+ final ConfigChangeResult ccr =
+ cm.applyConfigurationChange(withSslCertNicknames(cfg, ADS_CERTIFICATE_ALIAS, "no-such-nickname"));
+
+ assertThat(ccr.getMessages())
+ .as("the nickname which is not in the trust store is named")
+ .contains(WARN_CRYPTOMGR_SSL_CERT_NICKNAME_NOT_IN_TRUST_STORE.get(
+ "no-such-nickname", trustStore.getTrustStoreFile()))
+ .as("while the one it holds is not")
+ .doesNotContain(WARN_CRYPTOMGR_SSL_CERT_NICKNAME_NOT_IN_TRUST_STORE.get(
+ ADS_CERTIFICATE_ALIAS, trustStore.getTrustStoreFile()));
+ }
+
+ /**
+ The lookup is best effort: a trust store which cannot be read costs the administrator
+ the report of the nicknames it does not hold, and the change is stored either way. That
+ cost is named next to the restart the change asks for, rather than paid silently.
+ */
+ @Test
+ public void testSslCertNicknameChangeReportsATrustStoreItCannotRead() throws Exception
+ {
+ final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
+ final CryptoManagerCfg cfg = getServerContext().getRootConfig().getCryptoManager();
+ final TrustStoreBackend trustStore = (TrustStoreBackend) getServerContext()
+ .getBackendConfigManager().getLocalBackendById(ID_ADS_TRUST_STORE_BACKEND);
+ final Path trustStoreFile = StaticUtils.getFileForPath(trustStore.getTrustStoreFile()).toPath();
+ final Path movedAway = trustStoreFile.resolveSibling(trustStoreFile.getFileName() + ".moved-away");
+
+ // The trust store is opened on every lookup, so it cannot be read for exactly as long
+ // as the file is out of the way.
+ Files.move(trustStoreFile, movedAway);
+ final ConfigChangeResult ccr;
+ try
+ {
+ ccr = cm.applyConfigurationChange(withSslCertNicknames(cfg, "no-such-nickname"));
+ }
+ finally
+ {
+ Files.move(movedAway, trustStoreFile);
+ }
+
+ assertThat(ccr.getResultCode()).as("the change is stored either way").isEqualTo(ResultCode.SUCCESS);
+ assertThat(ccr.adminActionRequired()).isTrue();
+ assertThat(ccr.getMessages()).extracting(LocalizableMessage::ordinal)
+ .as("the restart is asked for, and the lookup which could not be done is named in place of its result")
+ .containsExactly(WARN_CRYPTOMGR_SSL_PROPERTY_REQUIRES_RESTART.ordinal(),
+ WARN_CRYPTOMGR_SSL_CERT_NICKNAME_LOOKUP_FAILED.ordinal());
+ assertThat(ccr.getMessages().get(1).toString())
+ .as("with the trust store which could not be read")
+ .contains(trustStore.getTrustStoreFile());
+ }
+
+ /**
+ Returns the crypto manager configuration as it stands, with the ssl-cert-nickname
+ property answering the provided nicknames, so that a change to that property is applied
+ without the configuration of the running server being modified.
+ */
+ private static CryptoManagerCfg withSslCertNicknames(final CryptoManagerCfg cfg, final String... nicknames)
+ {
+ return withProperty(cfg, "getSSLCertNickname", new TreeSet<>(Arrays.asList(nicknames)));
+ }
+
+ /**
+ Returns the crypto manager configuration as it stands, with the named getter answering
+ the provided value instead of the configured one.
+ */
+ private static CryptoManagerCfg withProperty(final CryptoManagerCfg cfg, final String getter, final Object value)
+ {
+ return (CryptoManagerCfg) Proxy.newProxyInstance(
+ CryptoManagerCfg.class.getClassLoader(),
+ new Class<?>[] { CryptoManagerCfg.class },
+ (proxy, method, args) -> getter.equals(method.getName()) ? value : method.invoke(cfg, args));
+ }
+
@Test
public void testImportKeysUsesLatestKey()
throws Exception {
--
Gitblit v1.10.0