#!/bin/bash
# =============================================================================
# security-permissions.sh -- Permissions Auditor
# =============================================================================
# Category: security | Bird: falcon | Tier: 2 | Node-aware: yes
# =============================================================================

TOOL_NAME="security-permissions"
TOOL_VERSION="2.0.0"
TOOL_TIER=2
TOOL_BIRD="falcon"
TOOL_LOG="/opt/binary/logs/tools/${TOOL_NAME}.log"

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [[ -f "${SCRIPT_DIR}/common.sh" ]]; then source "${SCRIPT_DIR}/common.sh"
elif [[ -f "${SCRIPT_DIR}/../common.sh" ]]; then source "${SCRIPT_DIR}/../common.sh"
else echo "FATAL: common.sh not found" >&2; exit 1; fi

export BIRD_ID="${TOOL_BIRD}"
CHANGES_MADE=""
_change() { CHANGES_MADE="${CHANGES_MADE}  ${1}\n"; echo "  [CHANGED] ${1}"; }

# Permission map: only paths that exist on this node get checked
declare -A PERM_MAP=(
    [/opt/binary]="root:root:755"
    [/opt/binary/tools]="root:root:755"
    [/opt/binary/keys]="root:root:700"
    [/opt/binary/logs]="root:root:755"
    [/opt/binary/data]="root:root:755"
    [/etc/binary]="root:root:755"
    [/opt/binary/web]="root:root:755"
    [/opt/binary/backups]="root:root:700"
)

_do_send_impl() {
    local fix=0
    [[ "${1:-}" == "--fix" ]] && fix=1
    CHANGES_MADE=""

    echo "=== Permissions Check: ${NODE_ID} ==="
    local issues=0
    for path in "${!PERM_MAP[@]}"; do
        [ ! -e "$path" ] && continue
        local spec="${PERM_MAP[$path]}"
        local eo eg em ao ag am
        eo=$(echo "$spec" | cut -d: -f1)
        eg=$(echo "$spec" | cut -d: -f2)
        em=$(echo "$spec" | cut -d: -f3)
        ao=$(stat -c '%U' "$path" 2>/dev/null)
        ag=$(stat -c '%G' "$path" 2>/dev/null)
        am=$(stat -c '%a' "$path" 2>/dev/null)

        if [ "$ao" != "$eo" ] || [ "$ag" != "$eg" ] || [ "$am" != "$em" ]; then
            echo "  [WRONG] ${path}: ${ao}:${ag}:${am} (expected ${eo}:${eg}:${em})"
            issues=$((issues + 1))
            if [ "$fix" -eq 1 ]; then
                chown "${eo}:${eg}" "$path" 2>/dev/null
                chmod "$em" "$path" 2>/dev/null
                _change "fixed: ${path} -> ${eo}:${eg}:${em}"
            fi
        else
            echo "  [OK] ${path}"
        fi
    done

    echo ""
    echo "  Issues: ${issues}"
    [ -n "$CHANGES_MADE" ] && echo "" && echo "Changes:" && echo -e "$CHANGES_MADE"
    tool_audit "send" "issues=${issues} fix=${fix} node=${NODE_ID}"
    return $issues
}

_do_status_impl() {
    local issues=0
    for path in "${!PERM_MAP[@]}"; do
        [ ! -e "$path" ] && continue
        local spec="${PERM_MAP[$path]}"
        local eo eg em ao ag am
        eo=$(echo "$spec" | cut -d: -f1); eg=$(echo "$spec" | cut -d: -f2); em=$(echo "$spec" | cut -d: -f3)
        ao=$(stat -c '%U' "$path" 2>/dev/null); ag=$(stat -c '%G' "$path" 2>/dev/null); am=$(stat -c '%a' "$path" 2>/dev/null)
        [ "$ao" != "$eo" ] || [ "$ag" != "$eg" ] || [ "$am" != "$em" ] && issues=$((issues + 1))
    done
    local status="healthy"; [ "$issues" -gt 0 ] && status="degraded"
    echo "{\"tool\":\"${TOOL_NAME}\",\"node\":\"${NODE_ID}\",\"status\":\"${status}\",\"issues\":${issues},\"checked_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
}

_do_digest_impl() { echo "=== Permissions Report: ${NODE_ID} ==="; _do_send_impl; }
_do_flush_impl() { [ -f "$TOOL_LOG" ] && truncate -s 0 "$TOOL_LOG" && echo "  [CHANGED] Log truncated"; }

tool_help() {
    echo "${TOOL_NAME} v${TOOL_VERSION} -- Permissions Auditor"
    echo "  --send [--fix]   Audit permissions (--fix corrects them)"
    echo "  --status         JSON pill"
    echo "  --digest         Full report"
    echo "  --flush          Truncate log"
}

do_send()   { tool_wrap "send"   _do_send_impl "$@"; }
do_status() { tool_wrap "status" _do_status_impl "$@"; }
do_digest() { tool_wrap "digest" _do_digest_impl "$@"; }
do_flush()  { tool_wrap "flush"  _do_flush_impl "$@"; }

tool_init; trap 'tool_complete' EXIT
case "${1:---help}" in
    --send) shift; do_send "$@" ;; --status) shift; do_status "$@" ;;
    --digest) shift; do_digest "$@" ;; --flush) shift; do_flush "$@" ;;
    --help) tool_help ;; *) handle_standard_command "$@" ;;
esac
