#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

readonly EXPECTED_HOST="sqjcnginx01"
readonly EXPECTED_IP="10.2.203.102"
readonly EXPECTED_LOCK_OWNER="NGINX_Q131_SECRET_EXPORT"
readonly EXPECTED_SOURCE_CONTRACT="Q101_NGINX01_CANONICAL"
readonly SOURCE_DIR="/etc/zhct/secrets"
readonly STAGING_PARENT="/root/q131-secret-transfer"
readonly STAGING_DIR="${STAGING_PARENT}/redis-from-nginx01"
readonly AUDIT_DIR="${STAGING_PARENT}/audit"
readonly RECEIPT="${STAGING_DIR}/SOURCE_RECEIPT.txt"
readonly SECRET_NAMES=("redis5.password" "redis7.password")
readonly SCRIPT_PATH="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)/$(basename -- "${BASH_SOURCE[0]}")"

MODE="${1:---static-validate}"
RUN_ID="${Q131_SECRET_EXPORT_RUN_ID:-q131-secret-export-$(date -u +%Y%m%dT%H%M%SZ)}"
STAGING_CREATED=0

identity_values_gate() {
  local actual_host="$1"
  local ip_lines="$2"
  [[ "${actual_host}" == "${EXPECTED_HOST}" ]] &&
    grep -Eq "(^|[[:space:]])${EXPECTED_IP}/" <<<"${ip_lines}"
}

identity_gate() {
  local actual_host ip_lines
  actual_host="$(hostname -s)"
  ip_lines="$(ip -o -4 addr show)"
  printf 'Q131_TARGET_HOST=%s\n' "${actual_host}"
  printf '%s\n' "${ip_lines}"
  identity_values_gate "${actual_host}" "${ip_lines}"
  printf 'Q131_TARGET_IDENTITY=PASS\n'
}

lock_gate() {
  [[ "${Q131_REMOTE_LOCK_GRANTED:-}" == "1" ]]
  [[ "${Q131_REMOTE_LOCK_OWNER:-}" == "${EXPECTED_LOCK_OWNER}" ]]
  [[ "${Q131_SECRET_SOURCE_CONTRACT:-}" == "${EXPECTED_SOURCE_CONTRACT}" ]]
  printf 'Q131_REMOTE_LOCK_GATE=PASS\n'
  printf 'Q131_SOURCE_CONTRACT=%s\n' "${EXPECTED_SOURCE_CONTRACT}"
}

root_gate() {
  [[ "${EUID}" -eq 0 ]]
  printf 'Q131_ROOT_GATE=PASS\n'
}

run_id_gate() {
  [[ "${RUN_ID}" =~ ^q131-secret-(export|cleanup)-[A-Za-z0-9._-]+$ ]]
  printf 'Q131_RUN_ID_GATE=PASS\n'
}

pre_mutation_gate() {
  run_id_gate
  root_gate
  identity_gate
  lock_gate
  printf 'Q131_PRE_MUTATION_GATE=PASS\n'
}

safe_stat() {
  stat -c 'TYPE=%F MODE=%a OWNER=%U:%G SIZE=%s' -- "$1"
}

validate_secret_file() {
  local file="$1"
  local mode owner group
  [[ -f "${file}" && ! -L "${file}" ]]
  owner="$(stat -c '%U' -- "${file}")"
  group="$(stat -c '%G' -- "${file}")"
  mode="$(stat -c '%a' -- "${file}")"
  [[ "${owner}" == "root" && "${group}" == "root" ]]
  [[ "${mode}" == "600" || "${mode}" == "400" ]]
  [[ -s "${file}" ]]
  awk 'NR != 1 || length($0) == 0 { bad=1 } END { exit (bad || NR != 1) }' "${file}"
}

source_fingerprint() {
  stat -c '%d:%i:%s:%Y:%a:%U:%G' -- "$1"
}

