diff --git a/secrets b/secrets index a2f5209..b10f956 100755 --- a/secrets +++ b/secrets @@ -150,6 +150,29 @@ _load_recipients() { fi } +# Emit "\t" for each recipient in recipients.txt. is the most +# recent preceding "# " comment, or empty. Read-only; no validation +# (callers that need rails call _load_recipients separately). +_recipients_dump() { + [ -e "$RECIPIENTS_FILE" ] || return 0 + local line trimmed name="" + while IFS= read -r line || [ -n "$line" ]; do + trimmed="${line#"${line%%[![:space:]]*}"}" + trimmed="${trimmed%"${trimmed##*[![:space:]]}"}" + [ -z "$trimmed" ] && continue + case "$trimmed" in + '#'*) + name="${trimmed#\#}" + name="${name#"${name%%[![:space:]]*}"}" + ;; + *) + printf '%s\t%s\n' "$trimmed" "$name" + name="" + ;; + esac + done < "$RECIPIENTS_FILE" +} + derive_project_name() { local explicit="${1:-}" if [ -n "$explicit" ]; then @@ -1954,6 +1977,33 @@ cmd_run() { exit "$rc" } +cmd_recipients() { + resolve_store + local sub="${1:-list}" + [ $# -gt 0 ] && shift + case "$sub" in + list) _recipients_list ;; + *) die "Unknown recipients subcommand: '$sub'. Usage: secrets recipients [list]" ;; + esac +} + +_recipients_list() { + check_initialized + if [ ! -e "$RECIPIENTS_FILE" ]; then + check_key + echo "recipients: single-key (no $RECIPIENTS_FILE_NAME)" + echo " $(get_pubkey)" + return 0 + fi + _load_recipients # validates the file (dies on bad key / symlink) + local count=0 k n + while IFS=$'\t' read -r k n; do count=$((count + 1)); done < <(_recipients_dump) + echo "recipients: $count (from $RECIPIENTS_FILE_NAME)" + while IFS=$'\t' read -r k n; do + if [ -n "$n" ]; then echo " $k ($n)"; else echo " $k"; fi + done < <(_recipients_dump) +} + cmd_which() { resolve_store echo "store: $SECRETS_DIR" @@ -2564,6 +2614,7 @@ case "${1:-help}" in rekey) cmd_rekey ;; verify) shift; cmd_verify "$@" ;; migrate) shift; cmd_migrate "$@" ;; + recipients) shift; cmd_recipients "$@" ;; which|where|status) cmd_which ;; help|--help|-h) cmd_help ;; *) die "Unknown command: $1. Run 'secrets help' for usage." ;; diff --git a/test/recipients.bats b/test/recipients.bats index a8ae7c6..87e5491 100644 --- a/test/recipients.bats +++ b/test/recipients.bats @@ -55,3 +55,24 @@ make_second_identity() { [ "$status" -ne 0 ] [[ "$output" == *"symlink"* ]] || false } + +@test "recipients list on a legacy store shows the single derived key" { + init_with_remote + run "$SECRETS_BIN" recipients list + [ "$status" -eq 0 ] + [[ "$output" == *"single-key"* ]] || false + STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt") + [[ "$output" == *"$STORE_PUB"* ]] || false +} + +@test "recipients list shows names and keys from recipients.txt" { + init_with_remote + make_second_identity + STORE_PUB=$(age-keygen -y "$SECRETS_DIR/key.txt") + printf '# alice\n%s\n# bob\n%s\n' "$STORE_PUB" "$BOB_PUB" > "$SECRETS_DIR/recipients.txt" + run "$SECRETS_BIN" recipients list + [ "$status" -eq 0 ] + [[ "$output" == *"recipients: 2"* ]] || false + [[ "$output" == *"alice"* ]] || false + [[ "$output" == *"bob"* ]] || false +}