ProcessBuilderOptions.java

/*
** Module   : ProcessBuilderOptions.java
** Abstract : store P2J process parameters read from directory.
**
** Copyright (c) 2014-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description----------------------------------
** 001 CA  20140206 First version.
** 002 MAG 20140224 Add support for output file.
** 003 CA  20160205 SecurityManager.getEncryptedKeyStore now returns a function which is cached
**                  for each thread, and will be non-null only if the thread has proper access to 
**                  this SecurityManager API.
** 004 SBI 20171116 Changed to be according to EncryptedKeyStoreFunction.
** 005 CA  20191211 When spawning a Java process programmatically, allow the caller to pass 
**                  environment options specific to that call (beside any jvmArgs configured in 
**                  the directory).
** 006 GBB 20230825 SecurityManager certificate methods calls updated.
** 007 GBB 20240709 Hard-coded config names replaced by ConfigItem constants. Redundant methods removed.
** 008 GBB 20240718 Config read implemented in concrete class BaseClientBuilderOptions.
** 009 GBB 20241010 Always set validateCertificate to true.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
** 
** Additional terms under GNU Affero GPL version 3 section 7:
** 
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
** 
**   0. Attribution Requirement.
** 
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
** 
**   1. No License To Use Trademarks.
** 
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
** 
**   2. No Misrepresentation of Affiliation.
** 
**     You may not represent yourself as Golden Code Development Corporation or FWD.
** 
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
** 
**   3. No Misrepresentation of Source or Origin.
** 
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/

package com.goldencode.p2j.main;

import java.util.*;

import com.goldencode.p2j.main.ServerKeyStore.*;
import com.goldencode.p2j.security.SecurityManager;
import com.goldencode.p2j.security.SecurityManager.*;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.Utils.DirScope;
import com.goldencode.util.*;

/**
 * The parameters stored inside this structure are read from directory on server start-up 
 * and are used as default parameters to create and spawn a web client process.
 * <p>
 * The container node is named {@link ConfigItem#CLIENT_CONF_CONTAINER} and the first child of these nodes is
 * used:
 * <ol>
 *    <li>server/&lt;server-id&gt;/runtime/&lt;process-id&gt;/</li>
 *    <li>server/&lt;server-id&gt;/runtime/default/</li>
 *    <li>server/&lt;server-id&gt;/</li>
 *    <li>server/default/</li>
 * </ol>
 */