validate_source_dir() {
  [[ -d "${SOURCE_DIR}" && ! -L "${SOURCE_DIR}" ]]
  [[ "$(stat -c '%U:%G' -- "${SOURCE_DIR}")" == "root:root" ]]
  [[ "$(stat -c '%a' -- "${SOURCE_DIR}")" == "700" ]]
}

validate_staging_dir() {
  [[ -d "${STAGING_DIR}" && ! -L "${STAGING_DIR}" ]]
  [[ "$(stat -c '%U:%G' -- "${STAGING_DIR}")" == "root:root" ]]
  [[ "$(stat -c '%a' -- "${STAGING_DIR}")" == "700" ]]
}

cleanup_partial_export() {
  if [[ "${STAGING_CREATED}" == "1" ]]; then
    rm -f -- \
      "${STAGING_DIR}/redis5.password" \
      "${STAGING_DIR}/redis7.password" \
      "${RECEIPT}"
    rmdir -- "${STAGING_DIR}" 2>/dev/null || true
  fi
}

finish() {
  local rc=$?
  if [[ "${rc}" -ne 0 && "${MODE}" == "export" ]]; then
    if [[ "${STAGING_CREATED}" == "1" ]]; then
      cleanup_partial_export
      printf 'Q131_EXPORT_ROLLBACK=PARTIAL_STAGING_REMOVED\n'
    else
      printf 'Q131_EXPORT_ROLLBACK=NOT_REQUIRED_NO_STAGED_SECRET_CREATED\n'
    fi
  fi
  printf 'Q131_SECRET_EXPORT_END MODE=%s RC=%s\n' "${MODE}" "${rc}"
  exit "${rc}"
}
trap finish EXIT

static_validate() {
  printf 'Q131_SECRET_EXPORT_BEGIN MODE=--static-validate\n'
  bash -n "${SCRIPT_PATH}"
  [[ "${EXPECTED_HOST}" == "sqjcnginx01" ]]
  [[ "${EXPECTED_IP}" == "10.2.203.102" ]]
  [[ "${STAGING_DIR}" == "/root/q131-secret-transfer/redis-from-nginx01" ]]
  if identity_values_gate "wrong-host" "${EXPECTED_IP}/24"; then
    printf 'Q131_WRONG_HOST_NEGATIVE_TEST=FAIL\n'
    return 1
  fi
  printf 'Q131_WRONG_HOST_NEGATIVE_TEST=PASS\n'
  printf 'Q131_SECRET_VALUE_OUTPUT=PROHIBITED\n'
  printf 'Q131_SECRET_CONTENT_SHA=PROHIBITED\n'
  printf 'Q131_STATIC_VALIDATE_PASS\n'
}

export_secrets() {
  local name source staged before after
  printf 'Q131_SECRET_EXPORT_BEGIN MODE=export\n'

  for name in "${SECRET_NAMES[@]}"; do
    source="${SOURCE_DIR}/${name}"
    staged="${STAGING_DIR}/${name}"
    before="$(source_fingerprint "${source}")"
    install -m 0600 -o root -g root -- "${source}" "${staged}"
    validate_secret_file "${staged}"
    cmp -s -- "${source}" "${staged}"
    after="$(source_fingerprint "${source}")"
    [[ "${before}" == "${after}" ]]
    printf 'STAGED_FILE=%s %s COPY_MATCH=PASS SOURCE_UNCHANGED=PASS\n' \
      "${staged}" "$(safe_stat "${staged}")"
  done

  {
    printf 'Q131_SOURCE_RECEIPT_VERSION=1\n'
    printf 'RUN_ID=%s\n' "${RUN_ID}"
    printf 'CREATED_AT=%s\n' "$(date --iso-8601=seconds)"
    printf 'SOURCE_HOST=%s\n' "${EXPECTED_HOST}"
    printf 'SOURCE_IP=%s\n' "${EXPECTED_IP}"
    printf 'SOURCE_CONTRACT=%s\n' "${EXPECTED_SOURCE_CONTRACT}"
    for name in "${SECRET_NAMES[@]}"; do
      printf 'TRANSFER_FILE=%s SOURCE=%s STAGED=%s OWNER=root:root MODE=600 SIZE=%s\n' \
        "${name}" "${SOURCE_DIR}/${name}" "${STAGING_DIR}/${name}" \
        "$(stat -c '%s' -- "${STAGING_DIR}/${name}")"
    done
    printf 'SECRET_VALUES=NOT_OUTPUT_NOT_LOGGED\n'
    printf 'SECRET_CONTENT_SHA=NOT_CALCULATED\n'
  } >"${RECEIPT}"
  chmod 0600 -- "${RECEIPT}"

  validate_staging_dir
  [[ "$(stat -c '%U:%G' -- "${RECEIPT}")" == "root:root" ]]
  [[ "$(stat -c '%a' -- "${RECEIPT}")" == "600" ]]
  printf 'SOURCE_RECEIPT=%s %s\n' "${RECEIPT}" "$(safe_stat "${RECEIPT}")"
  printf 'Q131_SECRET_EXPORT_PASS\n'
  printf 'Q131_SFTP_DOWNLOAD_REDIS5=%s\n' "${STAGING_DIR}/redis5.password"
  printf 'Q131_SFTP_DOWNLOAD_REDIS7=%s\n' "${STAGING_DIR}/redis7.password"
  printf 'Q131_SFTP_DOWNLOAD_RECEIPT=%s\n' "${RECEIPT}"
  printf 'Q131_SECRET_EXPORT_ROLLBACK=NOT_REQUIRED\n'
}

