fix(ansipa): fix ansipa-fetch-alerts.sh alert delivery and acknowledgment

Three bugs fixed, all confirmed end-to-end in a live FreeIPA + client VM:

1. Wrong `who` column: loginctl is SESSION/UID/USER (col 3), but `who` is
   USER/TTY/DATE (col 1) — the fallback used col 3, delivering to the wrong
   user.  Split into _parse_session_user() with per-source column selection.

2. Delivered to all users: alerts were sent to every active non-root session
   instead of only members of usr_scan-notify.  Added `id -nG` group check
   inside _parse_session_user().

3. smbclient ls path breakage: `smbclient -c "ls alerts\host\"` treats the
   argument as a glob pattern, returning NT_STATUS_NO_SUCH_FILE.  Changed to
   `cd alerts\host; ls` which correctly lists directory contents.

Bonus: track ACKNOWLEDGED set so alerts deleted-from-server in the current
run are not re-downloaded (avoiding a spurious WARN on the same run).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHops6PU4c2Mv5UyhUj8Ms
feat/astal-menu
Amir Alexander Abdelbaki 2026-07-01 11:37:00 +02:00
parent 65e859b8f9
commit 00ce4f5260
1 changed files with 40 additions and 11 deletions

View File

@ -46,26 +46,51 @@ touch "$FETCHED_STATE"
smb() { smbclient "//$IPA_SERVER/$SMB_SHARE" -A "$CREDS_FILE" "$@" 2>/dev/null; }
# ── List active login sessions ────────────────────────────────────────────────
# ── List active login sessions (usr_scan-notify members only) ─────────────────
# loginctl list-sessions --no-legend: SESSION UID USER SEAT TTY → $3 = USER
# who: USER TTY DATE TIME (HOST) → $1 = USER
# Only deliver to members of the usr_scan-notify FreeIPA user group.
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)
_parse_session_user() {
local line="$1" src="$2"
local user
if [[ "$src" == "loginctl" ]]; then
user=$(awk '{print $3}' <<< "$line")
else
user=$(awk '{print $1}' <<< "$line")
fi
[[ -z "$user" || "$user" == "root" || "$user" == "USER" ]] && return
# Only deliver to members of usr_scan-notify.
id -nG "$user" 2>/dev/null | tr ' ' '\n' | grep -qxF "usr_scan-notify" || return
local home
home=$(getent passwd "$user" | cut -d: -f6 2>/dev/null) || return
[[ -d "$home" ]] && ACTIVE_USERS+=("$user:$home")
}
if loginctl list-sessions --no-legend &>/dev/null; then
while IFS= read -r LINE; do
_parse_session_user "$LINE" "loginctl"
done < <(loginctl list-sessions --no-legend 2>/dev/null)
else
while IFS= read -r LINE; do
_parse_session_user "$LINE" "who"
done < <(who 2>/dev/null)
fi
# Deduplicate by user.
mapfile -t ACTIVE_USERS < <(printf '%s\n' "${ACTIVE_USERS[@]}" | sort -u)
mapfile -t ACTIVE_USERS < <(printf '%s\n' "${ACTIVE_USERS[@]+"${ACTIVE_USERS[@]}"}" | sort -u)
# ── List alerts on server for this host ───────────────────────────────────────
# smbclient `ls path\` treats the argument as a pattern, not a cd target;
# use `cd path; ls` to list directory contents instead.
SERVER_ALERTS=()
while IFS= read -r LINE; do
# smbclient ls output: " filename.alert A 1234 date"
FILE=$(echo "$LINE" | awk '{print $1}')
FILE=$(awk '{print $1}' <<< "$LINE")
[[ "$FILE" == *.alert ]] && SERVER_ALERTS+=("$FILE")
done < <(smb -c "ls alerts\\$HOSTNAME\\" 2>/dev/null || true)
done < <(smb -c "cd alerts\\$HOSTNAME; ls" 2>/dev/null || true)
# ── Check for locally deleted alerts (acknowledged) ───────────────────────────
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.
@ -82,8 +107,9 @@ while IFS= read -r ALERT_NAME; do
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.
# Remove from state file and skip re-download in this run.
sed -i "/^$ALERT_NAME\$/d" "$FETCHED_STATE" 2>/dev/null || true
ACKNOWLEDGED+=("$ALERT_NAME")
fi
done < "$FETCHED_STATE"
@ -92,6 +118,9 @@ TMP_DIR=$(mktemp -d /tmp/ansipa-alerts.XXXXXX)
trap 'rm -rf "$TMP_DIR"' EXIT
for ALERT_NAME in "${SERVER_ALERTS[@]}"; do
# Skip alerts already acknowledged (and deleted from server) in this run.
[[ " ${ACKNOWLEDGED[*]+"${ACKNOWLEDGED[*]}"} " == *" $ALERT_NAME "* ]] && continue
TMP_FILE="$TMP_DIR/$ALERT_NAME"
# Download alert content from server.