class ProcessBuilderOptions
implements ClientBuilderOptions
{
   /**
    * Thread-local cache of the key store function obtained from {@link SecurityManager}.  
    * This may be <code>null</code> if the thread doesn't have the proper security context to 
    * access the function.<p>
    * This is set as a thread-local variable instead of a static field to not expose the worker
    * in SecurityManager.CertificateSecurityManager#getEncryptedKeyStore()  which
    * returns the private key store to other threads which may not have a server context.  It is 
    * initialized only once per thread, and only if the thread has access to the 
    * SecurityManager.CertificateSecurityManager#certificateSm#getEncryptedKeyStore() API.
    */
   private static final ThreadLocal<EncryptedKeyStoreFunction> encryptedKeyStoreWorker = new ThreadLocal<>();
   
   /** The alias in the process keystore. */
   private final String processAlias;
   
   /** The alias in the truststore. */
   private final String truststoreAlias;
   
   /** The keystore password. */
   private final String keystorePassword;
   
   /** The keyentry password. */
   private final String keyentryPassword;
   
   /** The truststore password. */
   private final String truststorePassword;

   /** Flag indicating if the certificate must be validated. */
   private final boolean validateCertificate;

   /** The serialized process keystore. */
   private final byte[] processKeystore;

   /** The serialized truststore. */
   private final byte[] truststore;

   /**
    * All the subject IDs associated with this builder or <code>null</code> to use the context's subject IDs.
    */
   private final String[] accountIds;

   /** The name of the output file. */
   private final String outputToFile;
   
   /** P2J process name */
   private final String process;

   /** Base options. */
   private final BaseClientBuilderOptions baseOptions;
   
   /**
    * Initialize. Read all configuration parameters from directory.
    * 
    * @param    process
    *           The process subject ID.
    * @param    env
    *           Map of additional environment properties, in the <code>key=value</code> form.
    */
   public ProcessBuilderOptions(String process, Map<String, String> env)
   {
      SecurityManager sm = SecurityManager.getInstance();
      
      this.accountIds = sm.getAccountIds(process);
      this.process = process;
      
      String ksPassword = RandomWordGenerator.create();
      String kePassword = RandomWordGenerator.create();
      String procAlias  = sm.getAccountAlias(process);

      this.validateCertificate = true;

      if (encryptedKeyStoreWorker.get() == null)
      {
         encryptedKeyStoreWorker.set(sm.certificateSm.getEncryptedKeyStore());
      }
      
      if (encryptedKeyStoreWorker.get() != null)
      {
         this.processKeystore = encryptedKeyStoreWorker.get().getEncryptedKeyStore(procAlias, 
                                                                                   ksPassword, 
                                                                                   kePassword,
                                                                                   Store.TRUST_STORE);
      }
      else
      {
         this.processKeystore = null;
      }
      
      if (this.processKeystore == null)
      {
         this.processAlias     = null;
         this.keystorePassword = null;
         this.keyentryPassword = null;
      }
      else
      {
         this.processAlias     = procAlias;
         this.keystorePassword = ksPassword;
         this.keyentryPassword = kePassword;
      }

      this.truststorePassword  = RandomWordGenerator.create();
      this.truststoreAlias     = sm.getServerAlias();
      this.truststore          = sm.certificateSm.getEncryptedTrustStore(truststorePassword);
      
      this.outputToFile        = ConfigItem.OUTPUT_TO_FILE.read(null, DirScope.BOTH, accountIds);

      this.baseOptions = new BaseClientBuilderOptions(env)
      {
         /**
          * A convenience method to read the client config with the specified default value.
          *
          * @param   clientConfig
          *          The client config to read.
          * @param   defaultValue
          *          The default value returned if no value found in directory.
          * @param   <T>
          *          The type of the client config.
          *
          * @return  The client config value as read from directory.
          */
         @Override
         <T> T read(ConfigItem<T> clientConfig, T defaultValue)
         {
            return clientConfig.read(null, DirScope.BOTH, defaultValue, null, null, accountIds);
         }

         /**
          * A convenience method to read the client config with the specified default value.
          *
          * @param   clientConfig
          *          The client config to read.
          * @param   defaultValue
          *          The default value returned if no value found in directory.
          * @param   placeholderValue
          *          The value to replace the placeholder in the directory node name for the config.
          * @param   <T>
          *          The type of the client config.
          *
          * @return  The client config value as read from directory.
          */
         @Override
         <T> T read(ConfigItem<T> clientConfig, T defaultValue, String placeholderValue)
         {
            return clientConfig.read(null, DirScope.BOTH, defaultValue, null, placeholderValue, accountIds);
         }

         /**
          * Reads and returns the value of clientConfig/cfgOverrides.
          *
          * @param   defaultValue
          *          The default value returned if no value found in directory.
          *
          * @return  The cfgOverrides value.
          */
         @Override
         String readCfgOverrides(String defaultValue)
         {
            return read(ConfigItem.CFG_OVERRIDES, defaultValue);
         }
      };
   }
   
   /**
    * Returns the base client options. 
    *
    * @return   The base client options. 
    */
   @Override
   public BaseClientBuilderOptions getBaseOptions()
   {
      return baseOptions;
   }
   
   /**
    * Getter for the {@link #processAlias}.
    * 
    * @return   See above.
    */
   public String getProcessAlias()
   {
      return processAlias;
   }

   /**
    * Getter for the {@link #truststoreAlias}.
    * 
    * @return   See above.
    */
   public String getTruststoreAlias()
   {
      return truststoreAlias;
   }

   /**
    * Getter for the {@link #keystorePassword}.
    * 
    * @return   See above.
    */
   public String getKeystorePassword()
   {
      return keystorePassword;
   }

   /**
    * Getter for the {@link #keyentryPassword}.
    * 
    * @return   See above.
    */
   public String getKeyentryPassword()
   {
      return keyentryPassword;
   }

   /**
    * Getter for the {@link #truststorePassword}.
    * 
    * @return   See above.
    */
   public String getTruststorePassword()
   {
      return truststorePassword;
   }

   /**
    * Getter for the {@link #validateCertificate}.
    * 
    * @return   See above.
    */
   public boolean isValidateCertificate()
   {
      return validateCertificate;
   }

   /**
    * Getter for the {@link #processKeystore}.
    * 
    * @return   See above.
    */
   public byte[] getProcessKeystore()
   {
      return processKeystore;
   }

   /**
    * Getter for the {@link #truststore}.
    * 
    * @return   See above.
    */
   public byte[] getTruststore()
   {
      return truststore;
   }

   /**
    * Get the output file template.
    * 
    * @return  The the output file template string.
    */
   public String getOutputToFile()
   {
      return outputToFile;
   }

   /**
    * Get P2J process name.
    * 
    * @return  The P2J process name.
    */
   public String getProcess()
   {
      return process;
   }
}