cleanup_staging() {
  local name staged
  printf 'Q131_SECRET_EXPORT_BEGIN MODE=cleanup\n'
  rm -f -- \
    "${STAGING_DIR}/redis5.password" \
    "${STAGING_DIR}/redis7.password" \
    "${RECEIPT}"
  rmdir -- "${STAGING_DIR}"
  printf 'Q131_SECRET_STAGING_CLEANUP_PASS\n'
  printf 'Q131_AUDIT_DIR_RETAINED=%s\n' "${AUDIT_DIR}"
}

case "${MODE}" in
  --static-validate)
    static_validate
    ;;
  export)
    pre_mutation_gate
    [[ ! -e "${STAGING_DIR}" ]]
    install -d -m 0700 -o root -g root -- "${STAGING_PARENT}" "${AUDIT_DIR}"
    exec > >(tee -a "${AUDIT_DIR}/${RUN_ID}-${MODE}.log") 2>&1
    validate_source_dir
    for name in "${SECRET_NAMES[@]}"; do
      source="${SOURCE_DIR}/${name}"
      validate_secret_file "${source}"
      printf 'SOURCE_FILE=%s %s LINE_POLICY=ONE_NONEMPTY_LINE\n' \
        "${source}" "$(safe_stat "${source}")"
    done
    printf 'Q131_SOURCE_METADATA_GATE=PASS\n'
    install -d -m 0700 -o root -g root -- "${STAGING_DIR}"
    STAGING_CREATED=1
    export_secrets
    ;;
  cleanup)
    pre_mutation_gate
    [[ "${Q131_SECRET_TRANSFER_CONSUMED:-}" == "YES" ]]
    validate_staging_dir
    for name in "${SECRET_NAMES[@]}"; do
      staged="${STAGING_DIR}/${name}"
      validate_secret_file "${staged}"
      [[ "$(stat -c '%a' -- "${staged}")" == "600" ]]
    done
    [[ -f "${RECEIPT}" && ! -L "${RECEIPT}" ]]
    [[ "$(stat -c '%U:%G' -- "${RECEIPT}")" == "root:root" ]]
    [[ "$(stat -c '%a' -- "${RECEIPT}")" == "600" ]]
    [[ -d "${AUDIT_DIR}" && ! -L "${AUDIT_DIR}" ]]
    exec > >(tee -a "${AUDIT_DIR}/${RUN_ID}-${MODE}.log") 2>&1
    cleanup_staging
    ;;
  *)
    printf 'Usage: %s --static-validate|export|cleanup\n' "${SCRIPT_PATH}" >&2
    exit 64
    ;;
esac
