small pixel drawing of a pufferfish pa

enforce absolute $PA_DIR + update dir handling (#51)

* enforce absolute $PA_DIR + update dir handling

* test: use absolute dir (/tmp/pa-test)

* globby

Co-authored-by: arĉi <arcxi@dismail.de>

* data directory

* unbreak xdg basedir spec

Co-authored-by: arĉi <arcxi@dismail.de>

* simplify mkdir -p

Co-authored-by: arĉi <arcxi@dismail.de>

* s/basedir/PA_DIR/g

* small readme fixes

* adjust contribs

---------

Co-authored-by: arĉi <arcxi@dismail.de>
j3s j3s@c3f.net
Tue, 11 Feb 2025 21:36:53 -0600
commit

2b95fab2cd983da0c4e244ce71443fc2dff45ba6

parent

064dcdfb848d23bbaa156ebb5c400d5328578b13

6 files changed, 36 insertions(+), 43 deletions(-)

jump to
M READMEREADME

@@ -32,7 +32,7 @@ [l]ist - List all entries.

[s]how [name] - Show password for an entry. env vars: - password dir: export PA_DIR=~/.local/share/pa/passwords + data directory: export PA_DIR=~/.local/share/pa/passwords password length: export PA_LENGTH=50 password pattern: export PA_PATTERN=A-Za-z0-9-_ disable tracking: export PA_NOGIT=

@@ -40,8 +40,8 @@

command examples $ pa add test - Generate a password? [y/N]: y - Saved 'test' to the store. + generate a password? [y/N]: y + saved 'test' to the store. $ pa list test

@@ -53,7 +53,7 @@ $ pa edit test

<opens $EDITOR or vi> $ pa del test - Delete password 'test'? [y/N]: y + delete password 'test'? [y/N]: y $ pa git log --oneline bbe85dc (HEAD -> main) delete 'test'
M contrib/pa-passcontrib/pa-pass

@@ -6,8 +6,7 @@ # password dir of pass: export PASSWORD_STORE_DIR=~/.password-store

# password dir of pa: export PA_DIR=~/.local/share/pa/passwords : "${PASSWORD_STORE_DIR:=$HOME/.password-store}" -basedir=${XDG_DATA_HOME:=$HOME/.local/share}/pa -: "${PA_DIR:=$basedir/passwords}" +: "${PA_DIR:=${XDG_DATA_HOME:-$HOME/.local/share}/pa}" # Create pa store if it doesn't exist. pa l >/dev/null || exit

@@ -16,12 +15,13 @@ age=$(command -v age || command -v rage)

find "$PASSWORD_STORE_DIR" -name '*.gpg' | while read -r passfile; do name=$(printf %s "${passfile#"$PASSWORD_STORE_DIR/"}" | sed 's/\.gpg$//') - mkdir -p "$PA_DIR/$(dirname "./$name")" + mkdir -p "$PA_DIR/passwords/$(dirname "./$name")" gpg2 -d "$passfile" | - $age -R "$basedir/recipients" -o "$PA_DIR/$name.age" && + $age -R "$PA_DIR/recipients" -o "$PA_DIR/passwords/$name.age" && printf '%s\n' "saved '$name' to the store." done if [ -z "${PA_NOGIT+x}" ] && command -v git >/dev/null 2>&1; then - git -C "$PA_DIR" add . && git -C "$PA_DIR" commit -m "migrate from pass" + git -C "$PA_DIR/passwords" add . + git -C "$PA_DIR/passwords" commit -m "migrate from pass" fi
M contrib/pa-rekeycontrib/pa-rekey

@@ -19,13 +19,12 @@

# Restrict permissions of any new files to only the current user. umask 077 -basedir=${XDG_DATA_HOME:=$HOME/.local/share}/pa -: "${PA_DIR:=$basedir/passwords}" +: "${PA_DIR:=${XDG_DATA_HOME:-$HOME/.local/share}/pa}" -realstore=$(realpath "$PA_DIR") || +realstore=$(realpath "$PA_DIR/passwords") || die "couldn't get path to password directory" -tmpdir=$basedir/tmp +tmpdir=$PA_DIR/tmp mkdir "$tmpdir" || die "couldn't create temporary directory"

@@ -57,8 +56,8 @@ rm -rf "$realstore" ||

die "couldn't remove password directory" mv "$tmpdir/passwords" "$realstore" -mv "$tmpdir/identities" "$(realpath "$basedir/identities")" -mv "$tmpdir/recipients" "$(realpath "$basedir/recipients")" +mv "$tmpdir/identities" "$(realpath "$PA_DIR/identities")" +mv "$tmpdir/recipients" "$(realpath "$PA_DIR/recipients")" rmdir "$tmpdir" # Recreate git repository if needed.
M contrib/pa-urncontrib/pa-urn

@@ -36,10 +36,9 @@

age=$(command -v age || command -v rage) || die "age not found, install per https://age-encryption.org" -basedir=${XDG_DATA_HOME:=$HOME/.local/share}/pa -: "${PA_DIR:=$basedir/passwords}" +: "${PA_DIR:=${XDG_DATA_HOME:-$HOME/.local/share}/pa}" -dir=$(realpath "$PA_DIR") || +dir=$(realpath "$PA_DIR/passwords") || die "couldn't get path to password directory" name=$(basename "$dir")

@@ -56,15 +55,15 @@ die "couldn't change to parent of password directory"

case $1 in c*) - { create_tar "$name" | $age -R "$basedir/recipients" -o "$urn"; } && + { create_tar "$name" | $age -R "$PA_DIR/recipients" -o "$urn"; } && printf '%s\n' "store has been archived into $urn" ;; o*) [ -f "$urn" ] || die "file '$urn' doesn't exist" - { $age --decrypt -i "$basedir/identities" "$urn" | extract_tar; } && - printf '%s\n' "file has been extracted into $PA_DIR" + { $age --decrypt -i "$PA_DIR/identities" "$urn" | extract_tar; } && + printf '%s\n' "file has been extracted into $PA_DIR/passwords" ;; *) usage ;; esac
M papa

