Some checks failed
CI / changes (push) Successful in 7s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Has been cancelled
#71 (CRITICAL): backup.sh committed 100644 → git reset --hard on deploy re-asserts non-exec mode → raw-path cron fails Permission denied (last good dump 2026-05-27, no S3). Commit 100755 + chmod ops/*.sh in deploy.yml + size sanity-check (never prune good dumps for a truncated one) + keep-N retention + optional S3 (redacted /etc/default template). Modeled on the working backup-tradein-db.sh. #427: widen basic_auth username regex ^[a-z][a-z0-9_.-]{1,62}$ + literal-escape dotted names in grep probes. #429: replace list_users.sh false-positive grep with anchored awk over basic_auth block. #428: PILOT_ACCESS.md support email → pilot@gendsgn.ru. Closes #71 Closes #427 Closes #429 Closes #428
65 lines
2.6 KiB
Bash
65 lines
2.6 KiB
Bash
#!/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"
|