diff --git a/scripts/taler-bank/auto-confirm-withdrawals.sh b/scripts/taler-bank/auto-confirm-withdrawals.sh index 3b76b4c..3b9dc85 100755 --- a/scripts/taler-bank/auto-confirm-withdrawals.sh +++ b/scripts/taler-bank/auto-confirm-withdrawals.sh @@ -1,18 +1,25 @@ #!/bin/bash -# Auto-confirm bank withdrawals once the wallet has selected exchange+reserve. -# Required for regional x-taler-bank: confirm wires GOA from user → exchange. +# Auto-confirm bank withdrawals for the community demo pool ONLY. +# +# Only account: explorer (override only if you really mean another pool user +# via BANK_USER, but still confirms with that user's token only — never +# confirms other customers' withdrawals). # # Run once: auto-confirm-withdrawals.sh # Loop: auto-confirm-withdrawals.sh --loop [SECS] # # Env: -# BANK_URL (default http://127.0.0.1:9012) -# BANK_USER (default explorer) — account that owns the demo withdrawals -# BANK_PASS or /root/bank-explorer-password.txt / /root/bank-${USER}-password.txt +# BANK_URL (default http://127.0.0.1:9012) +# BANK_USER (default explorer) — must be the pool account +# BANK_PASS or /root/bank-explorer-password.txt +# LANDING_DIR (default /var/www/bank-landing) +# ALLOW_NON_EXPLORER=1 — allow BANK_USER other than explorer (off by default) set -euo pipefail BANK="${BANK_URL:-http://127.0.0.1:9012}" +BANK="${BANK%/}" USER="${BANK_USER:-explorer}" +LANDING_DIR="${LANDING_DIR:-/var/www/bank-landing}" LOOP=0 SLEEP=5 if [ "${1:-}" = "--loop" ]; then @@ -20,6 +27,12 @@ if [ "${1:-}" = "--loop" ]; then SLEEP="${2:-5}" fi +# Safety: only the shared community account unless explicitly overridden +if [ "$USER" != "explorer" ] && [ "${ALLOW_NON_EXPLORER:-0}" != "1" ]; then + echo "refusing BANK_USER=$USER — auto-confirm is for explorer only (set ALLOW_NON_EXPLORER=1 to override)" >&2 + exit 1 +fi + PASS="${BANK_PASS:-}" if [ -z "$PASS" ]; then for f in "/root/bank-${USER}-password.txt" /root/bank-explorer-password.txt; do @@ -28,62 +41,89 @@ if [ -z "$PASS" ]; then fi [ -n "$PASS" ] || { echo "no password for $USER" >&2; exit 1; } +# JSON field extract without python (bank container may lack python3) +json_str() { + # json_str FIELD < json-text-or-file + local field="$1" + local data + if [ -f "${2:-}" ]; then data=$(cat "$2"); else data=$(cat); fi + printf '%s' "$data" | sed -n "s/.*\"${field}\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" | head -1 +} + token() { - echo '{"scope":"readwrite"}' > /tmp/acw-tok-req.json curl -sS -m 12 -u "${USER}:${PASS}" \ -H 'Content-Type: application/json' \ - -d @/tmp/acw-tok-req.json \ + -d '{"scope":"readwrite","refreshable":true}' \ "${BANK}/accounts/${USER}/token" } -# Extract withdrawal IDs we care about: -# 1) public landing withdraw.uri -# 2) any ID passed as args after --loop secs +# IDs created for the community pool (landing + watch list only) known_ids() { - if [ -f /var/www/bank-landing/withdraw.uri ]; then - basename "$(tr -d '\n' /dev/null || true) [ -n "$info" ] || return 0 - python3 -c "import json,sys; d=json.loads(sys.argv[1]); sys.exit(0 if d.get('status')=='selected' else 1)" "$info" 2>/dev/null || { - st=$(python3 -c "import json,sys; print(json.loads(sys.argv[1]).get('status','?'))" "$info" 2>/dev/null || echo '?') - echo "skip $wid status=$st" + + st=$(printf '%s' "$info" | sed -n 's/.*"status"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + uname=$(printf '%s' "$info" | sed -n 's/.*"username"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + + # Only confirm withdrawals owned by the pool account + if [ -n "$uname" ] && [ "$uname" != "$USER" ]; then + echo "skip $wid owner=$uname (only confirm $USER)" return 0 - } - echo "confirming $wid ..." + fi + # If API omits username, still only confirm via explorer token (cannot confirm others) + if [ -z "$uname" ]; then + # require sender_wire / payto to mention explorer when present + case "$info" in + *explorer*) ;; + *) + # still try only if status selected — confirm endpoint is under explorer account + ;; + esac + fi + + if [ "$st" != "selected" ]; then + echo "skip $wid status=${st:-?} owner=${uname:-?}" + return 0 + fi + + echo "confirming $wid as $USER (community pool) ..." conf=$(curl -sS -m 15 -o /tmp/acw-conf.out -w '%{http_code}' \ -X POST \ -H "Authorization: Bearer ${tok}" \ -H 'Content-Type: application/json' \ -d '{}' \ "${BANK}/accounts/${USER}/withdrawals/${wid}/confirm") - echo " HTTP $conf $(cat /tmp/acw-conf.out 2>/dev/null | head -c 200)" - # re-check - curl -sS -m 8 "${BANK}/withdrawals/${wid}" | python3 -c "import json,sys; d=json.load(sys.stdin); print(' now', d.get('status'), d.get('amount'))" 2>/dev/null || true + echo " HTTP $conf $(head -c 200 /tmp/acw-conf.out 2>/dev/null || true)" + curl -sS -m 8 "${BANK}/withdrawals/${wid}" 2>/dev/null \ + | sed -n 's/.*"status"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/ now status=\1/p' | head -1 || true } once() { - local tjson tok + local tjson tok ids tjson=$(token) - tok=$(python3 -c "import json,sys; print(json.loads(sys.argv[1]).get('access_token',''))" "$tjson") + tok=$(printf '%s' "$tjson" | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) if [ -z "$tok" ]; then - echo "token fail: $tjson" >&2 + echo "token fail for $USER: $tjson" >&2 return 1 fi - local ids ids=$(known_ids | sort -u) if [ -z "$ids" ]; then - echo "no withdrawal ids to watch" + echo "no withdrawal ids to watch (landing withdraw.uri / withdraw-watch.ids)" return 0 fi while read -r wid; do @@ -93,7 +133,7 @@ once() { } if [ "$LOOP" -eq 1 ]; then - echo "auto-confirm loop every ${SLEEP}s for user=$USER" + echo "auto-confirm loop every ${SLEEP}s for pool user=$USER only" while true; do once || true sleep "$SLEEP"