# 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;
       }
    }