#!/usr/bin/bash
# set -xv

usage() {
    echo -e 'Usage: platsec-trust [silo]' 1>&2
    echo -e '\t where silo is either of prod, eng, alpaca_prod, alpaca_eng, alpaca_pdx, workday, aws, awsdev, il4, eu_sov, all' 1>&2
    echo -e '\t Example:' 1>&2
    echo -e '\t\t platsec-trust prod' 1>&2
    echo -e '\t\t platsec-trust il4  (FIPS 140-3 compliant)' 1>&2
    echo -e '\t\t platsec-trust eu_sov' 1>&2
    exit 1
}


if [[ "$#" -ne 1 || ("$1" != "prod" && "$1" != "eng" && "$1" != "workday" && "$1" != "aws" && "$1" != "awsdev" && "$1" != "all" && "$1" != "alpaca_eng" && "$1" != "alpaca_pdx" && "$1" != "alpaca_prod" && "$1" != "il4" && "$1" != "eu_sov") ]]; then
    usage
fi

echo "### Initializing platsec-trust script, check out the logs in /etc/platsec/logs"

# Injecting a configuration to redirect stdout and stderr to log file in /etc/platsec/logs
source /etc/platsec/conf/platsec-logs-vars platsec-trust

# Getting OS info
source /etc/platsec/conf/os_info

# Creating lockfile for any service running platsec-trust, timeout 180 seconds (3 minutes)
source /etc/platsec/conf/process_validation

# Cleaning up logs older than 30 days, if the directory exist
if [[ -d $LOG_PATH ]]; then
   echo "## Cleaning up logs older than 30 days"
   find "$LOG_PATH" -type f -mtime +30 -name '*.log' -delete ;
fi

# Setup host
SILO=$1
if [[ $SILO == "aws" ]]; then
    SILO="prod"
fi

if [[ $SILO == "awsdev" ]]; then
    SILO="eng"
fi

if [[ $SILO == "alpaca_eng" ]]; then
    SILO="alpaca_eng"
fi

if [[ $SILO == "alpaca_pdx" ]]; then
    # alpaca_pdx is for backward compatibility
    SILO="alpaca_eng"
fi

if [[ $SILO == "alpaca_prod" ]]; then
    SILO="alpaca_prod"
fi

# FIPS Configuration for IL4 silo
FIPS_MODE=false
FIPS_JAR_PATH="/usr/share/platsec/lib/bc-fips-2.1.2.jar"
KEYTOOL_FIPS_ARGS=""

if [[ $SILO == "il4" ]]; then
    echo "### Enabling FIPS 140-3 mode for IL4 silo"
    FIPS_MODE=true
    if [[ ! -f "$FIPS_JAR_PATH" ]]; then
        echo "ERROR: FIPS mode required but BouncyCastle FIPS JAR not found at $FIPS_JAR_PATH"
        echo "ERROR: Please ensure the bc-fips-2.1.2.jar is installed with the platsec-certs package"
        exit 1
    fi
    KEYTOOL_FIPS_ARGS="-storetype BCFKS -providerclass org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider -providerpath $FIPS_JAR_PATH"
    echo "### FIPS JAR found at: $FIPS_JAR_PATH"
    echo "### FIPS keytool arguments: $KEYTOOL_FIPS_ARGS"
fi

echo "### Runing platsec-system-certs scripts"
bash -x /usr/bin/platsec-system-certs $SILO

# Setup java - adapted from workday-truststore
echo "### Setting up Java"
STORE_PASSW=$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d" ")
JAVA_LOC="/usr/java/jre* /usr/java/jdk*/jre /usr/local/jre* /usr/local/jdk*/jre /usr/lib/jvm/zulu*/jre /usr/lib/jvm/zre* /usr/local/zdk*/jre /usr/local/zre* /usr/local/zingjdk*/jre /usr/local/zingjre* /usr/local/zing*/jre /usr/local/zing* /usr/local/zulu*/jre /usr/local/zulu* /usr/lib/java/*/jre /usr/lib/jvm/*/jre /usr/lib/jvm/*"

