#!/usr/bin/env bash # list_users.sh — показать текущих пользователей (имена + comment, без хешей) # Usage: ./scripts/auth/list_users.sh set -euo pipefail # 3× dirname от $0 (.../scripts/auth/list_users.sh) → repo root. Bug fix 2026-05-26. SCRIPT_DIR="$(cd "$(dirname "$(realpath "$0")")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" SNIPPET="$REPO_ROOT/caddy/users.caddy.snippet" if [[ ! -f "$SNIPPET" ]]; then echo "ERROR: snippet not found at $SNIPPET" >&2 exit 1 fi echo "Users in $SNIPPET:" echo "" printf "%-12s %s\n" "USERNAME" "COMMENT" printf "%-12s %s\n" "--------" "-------" # Parse only the real user lines INSIDE the `basic_auth ... { ... }` block (#429). # The previous grep `^\s+[a-z][a-z0-9-]+\s+[A-Za-z0-9+/]+=*` also matched indented # base64-ish comment text and miscounted. Here we: # - enter the block on a line whose first token is `basic_auth` ending in `{`; # - leave it on the closing `}`; # - inside, skip blank and comment-only lines, require a username-shaped first # field (^[a-z][a-z0-9_.-]+$, matches add/remove_user.sh) AND a 2nd field # (the hash). Comment is whatever follows the first `#`. # A single pass prints rows and the total, so the count can't drift from the list. awk ' $1 == "basic_auth" && /\{[[:space:]]*$/ { in_block = 1; next } in_block && /^[[:space:]]*\}/ { in_block = 0; next } !in_block { next } { line = $0 sub(/^[[:space:]]+/, "", line) # strip leading indent if (line == "" || line ~ /^#/) next # blank / comment-only user = line sub(/[[:space:]].*$/, "", user) # first whitespace-delimited token if (user !~ /^[a-z][a-z0-9_.-]+$/) next # must look like a username rest = line sub(/^[^[:space:]]+[[:space:]]+/, "", rest) # drop username if (rest == "") next # no 2nd field -> not a user line hash = rest sub(/[[:space:]].*$/, "", hash) # 2nd token = the hash # Real entry: 2nd token is a base64-encoded bcrypt hash (base64 charset, # long). This is what excludes indented prose like "note this ..." whose # 2nd word ("this") is short non-base64 text — the #429 false positive. if (hash !~ /^[A-Za-z0-9+\/=]{20,}$/) next comment = "" h = index(rest, "#") if (h > 0) { comment = substr(rest, h + 1) sub(/^[[:space:]]+/, "", comment) } printf "%-12s %s\n", user, comment count++ } END { print ""; printf "Total: %d user(s)\n", count } ' "$SNIPPET"