UnboundID Server SDK

Ping Identity
UnboundID Server SDK Documentation

ExamplePassphraseProvider.java

/*
 * 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'.");
  }
}