# Build deduplicated list of Java installations
declare -A REAL_PATH_MAP
declare -a UNIQUE_JRE_LIST

echo "### Scanning for Java installations and deduplicating symlinks"
for jre in ${JAVA_LOC} ; do
    # Skip Java 1.6 installations
    if [[ $jre =~ ^/usr/local/jre1.6.(.*) ]] ; then
       continue
    fi
    if [[ $jre =~ ^/usr/local/jdk1.6.(.*) ]] ; then
       continue
    fi

    # Skip old Java 1.7.0 versions (below update 11)
    if [[ $jre =~ ^/usr/local/jre1.7.0_(.*) ]] ; then
        jversion=10#${BASH_REMATCH[1]}
        if [[ $jversion -lt 11 ]] ; then
            continue
        fi
    fi

    # Check if lib/security directory exists
    if [[ ! -d "${jre}/lib/security" ]] ; then
       continue;
    fi

    # Deduplicate symlinks using realpath if available
    if command -v realpath &> /dev/null; then
        REAL_JRE_PATH=$(realpath "${jre}" 2>/dev/null || echo "${jre}")
        if [[ -n "${REAL_PATH_MAP[$REAL_JRE_PATH]}" ]]; then
            echo "Skipping ${jre} (symlink to already found ${REAL_PATH_MAP[$REAL_JRE_PATH]})"
            continue
        fi
        REAL_PATH_MAP[$REAL_JRE_PATH]="${jre}"
        UNIQUE_JRE_LIST+=("${jre}")
        echo "Found Java installation: ${jre} (real path: ${REAL_JRE_PATH})"
    else
        # If realpath is not available, add all paths without deduplication
        UNIQUE_JRE_LIST+=("${jre}")
        echo "Found Java installation: ${jre} (realpath not available, symlink deduplication disabled)"
    fi
done

echo "### Processing ${#UNIQUE_JRE_LIST[@]} unique Java installation(s)"

