#!/usr/bin/env bash
# ansipa-fetch-alerts.sh — fetch security alerts from the server SMB share.
# Runs as root every 10 minutes via ansipa-fetch-alerts.timer (policy-scan-notify).
#
# For each alert on the server that hasn't been acknowledged yet:
#   - Downloads it to ~/administration/<hostname>/ for every active login session.
#   - A local file that has been deleted counts as acknowledged and is removed
#     from the server alerts directory on the next run.
#
# Prerequisites:
#   /etc/ansipa-smb.creds   — Samba credentials file (deployed by deploy-ansipa-policies.yml)
#   /etc/ipa/default.conf   — FreeIPA client config (provides server hostname)
#   smbclient               — from the samba-client package

set -euo pipefail

LOG_TAG="ansipa-fetch-alerts"
ADMIN_SUBDIR="administration"
CREDS_FILE="/etc/ansipa-smb.creds"
SMB_SHARE="ansipa-scans"
STATE_DIR="/var/lib/ansipa-policies"
FETCHED_STATE="$STATE_DIR/fetched-alerts"

log()  { echo "[$LOG_TAG] $*"; logger -t "$LOG_TAG" "$*" 2>/dev/null || true; }
warn() { echo "[$LOG_TAG][WARN] $*" >&2; logger -t "$LOG_TAG" "WARN: $*" 2>/dev/null || true; }

# ── Prerequisites ─────────────────────────────────────────────────────────────
if [[ ! -f "$CREDS_FILE" ]]; then
    warn "Credentials file $CREDS_FILE not found — run deploy-ansipa-policies.yml first."
    exit 0
fi
if ! command -v smbclient &>/dev/null; then
    warn "smbclient not installed — install samba-client."
    exit 0
fi

IPA_SERVER=$(awk '/^server[[:space:]]*=/{print $3}' /etc/ipa/default.conf 2>/dev/null || echo "")
if [[ -z "$IPA_SERVER" ]]; then
    warn "Cannot read IPA server from /etc/ipa/default.conf — host enrolled?"
    exit 0
fi

HOSTNAME=$(hostname -f 2>/dev/null || hostname)
mkdir -p "$STATE_DIR"
touch "$FETCHED_STATE"

smb() { smbclient "//$IPA_SERVER/$SMB_SHARE" -A "$CREDS_FILE" "$@" 2>/dev/null; }

# ── List active login sessions ────────────────────────────────────────────────
ACTIVE_USERS=()
while IFS= read -r LINE; do
    USER=$(echo "$LINE" | awk '{print $3}')
    [[ -z "$USER" || "$USER" == "root" ]] && continue
    HOME_DIR=$(getent passwd "$USER" | cut -d: -f6) || continue
    [[ -d "$HOME_DIR" ]] && ACTIVE_USERS+=("$USER:$HOME_DIR")
done < <(loginctl list-sessions --no-legend 2>/dev/null || who 2>/dev/null || true)
# Deduplicate by user.
mapfile -t ACTIVE_USERS < <(printf '%s\n' "${ACTIVE_USERS[@]}" | sort -u)

# ── List alerts on server for this host ───────────────────────────────────────
SERVER_ALERTS=()
while IFS= read -r LINE; do
    # smbclient ls output: "  filename.alert   A   1234   date"
    FILE=$(echo "$LINE" | awk '{print $1}')
    [[ "$FILE" == *.alert ]] && SERVER_ALERTS+=("$FILE")
done < <(smb -c "ls alerts\\$HOSTNAME\\" 2>/dev/null || true)

# ── Check for locally deleted alerts (acknowledged) ───────────────────────────
while IFS= read -r ALERT_NAME; do
    [[ -z "$ALERT_NAME" ]] && continue
    # If none of the active users still have this alert file, it was acknowledged.
    ALL_DELETED=true
    for USER_INFO in "${ACTIVE_USERS[@]}"; do
        HOME_DIR="${USER_INFO#*:}"
        USER="${USER_INFO%%:*}"
        LOCAL_FILE="$HOME_DIR/$ADMIN_SUBDIR/$HOSTNAME/$ALERT_NAME"
        if [[ -f "$LOCAL_FILE" ]]; then
            ALL_DELETED=false
            break
        fi
    done
    if [[ "$ALL_DELETED" == true ]] && [[ ${#ACTIVE_USERS[@]} -gt 0 ]]; then
        log "Alert acknowledged (deleted locally): $ALERT_NAME — removing from server"
        smb -c "del alerts\\$HOSTNAME\\$ALERT_NAME" 2>/dev/null || true
        # Remove from state file.
        sed -i "/^$ALERT_NAME\$/d" "$FETCHED_STATE" 2>/dev/null || true
    fi
done < "$FETCHED_STATE"

# ── Download new/pending alerts to user home dirs ─────────────────────────────
TMP_DIR=$(mktemp -d /tmp/ansipa-alerts.XXXXXX)
trap 'rm -rf "$TMP_DIR"' EXIT

for ALERT_NAME in "${SERVER_ALERTS[@]}"; do
    TMP_FILE="$TMP_DIR/$ALERT_NAME"

    # Download alert content from server.
    smb -c "get alerts\\$HOSTNAME\\$ALERT_NAME $TMP_FILE" 2>/dev/null || {
        warn "Failed to download alert: $ALERT_NAME"
        continue
    }

    NEW=false
    for USER_INFO in "${ACTIVE_USERS[@]}"; do
        HOME_DIR="${USER_INFO#*:}"
        USER="${USER_INFO%%:*}"
        LOCAL_DIR="$HOME_DIR/$ADMIN_SUBDIR/$HOSTNAME"
        LOCAL_FILE="$LOCAL_DIR/$ALERT_NAME"
        mkdir -p "$LOCAL_DIR"
        chown "$USER" "$LOCAL_DIR" 2>/dev/null || true

        if [[ ! -f "$LOCAL_FILE" ]]; then
            cp "$TMP_FILE" "$LOCAL_FILE"
            chown "$USER" "$LOCAL_FILE"
            chmod 644 "$LOCAL_FILE"
            NEW=true
        fi
    done

    # Track fetched alerts so we can detect acknowledgment on the next run.
    if ! grep -qx "$ALERT_NAME" "$FETCHED_STATE" 2>/dev/null; then
        echo "$ALERT_NAME" >> "$FETCHED_STATE"
    fi

    [[ "$NEW" == true ]] && log "New alert delivered: $ALERT_NAME"
done

log "Done. ${#SERVER_ALERTS[@]} server alert(s) for $HOSTNAME."