@@ -188,7 +188,7 @@ [l]ist - List all entries.

[s]how [name] - Show password for an entry. env vars: - password dir: export PA_DIR=~/.local/share/pa/passwords + data directory: export PA_DIR=~/.local/share/pa password length: export PA_LENGTH=50 password pattern: export PA_PATTERN=A-Za-z0-9-_ disable tracking: export PA_NOGIT=

@@ -203,20 +203,19 @@

age_keygen=$(command -v age-keygen || command -v rage-keygen) || die "age-keygen not found, install per https://age-encryption.org" - basedir=${XDG_DATA_HOME:=$HOME/.local/share}/pa - : "${PA_DIR:=$basedir/passwords}" - identities_file=$basedir/identities - recipients_file=$basedir/recipients + : "${PA_DIR:=${XDG_DATA_HOME:-$HOME/.local/share}/pa}" + + glob "$PA_DIR" '/*' || + die "PA_DIR must be an absolute path (got '$PA_DIR')" + + identities_file=$PA_DIR/identities + recipients_file=$PA_DIR/recipients - mkdir -p "$basedir" "$PA_DIR" || + mkdir -p "$PA_DIR/passwords" || die "couldn't create pa directories" - cd "$PA_DIR" || + cd "$PA_DIR/passwords" || die "couldn't change to password directory" - - # Move any passwords hanging out in the old dir - # for backwards-compat reasons - mv "$basedir"/*.age "$PA_DIR" 2>/dev/null # Ensure that globbing is disabled # to avoid insecurities with word-splitting.

@@ -272,11 +271,7 @@

glob "$command" '[ds]*' && [ ! -f "$name.age" ] && die "password '$name' doesn't exist" - # First, copy any existing identities files from the old - # storage location to the new one for backwards compat. - # Then, attempt key generation. [ -f "$identities_file" ] || - cp ~/.age/key.txt "$identities_file" 2>/dev/null || $age_keygen -o "$identities_file" 2>/dev/null [ -f "$recipients_file" ] ||
M testtest

@@ -8,14 +8,14 @@ printf "%s\n" "$*"

failures=$((failures + 1)) } -export PA_DIR=test-stuff/passwords +export PA_DIR=/tmp/pa-test # clean up previous run # (the previous state is left around # intentionally, in case the dev # wants to poke around) # that's why we don't clean up on exit -rm -rf test-stuff +rm -rf /tmp/pa-test # pa welcomes you ./pa | grep -q "a simple password manager" ||

@@ -25,13 +25,13 @@ # generate pa dirs/identityfile/recipientfile

./pa list # pa auto-generated files are correct -test -s test-stuff/identities || +test -s "$PA_DIR/identities" || fail "an identities file should exist" -test -s test-stuff/recipients || +test -s "$PA_DIR/recipients" || fail "a recipients file should exist" -test -d "$PA_DIR/.git" || +test -d "$PA_DIR/passwords/.git" || fail "git dir should exist" # TODO: ensure git author/email are set correctly, etc

@@ -44,7 +44,7 @@ generate a password? [y/N]: y

saved 'nested/password' to the store." || fail "pa add should say it stored nested/password" -test -s test-stuff/passwords/nested/password.age || +test -s "$PA_DIR/passwords/nested/password.age" || fail "pa add should create an encrypted password file" # pa list

@@ -56,7 +56,7 @@ test" ||

fail "pa list output should match example" # ensure git commits are working -git -C test-stuff/passwords log | grep -q "add 'nested/password'" || +git -C "$PA_DIR/passwords" log | grep -q "add 'nested/password'" || fail "git log should have line: add 'nested/password'" # print info & exit w/ correct status