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

readonly LABEL=REDIS02_Q132_SELINUX_ACL_REPAIR_ACTIVATE_VERIFY
readonly EXPECTED_HOST=sqjcredis02
readonly EXPECTED_IP=10.2.203.113
readonly VIP=10.2.203.117
readonly ENTRY=/root/redis-q131-q132-delivery-v2/redis-q131-q132
readonly ENTRY_SHA256=d229ade533bbe22f66f174bbcfccf685b470f00c466a5286649710be14794f5e
readonly Q131_PASS_SHA256=c4a0ef639ece14dfbe383465a9f3b5bbd4ecbb8e5e66d129830178b43adf9283
readonly READINESS_AUDIT=/var/backups/zhct-q132-activation-readiness/q132-activation-readiness-20260805T100537Z/operator.log
readonly SUPPORT_PACKAGE=policycoreutils-python-utils
readonly SCRIPT_PATH="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)/$(basename -- "${BASH_SOURCE[0]}")"
RUN_ID="q132-selinux-acl-activate-$(date -u +%Y%m%dT%H%M%SZ)"
AUDIT_DIR="/var/backups/zhct-q132-selinux-acl-activate/${RUN_ID}"
AUDIT_LOG="${AUDIT_DIR}/operator.log"
BASE_RUN_ID="${RUN_ID}-role"
mutation_started=0
success=0
acl_added=0
port_added=0
package_installed_this_run=0
ADDED_REGEXES=()

FCONTEXT_REGEXES=(
    '/opt/redis/5\.0\.14/bin(/.*)?'
    '/opt/redis/7\.2\.14/bin(/.*)?'
    '/etc/redis(/.*)?'
    '/data/redis5(/.*)?'
    '/data/redis7(/.*)?'
    '/var/log/redis5(/.*)?'
    '/var/log/redis7(/.*)?'
    '/var/run/redis5(/.*)?'
    '/var/run/redis7(/.*)?'
)
FCONTEXT_TYPES=(
    redis_exec_t redis_exec_t redis_conf_t redis_var_lib_t redis_var_lib_t
    redis_log_t redis_log_t redis_var_run_t redis_var_run_t
)
RESTORE_PATHS=(
    /opt/redis/5.0.14/bin /opt/redis/7.2.14/bin /etc/redis
    /data/redis5 /data/redis7 /var/log/redis5 /var/log/redis7
)

die() {
    printf 'ERROR: %s\n' "$*" >&2
    return 1
}

static_validate() {
    bash -n "$SCRIPT_PATH"
    [[ "${#FCONTEXT_REGEXES[@]}" -eq "${#FCONTEXT_TYPES[@]}" ]]
    grep -Fq 'q132 execute' "$SCRIPT_PATH"
    grep -Fq 'q132 verify' "$SCRIPT_PATH"
    grep -Fq 'q132 archive' "$SCRIPT_PATH"
    grep -Fq 'q132 rollback' "$SCRIPT_PATH"
    grep -Fq 'setfacl -m u:redis:--x /etc' "$SCRIPT_PATH"
    grep -Fq 'systemctl disable --now redis5.service redis7.service sentinel5.service sentinel7.service orchestrator.service' "$SCRIPT_PATH"
    if grep -En 'systemctl[[:space:]]+enable[^\n]*(sentinel|orchestrator)|systemctl[[:space:]]+start[^\n]*(sentinel|orchestrator)|ip[[:space:]]+addr[[:space:]]+add' "$SCRIPT_PATH" |
        grep -v 'grep -En'; then
        return 1
    fi
    if grep -En 'sha256sum[^\n]*(redis5\.password|redis7\.password)|cat[[:space:]]+[^\n]*(redis5\.password|redis7\.password)' "$SCRIPT_PATH" |
        grep -v 'grep -En'; then
        return 1
    fi
    printf '%s\n' \
        'Q132_SELINUX_ACL_ACTIVATE_STATIC_VALIDATE=PASS' \
        'REDIS_UNITS=REDIS5_REDIS7_ONLY' \
        'AUXILIARY_UNITS=DISABLED_INACTIVE' \
        'VIP=DISABLED' \
        'HA_CLAIM=PROHIBITED'
}