# Process only the deduplicated list
for jre in "${UNIQUE_JRE_LIST[@]}" ; do
    echo "Installing to JRE_HOME: " "${jre}"

    JSSECACERTS_PATH="${jre}/lib/security/jssecacerts"
    TIMESTAMP="$(date +"%F-%H-%M-%S")"
    JSSECACERTS_DIR_BACKUP_PATH="${JSSECACERTS_PATH}.${TIMESTAMP}.backup"
    JRE_HOME=${jre}
    DEST_KS="${JSSECACERTS_PATH}.${TIMESTAMP}.keystore"

    #
    # BEGIN KEYSTORE CREATION
    #
    # Generate a new jssecacerts keystore to a temporary path, leaving the existing one as-is for now
    #

    #### require prefix to be the JRE location to install the truststore into
    SRC_KS=${JRE_HOME}/lib/security/cacerts
    if ! [[ -e "${SRC_KS}" ]]; then
       echo "Missing the standard JRE cacerts: ${SRC_KS}."
       echo "Skipping ${JRE_HOME}"
       continue
    fi


    if [[ $SILO != "alpaca_eng" && $SILO != "alpaca_prod" ]]; then
      #### create a new keystore with the same contents as JDK baseline cacerts but different password
      #### if using alpaca_eng or alpaca_prod, we don't want cacerts as a baseline, but instead start empty.
      echo "### Creating a new keystore based on JDK baseline"
      if [[ "$FIPS_MODE" == "true" ]]; then
          echo "### Using FIPS-compliant keytool for keystore import"
          yes "" | "${JRE_HOME}"/bin/keytool -importkeystore $KEYTOOL_FIPS_ARGS -srckeystore "${SRC_KS}" -destkeystore "${DEST_KS}" -deststorepass "${STORE_PASSW}" -noprompt
      else
          yes "" | "${JRE_HOME}"/bin/keytool -importkeystore -srckeystore "${SRC_KS}" -destkeystore "${DEST_KS}" -deststorepass "${STORE_PASSW}" -noprompt
      fi
      RETVAL=$?
      if [[ $RETVAL -ne 0 ]]; then
         echo "keytool import from JDK baseline cacerts failed: $RETVAL"
         continue
      fi
    fi

    #### Add the found certs
    if [[ $SILO == "all" ]]
    then
      NEW_CERTS=$(ls /etc/platsec/silos/eng/*.cer)
      NEW_CERTS+=$(ls /etc/platsec/silos/prod/*.cer)
    else
      NEW_CERTS=$(ls /etc/platsec/silos/${SILO}/*.cer)
    fi

    #### Add each cert. If using alpaca_eng or alpaca_prod, new keystore will be created here.
    for CERT in ${NEW_CERTS}
    do
        echo "### $(date) Start to import cert: ${CERT} - Silo: ${SILO}"
        ALIAS=${CERT%.cer} # Strip .cer, we remove the leading path below
        if [[ "$FIPS_MODE" == "true" ]]; then
            echo "### Using FIPS-compliant keytool for certificate import: ${ALIAS##*/}"
            "${JRE_HOME}"/bin/keytool -importcert $KEYTOOL_FIPS_ARGS -alias "${ALIAS##*/}" -keystore "${DEST_KS}" -storepass "${STORE_PASSW}" -file "${CERT}" -noprompt
        else
            "${JRE_HOME}"/bin/keytool -importcert -alias "${ALIAS##*/}" -keystore "${DEST_KS}" -storepass "${STORE_PASSW}" -file "${CERT}" -noprompt
        fi
        echo "### $(date) Finish to import cert: ${CERT} - Silo: ${SILO}"
    done

    ##### save the password if find wmu-bda-server Chef Role
    if [[ -f /usr/local/etc/WMU-BDA ]]; then
        echo "### Save password for wmu-bda-server Chef Role"
        echo "${STORE_PASSW}" >& "${JRE_HOME}"/lib/security/trustcerts.pwd
    fi



    #
    # BEGIN KEYSTORE REPLACEMENT
    #
    # Once the new jssecacerts has been created at the temporary path, move the new one to the old one's place
    #
    # If jssecacerts is a file, the existing file will be overwritten
    # If jssecacerts is a directory for some reason:
    # - Backup the directory to a "jssecacerts.TIMESTAMP.backup" path
    # - If the above move operation succeeds, move the temp keystore path to
    #

    # If jssecacerts exists as a file, remove it up before generating a new one
    if [[ -f "$JSSECACERTS_PATH" ]]; then
      if ! rm "${JSSECACERTS_PATH}"; then
        echo "Unable to replace existing jssecacerts in ${jre}. Check permissions on file or remove files manually and then try executing platsec-trust again"
        rm -f "${DEST_KS}"
        continue
      fi

    # If jssecacerts exists as a directory, move it before generating a new one
    elif [[ -d "$JSSECACERTS_PATH" ]]; then
      echo "Moving ${JSSECACERTS_PATH} to ${JSSECACERTS_DIR_BACKUP_PATH}"
      if ! mv "${JSSECACERTS_PATH}" "${JSSECACERTS_DIR_BACKUP_PATH}"; then
        echo "Unable to backup existing jssecacerts in ${jre}. Check permissions on directory or move files manually and then try executing platsec-trust again"
        rm -f "${DEST_KS}"
        continue
      fi
    fi

    if ! mv "${DEST_KS}" "${JSSECACERTS_PATH}"; then
      echo "Unable to move jssecacerts ${DEST_KS} to ${JSSECACERTS_PATH}. Check permissions on directory "
      rm -f "${DEST_KS}"
      continue
    fi

    echo "Finished importing certificates to ${jre}."
    PASS_FILE="/etc/platsec/.jssecacerts_pass"
    echo "${STORE_PASSW}" > ${PASS_FILE}
    chmod 444 ${PASS_FILE}
    chmod 444 "${JSSECACERTS_PATH}"
    echo "### Finished to setup password file"
    true
    echo "### Platsec-trust script concluded successfully"
done
# Lockfile cleanup is handled by trap in process_validation