/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * docs/licenses/cddl.txt * or http://www.opensource.org/licenses/cddl1.php. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * docs/licenses/cddl.txt. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Portions Copyright 2021-2023 Ping Identity Corporation */ package com.unboundid.directory.sdk.examples; import java.util.Collections; import java.util.List; import java.util.Map; import com.unboundid.directory.sdk.common.api.PassphraseProvider; import com.unboundid.directory.sdk.common.config.PassphraseProviderConfig; import com.unboundid.directory.sdk.common.types.ServerContext; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.util.NotNull; import com.unboundid.util.StaticUtils; import com.unboundid.util.args.ArgumentParser; import com.unboundid.util.args.ArgumentException; import com.unboundid.util.args.StringArgument; /** * This class provides an example passphrase provider implementation that may * be used to obtain a passphrase from a specified Java property. * <p> * The passphrase provider supports the following configuration properties: * <ul> * <li> * property-name -- The name of the Java property that holds the passphrase. * This property must be defined and must have a non-empty value. * </li> * </ul> */ public final class ExamplePassphraseProvider extends PassphraseProvider { /** * The name of the extension argument that specifies the name of the Java * property from which the passphrase will be read. */ private static final String ARG_NAME_PROPERTY_NAME = "property-name"; // The name of the property that holds the passphrase. private volatile String propertyName; /** * Creates a new instance of this passphrase provider. All passphrase * provider implementations must include a default constructor, but any * initialization should generally be done in the * {@code initializePassphraseProvider} method. */ public ExamplePassphraseProvider() { propertyName = null; } /** * Retrieves a human-readable name for this extension. * * @return A human-readable name for this extension. */ @NotNull() @Override() public String getExtensionName() { return "Example Passphrase Provider"; } /** * Retrieves a human-readable description for this extension. Each element * of the array that is returned will be considered a separate paragraph in * generated documentation. * * @return A human-readable description for this extension, or {@code null} * or an empty array if no description should be available. */ @NotNull() @Override() public String[] getExtensionDescription() { return new String[] { "An example passphrase provider that can retrieve a passphrase from a " + "specified Java property that is defined in the JVM. The " + "property must be defined and non-empty." }; } /** * Updates the provided argument parser to define any configuration arguments * which may be used by this extension. The argument parser may also be * updated to define relationships between arguments (e.g., to specify * required, exclusive, or dependent argument sets). * * @param parser The argument parser to be updated with the configuration * arguments which may be used by this extension. * * @throws ArgumentException If a problem is encountered while updating the * provided argument parser. */ @Override() public void defineConfigArguments(@NotNull final ArgumentParser parser) throws ArgumentException { final Character shortIdentifier = null; final String longIdentifier = ARG_NAME_PROPERTY_NAME; final boolean required = true; final int maxOccurrences = 1; final String placeholder = "{propertyName}"; final String description = "The name of the Java property from " + "which the passphrase will be read."; parser.addArgument(new StringArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description)); } /** * Initializes this passphrase provider. * * @param serverContext A handle to the server context for the server in * which this extension is running. * @param config The general configuration for this passphrase * provider. * @param parser The argument parser which has been initialized from * the configuration for this passphrase provider. * * @throws LDAPException If a problem occurs while initializing this * passphrase provider. */ @Override() public void initializePassphraseProvider( @NotNull final ServerContext serverContext, @NotNull final PassphraseProviderConfig config, @NotNull final ArgumentParser parser) throws LDAPException { propertyName = validateConfigAndGetPropertyName(parser); } /** * Indicates whether the configuration represented by the provided argument * parser is acceptable for use by this extension. The parser will have been * used to parse any configuration available for this extension, and any * automatic validation will have been performed. This method may be used to * perform any more complex validation which cannot be performed automatically * by the argument parser. * * @param config The general configuration for this extension. * @param parser The argument parser that has been used to * parse the proposed configuration for this * extension. * @param unacceptableReasons A list to which messages may be added to * provide additional information about why the * provided configuration is not acceptable. * * @return {@code true} if the configuration in the provided argument parser * appears to be acceptable, or {@code false} if not. */ @Override() public boolean isConfigurationAcceptable( @NotNull final PassphraseProviderConfig config, @NotNull final ArgumentParser parser, @NotNull final List<String> unacceptableReasons) { try { validateConfigAndGetPropertyName(parser); return true; } catch (final LDAPException e) { config.getServerContext().debugCaught(e); unacceptableReasons.add(e.getMessage()); return false; } } /** * Attempts to apply the configuration from the provided argument parser to * this extension. * * @param config The general configuration for this extension. * @param parser The argument parser that has been used to * parse the new configuration for this * extension. * @param adminActionsRequired A list to which messages may be added to * provide additional information about any * additional administrative actions that may * be required to apply some of the * configuration changes. * @param messages A list to which messages may be added to * provide additional information about the * processing performed by this method. * * @return A result code providing information about the result of applying * the configuration change. A result of {@code SUCCESS} should be * used to indicate that all processing completed successfully. Any * other result will indicate that a problem occurred during * processing. */ @NotNull() @Override() public ResultCode applyConfiguration( @NotNull final PassphraseProviderConfig config, @NotNull final ArgumentParser parser, @NotNull final List<String> adminActionsRequired, @NotNull final List<String> messages) { try { propertyName = validateConfigAndGetPropertyName(parser); return ResultCode.SUCCESS; } catch (final LDAPException e) { config.getServerContext().debugCaught(e); messages.add(e.getMessage()); return e.getResultCode(); } } /** * Validates the configuration and retrieves the name of the property that * will hold the passphrase. * * @param parser The argument parser that holds the configuration to * validate. * * @return The name of the property that will hold the passphrase. It will * not be {@code null}. * * @throws LDAPException If the configuration is not valid. */ @NotNull() private static String validateConfigAndGetPropertyName( @NotNull final ArgumentParser parser) throws LDAPException { // Make sure that the property-name argument was specified. final StringArgument propertyNameArg = parser.getStringArgument(ARG_NAME_PROPERTY_NAME); if ((propertyNameArg == null) || (! propertyNameArg.isPresent())) { throw new LDAPException(ResultCode.PARAM_ERROR, "The required " + ARG_NAME_PROPERTY_NAME + " argument was not provided."); } // Make sure that the specified property name is not empty. final String propertyName = propertyNameArg.getValue(); if ((propertyName == null) || propertyName.isEmpty()) { throw new LDAPException(ResultCode.PARAM_ERROR, "The required " + ARG_NAME_PROPERTY_NAME + " argument has an empty value."); } // Make sure that we can get the passphrase from the specified property. getPassphraseFromProperty(propertyName); return propertyName; } /** * Performs any cleanup which may be necessary when this passphrase provider * is to be taken out of service. */ @Override() public void finalizePassphraseProvider() { // No special finalization is required. } /** * Retrieves the passphrase. * * @param allowCached Indicates whether to allow the server to use a cached * version of the passphrase. * * @return The passphrase that should be used. It must not be {@code null} * or empty. * * @throws LDAPException If the passphrase cannot be retrieved. */ @Override() @NotNull() public char[] getPassphrase(final boolean allowCached) throws LDAPException { return getPassphraseFromProperty(propertyName); } /** * Retrieves the passphrase from the specified system property. * * @param propertyName The name of the system property from which the * passphrase should be retrieved. It must not be * {@code null}. * * @return The passphrase read from the specified system property. * * @throws LDAPException If the specified system property does not have a * valid passphrase. */ @NotNull() private static char[] getPassphraseFromProperty( @NotNull final String propertyName) throws LDAPException { if (propertyName == null) { // This should never happen in a properly initialized passphrase provider. throw new LDAPException(ResultCode.OTHER, "Unable to retrieve the passphrase from an uninitialized " + "passphrase provider."); } final String propertyValue = System.getProperty(propertyName); if (propertyValue == null) { throw new LDAPException(ResultCode.OTHER, "Unable to retrieve a passphrase from system property '" + propertyName + "' because that property is not defined in " + "the JVM in which the server is running."); } if (propertyValue.isEmpty()) { throw new LDAPException(ResultCode.OTHER, "Unable to retrieve a passphrase from system property '" + propertyName + "' because that property has an empty value."); } return propertyValue.toCharArray(); } /** * Retrieves a map containing examples of configurations that may be used for * this extension. The map key should be a list of sample arguments, and the * corresponding value should be a description of the behavior that will be * exhibited by the extension when used with that configuration. * * @return A map containing examples of configurations that may be used for * this extension. It may be {@code null} or empty if there should * not be any example argument sets. */ @NotNull() @Override() public Map<List<String>,String> getExamplesArgumentSets() { return StaticUtils.mapOf( Collections.singletonList( "property-name=secret-passphrase"), "Retrieves the passphrase from a Java system property named " + "'secret-passphrase'."); } }