rollback_all() {
    set +e
    printf '%s\n' '=== AUTOMATIC SAFE ROLLBACK ==='
    "$ENTRY" q132 rollback \
        --activation-run-id "${BASE_RUN_ID}-activate" \
        --run-id "${BASE_RUN_ID}-rollback" || true
    systemctl disable --now redis5.service redis7.service sentinel5.service sentinel7.service orchestrator.service
    systemctl reset-failed redis5.service redis7.service
    if [[ "$acl_added" -eq 1 ]]; then setfacl -x u:redis /etc; fi
    chgrp root /etc/redis
    chmod 0750 /etc/redis
    if [[ "$port_added" -eq 1 ]]; then semanage port -d -p tcp 6387; fi
    for ((i=${#ADDED_REGEXES[@]}-1; i>=0; i--)); do
        semanage fcontext -d "${ADDED_REGEXES[$i]}"
    done
    restorecon -RF "${RESTORE_PATHS[@]}" || true
    chcon -R -t user_tmp_t /opt/redis/5.0.14/bin /opt/redis/7.2.14/bin || true
    printf 'Q132_SELINUX_ACL_ACTIVATE_ROLLBACK=SAFE_QUIET_ACL_AND_POLICY_REMOVED\n'
    if [[ "$package_installed_this_run" -eq 1 ]]; then
        printf 'Q132_SUPPORT_PACKAGE_ROLLBACK=RETAINED_REQUIRED_TOOLING_NO_DNF_UNDO\n'
    fi
    set -e
}

finish() {
    local rc=$?
    trap - EXIT
    if [[ "$rc" -ne 0 && "$mutation_started" -eq 1 && "$success" -eq 0 ]]; then
        rollback_all
    elif [[ "$rc" -eq 0 ]]; then
        printf 'Q132_SELINUX_ACL_ACTIVATE_ROLLBACK=NOT_REQUIRED\n'
    else
        printf 'Q132_SELINUX_ACL_ACTIVATE_ROLLBACK=NOT_REQUIRED_ZERO_ROLE_MUTATION\n'
    fi
    printf '%s\n' \
        'SECRET_VALUES=NOT_OUTPUT_NOT_HASHED' \
        'SENTINEL_HA=NOT_CONFIGURED' \
        'ORCHESTRATOR_RAFT=NOT_CONFIGURED' \
        'VIP=DISABLED' \
        'CROSS_HOST_HA=NOT_ACCEPTED'
    printf 'END|%s|RC=%s|AUDIT_LOG=%s\n' "$LABEL" "$rc" "$AUDIT_LOG"
    exit "$rc"
}
trap finish EXIT

printf 'BEGIN|%s|%s\n' "$LABEL" "$(date '+%Y-%m-%dT%H:%M:%S%z')"
if [[ "${1:-}" == --static-validate ]]; then
    static_validate
    exit 0
fi
[[ $# -eq 0 ]]
[[ $(id -u) -eq 0 ]]
actual_host=$(hostname -s)
[[ "$actual_host" == "$EXPECTED_HOST" ]]
ip -o -4 addr show scope global | awk '{split($4,a,"/"); print a[1]}' | grep -Fxq "$EXPECTED_IP"
printf 'Q132_REPAIR_ACTIVATE_TARGET_IDENTITY=PASS|host=%s|ip=%s\n' "$actual_host" "$EXPECTED_IP"

[[ -f "$ENTRY" && ! -L "$ENTRY" && $(stat -c '%U:%G:%a' "$ENTRY") == root:root:750 ]]
printf '%s  %s\n' "$ENTRY_SHA256" "$ENTRY" | sha256sum -c -
[[ -f "$READINESS_AUDIT" && ! -L "$READINESS_AUDIT" ]]
grep -Fxq 'Q132_IMPORT_PASS_RECEIPT_GATE=PASS' "$READINESS_AUDIT"
grep -Fxq 'READINESS_COMPONENTS|dac=0|selinux=0|quiet=1' "$READINESS_AUDIT"
grep -Fxq 'Q132_ACTIVATION_GATE=BLOCKED_REQUIRES_CONTROLLED_SELINUX_OR_DAC_REPAIR' "$READINESS_AUDIT"
grep -Fxq 'Q132_ACTIVATION_READINESS_DIAGNOSIS=PASS_READ_ONLY' "$READINESS_AUDIT"
grep -Fxq 'SERVICE_MUTATION=ZERO' "$READINESS_AUDIT"
grep -Fxq 'SELINUX_MUTATION=ZERO' "$READINESS_AUDIT"

for command_name in dnf rpm semanage restorecon matchpathcon getenforce setfacl getfacl systemctl ss runuser sha256sum; do
    if [[ "$command_name" == semanage ]] && ! command -v semanage >/dev/null 2>&1; then continue; fi
    command -v "$command_name" >/dev/null 2>&1 || die "missing command: $command_name"
done
[[ $(getenforce) == Enforcing ]]
for unit in redis5.service redis7.service sentinel5.service sentinel7.service orchestrator.service; do
    [[ $(systemctl is-enabled "$unit" 2>/dev/null || true) == disabled ]]
    [[ $(systemctl is-active "$unit" 2>/dev/null || true) == inactive ]]
done
! ss -lntupH | grep -E ':(6379|6387|26379|26387|3000|10008)([[:space:]]|$)' >/dev/null
[[ $(stat -c '%U:%G:%a' /etc) == root:root:750 ]]
[[ $(stat -c '%U:%G:%a' /etc/redis) == root:root:750 ]]
! getfacl -cp /etc | grep -q '^user:redis:'
for config in /etc/redis/redis5.conf /etc/redis/redis7.conf; do
    [[ -f "$config" && ! -L "$config" && $(stat -c '%U:%G:%a' "$config") == root:redis:640 ]]
    grep -Eq '__[A-Z0-9_]+__' "$config"
done
for secret in /etc/zhct/secrets/redis5.password /etc/zhct/secrets/redis7.password; do
    [[ -f "$secret" && ! -L "$secret" && -s "$secret" ]]
    [[ $(stat -c '%U:%G:%a' "$secret") == root:root:600 ]]
done
printf 'Q132_REPAIR_ACTIVATE_PREPACKAGE_GATE=PASS\n'

install -d -o root -g root -m 0700 "$AUDIT_DIR"
exec > >(tee -a "$AUDIT_LOG") 2>&1
printf 'AUDIT_BEGIN|%s|%s\n' "$LABEL" "$(date '+%Y-%m-%dT%H:%M:%S%z')"
printf 'RUN_ID=%s\nBASE_RUN_ID=%s\n' "$RUN_ID" "$BASE_RUN_ID"

if ! rpm -q "$SUPPORT_PACKAGE" >/dev/null 2>&1; then
    plan_file="${AUDIT_DIR}/dnf-assumeno.log"
    set +e
    dnf --assumeno install "$SUPPORT_PACKAGE" >"$plan_file" 2>&1
    plan_rc=$?
    set -e
    cat "$plan_file"
    [[ "$plan_rc" -eq 1 ]]
    grep -Eq 'Installing:|Install[[:space:]]+[0-9]+ Package' "$plan_file"
    ! grep -Eq 'Removing:|Upgrading:|Downgrading:' "$plan_file"
    printf 'Q132_SEMANAGE_DNF_PLAN_GATE=PASS_INSTALL_ONLY\n'
    dnf -y install "$SUPPORT_PACKAGE"
    package_installed_this_run=1
fi
command -v semanage >/dev/null 2>&1
rpm -q "$SUPPORT_PACKAGE"
printf 'Q132_SEMANAGE_SUPPORT=PASS\n'

policy_fcontexts=$(semanage fcontext -l 2>&1)
for required_type in redis_exec_t redis_conf_t redis_var_lib_t redis_log_t redis_var_run_t; do
    grep -Fq ":${required_type}:" <<<"$policy_fcontexts"
done
FCONTEXT_ACTIONS=()
for ((i=0; i<${#FCONTEXT_REGEXES[@]}; i++)); do
    regex=${FCONTEXT_REGEXES[$i]}
    type=${FCONTEXT_TYPES[$i]}
    matches=$(grep -F -- "$regex" <<<"$policy_fcontexts" || true)
    if [[ -z "$matches" ]]; then
        FCONTEXT_ACTIONS+=(ADD_MISSING)
    elif grep -Fv ":${type}:" <<<"$matches" | grep -q .; then
        die "conflicting fcontext: $regex"
    else
        FCONTEXT_ACTIONS+=(KEEP_EXISTING_DESIRED)
    fi
done
port_table=$(semanage port -l 2>&1)
grep -E '^redis_port_t[[:space:]]+tcp[[:space:]].*(^|[[:space:],])6379([[:space:],]|$)' <<<"$port_table" >/dev/null
port_6387_owner=$(grep -E '(^|[[:space:],])6387([[:space:],]|$)' <<<"$port_table" | awk '{print $1}' | sort -u | paste -sd ',' - || true)
case "$port_6387_owner" in
    '') PORT_6387_ACTION=ADD ;;
    redis_port_t) PORT_6387_ACTION=KEEP ;;
    *) die "tcp/6387 conflicting type: $port_6387_owner" ;;
esac
printf '%s\n' '=== REPAIR PLAN ==='
for ((i=0; i<${#FCONTEXT_REGEXES[@]}; i++)); do
    printf 'FCONTEXT|%s|%s|%s\n' "${FCONTEXT_ACTIONS[$i]}" "${FCONTEXT_TYPES[$i]}" "${FCONTEXT_REGEXES[$i]}"
done
printf 'PORT_6387_ACTION=%s\n' "$PORT_6387_ACTION"
printf 'ACL_ACTION=ADD_USER_REDIS_TRAVERSE_ONLY_TO_ETC\n'
printf 'Q132_SELINUX_ACL_REPAIR_PREWRITE_GATE=PASS\n'

mutation_started=1
for ((i=0; i<${#FCONTEXT_REGEXES[@]}; i++)); do
    if [[ "${FCONTEXT_ACTIONS[$i]}" == ADD_MISSING ]]; then
        semanage fcontext -a -t "${FCONTEXT_TYPES[$i]}" "${FCONTEXT_REGEXES[$i]}"
        ADDED_REGEXES+=("${FCONTEXT_REGEXES[$i]}")
    fi
done
if [[ "$PORT_6387_ACTION" == ADD ]]; then
    semanage port -a -t redis_port_t -p tcp 6387
    port_added=1
fi
restorecon -RFv "${RESTORE_PATHS[@]}"
setfacl -m u:redis:--x /etc
acl_added=1
getfacl -cp /etc | grep -Fxq 'user:redis:--x'
chgrp redis /etc/redis
chmod 0750 /etc/redis
[[ $(stat -c '%U:%G:%a' /etc/redis) == root:redis:750 ]]
for path in /opt/redis/5.0.14/bin/redis-server /opt/redis/7.2.14/bin/redis-server; do
    case "$(stat -c '%C' "$path")" in *:redis_exec_t:*) ;; *) die "redis_exec_t not applied: $path" ;; esac
done
for config in /etc/redis/redis5.conf /etc/redis/redis7.conf; do
    runuser -u redis -- test -r "$config"
done
printf 'Q132_SELINUX_ACL_REPAIR=PASS\n'

if ! "$ENTRY" q132 execute --run-id "$BASE_RUN_ID" --q131-pass-sha "$Q131_PASS_SHA256"; then
    die 'q132 execute failed'
fi
"$ENTRY" q132 verify --run-id "${BASE_RUN_ID}-verify" --q131-pass-sha "$Q131_PASS_SHA256"
"$ENTRY" q132 archive --source-run-id "$BASE_RUN_ID"

for unit in redis5.service redis7.service; do
    [[ $(systemctl is-enabled "$unit") == enabled ]]
    [[ $(systemctl is-active "$unit") == active ]]
done
for unit in sentinel5.service sentinel7.service orchestrator.service; do
    [[ $(systemctl is-enabled "$unit" 2>/dev/null || true) == disabled ]]
    [[ $(systemctl is-active "$unit" 2>/dev/null || true) == inactive ]]
done
ss -lntH | awk '{print $4}' | grep -Fxq "$EXPECTED_IP:6379"
ss -lntH | awk '{print $4}' | grep -Fxq "$EXPECTED_IP:6387"
for port in 26379 26387 3000 10008; do
    ! ss -lntH | awk '{print $4}' | grep -Eq ":${port}$"
done
! ip -o -4 addr show | grep -Fq "$VIP/"
IFS= read -r redis5_auth </etc/zhct/secrets/redis5.password
IFS= read -r redis7_auth </etc/zhct/secrets/redis7.password
[[ $(REDISCLI_AUTH="$redis5_auth" /opt/redis/5.0.14/bin/redis-cli --no-auth-warning -h "$EXPECTED_IP" -p 6379 PING) == PONG ]]
[[ $(REDISCLI_AUTH="$redis7_auth" /opt/redis/7.2.14/bin/redis-cli --no-auth-warning -h "$EXPECTED_IP" -p 6387 PING) == PONG ]]
REDISCLI_AUTH="$redis5_auth" /opt/redis/5.0.14/bin/redis-cli --no-auth-warning -h "$EXPECTED_IP" -p 6379 INFO replication | grep -E '^(role|master_host|master_link_status):' || true
REDISCLI_AUTH="$redis7_auth" /opt/redis/7.2.14/bin/redis-cli --no-auth-warning -h "$EXPECTED_IP" -p 6387 INFO replication | grep -E '^(role|master_host|master_link_status):' || true
unset redis5_auth redis7_auth

success=1
printf '%s\n' \
    'Q132_SELINUX_ACL_REPAIR_ACTIVATE_VERIFY=PASS_REDIS02_LOCAL_ROLE_ACTIVATED' \
    'Q132_ROLE_VERIFY=PASS_LOCAL_ONLY' \
    'Q132_EVIDENCE_ARCHIVE=PASS' \
    'SENTINEL5=DISABLED_INACTIVE' \
    'SENTINEL7=DISABLED_INACTIVE' \
    'ORCHESTRATOR=DISABLED_INACTIVE'
