pashage

Yet Another Opinionated Re-engineering of the Unix Password Store
git clone https://git.instinctive.eu/pashage.git
Log | Files | Refs | README | LICENSE

commit cefe552791b1e9ebb41f3836cd491490cdb04a0a
parent f562e3cda4f5407ef8fa8f969cad7cc1a92f3f89
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date:   Sun,  3 Nov 2024 17:07:27 +0000

Re-encryption in copy and move commands is redesigned
Diffstat:
Mspec/action_spec.sh | 59++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mspec/internal_spec.sh | 9+++++++++
Msrc/pashage.sh | 95+++++++++++++++++++++++++++----------------------------------------------------
3 files changed, 89 insertions(+), 74 deletions(-)

diff --git a/spec/action_spec.sh b/spec/action_spec.sh @@ -41,7 +41,7 @@ Describe 'Action Functions' } basename() { @basename "$@"; } - diff() { @diff "$@"; } + cat() { @cat "$@"; } dirname() { @dirname "$@"; } mkdir() { mocklog mkdir "$@"; } @@ -202,7 +202,13 @@ Describe 'Action Functions' result() { %text:expand #|$ scm_begin - #|$ scm_mv sub/ subdir/sub/ + #|$ mkdir -p -- ${PREFIX}/subdir/sub + #|$ scm_mv sub/.age-recipients subdir/sub/.age-recipients + #|$ mkdir -p -- ${PREFIX}/subdir/sub/bare + #|$ scm_mv sub/bare/deep.age subdir/sub/bare/deep.age + #|$ mkdir -p -- ${PREFIX}/subdir/sub/bare/sub + #|$ scm_mv sub/bare/sub/deepest.age subdir/sub/bare/sub/deepest.age + #|$ scm_mv sub/secret.age subdir/sub/secret.age #|$ scm_commit Move sub/ to subdir/sub/ } When call do_copy_move sub subdir/ @@ -211,7 +217,46 @@ Describe 'Action Functions' The error should equal "$(result)" End - It 'recursively re-enecrypts a directory' + It 'recursively moves files to a directory with the same identity' + result() { + %text:expand + #|$ scm_begin + #|$ mkdir -p -- ${PREFIX}/subdir/new-bare + #|$ scm_mv sub/bare/deep.age subdir/new-bare/deep.age + #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/sub + #|$ scm_mv sub/bare/sub/deepest.age subdir/new-bare/sub/deepest.age + #|$ scm_commit Move sub/bare/ to subdir/new-bare/ + } + When call do_copy_move sub/bare subdir/new-bare + The status should be success + The output should be blank + The error should equal "$(result)" + End + + It 'recursively re-encrypts a directory' + result() { + %text:expand + #|$ scm_begin + #|$ mkdir -p -- ${PREFIX}/new-bare + #|$ do_decrypt ${PREFIX}/sub/bare/deep.age + #|$ do_encrypt new-bare/deep.age + #|$ scm_rm sub/bare/deep.age + #|$ scm_add new-bare/deep.age + #|$ mkdir -p -- ${PREFIX}/new-bare/sub + #|$ do_decrypt ${PREFIX}/sub/bare/sub/deepest.age + #|$ do_encrypt new-bare/sub/deepest.age + #|$ scm_rm sub/bare/sub/deepest.age + #|$ scm_add new-bare/sub/deepest.age + #|$ scm_commit Move sub/bare/ to new-bare/ + } + When call do_copy_move sub/bare new-bare + The status should be success + The output should be blank + The error should equal "$(result)" + End + + It 'recursively re-encrypts a directory with the same identity when forced' + DECISION=force result() { %text:expand #|$ scm_begin @@ -297,14 +342,6 @@ Describe 'Action Functions' The status should equal 1 End - It 'checks internal consistency of DECISION' - DECISION=garbage - When run do_copy_move root subdir - The output should be blank - The error should equal 'Unexpected DECISION value "garbage"' - The status should equal 1 - End - # Unreachable branches in do_copy_move_file, defensively implemented It 'defensively avois re-encrypting' DECISION=keep diff --git a/spec/internal_spec.sh b/spec/internal_spec.sh @@ -158,6 +158,7 @@ Describe 'Internal Helper Functions' echo "Toplevel recipient" >"${PREFIX}/.age-recipients" echo "Subdir recipient" >"${PREFIX}/subdir/.age-recipients" } + cat() { @cat "$@"; } cleanup() { @rm -rf "${PREFIX}"; } BeforeEach 'setup' @@ -168,6 +169,7 @@ Describe 'Internal Helper Functions' The status should be success The variable LOCAL_RECIPIENT_FILE should equal \ "${PREFIX}/.age-recipients" + The variable LOCAL_RECIPIENTS should equal 'Toplevel recipient' End It 'returns root from unmarked subdirectory' @@ -175,6 +177,7 @@ Describe 'Internal Helper Functions' The status should be success The variable LOCAL_RECIPIENT_FILE should equal \ "${PREFIX}/.age-recipients" + The variable LOCAL_RECIPIENTS should equal 'Toplevel recipient' End It 'returns subdirectory from itself' @@ -182,6 +185,7 @@ Describe 'Internal Helper Functions' The status should be success The variable LOCAL_RECIPIENT_FILE should equal \ "${PREFIX}/subdir/.age-recipients" + The variable LOCAL_RECIPIENTS should equal 'Subdir recipient' End It 'returns subdirectory from sub-subdirectory' @@ -189,6 +193,7 @@ Describe 'Internal Helper Functions' The status should be success The variable LOCAL_RECIPIENT_FILE should equal \ "${PREFIX}/subdir/.age-recipients" + The variable LOCAL_RECIPIENTS should equal 'Subdir recipient' End setup() { @@ -201,12 +206,14 @@ Describe 'Internal Helper Functions' When call set_LOCAL_RECIPIENT_FILE foo The status should be success The variable LOCAL_RECIPIENT_FILE should equal '' + The variable LOCAL_RECIPIENTS should equal '' End It 'returns nothing from unmarked subdirectory below empty root' When call set_LOCAL_RECIPIENT_FILE special/foo The status should be success The variable LOCAL_RECIPIENT_FILE should equal '' + The variable LOCAL_RECIPIENTS should equal '' End It 'returns subdirectory from itself even under empty root' @@ -214,6 +221,7 @@ Describe 'Internal Helper Functions' The status should be success The variable LOCAL_RECIPIENT_FILE should equal \ "${PREFIX}/subdir/.age-recipients" + The variable LOCAL_RECIPIENTS should equal 'Subdir recipient' End It 'returns subdirectory from sub-subdirectory even under empty root' @@ -221,6 +229,7 @@ Describe 'Internal Helper Functions' The status should be success The variable LOCAL_RECIPIENT_FILE should equal \ "${PREFIX}/subdir/.age-recipients" + The variable LOCAL_RECIPIENTS should equal 'Subdir recipient' End End diff --git a/src/pashage.sh b/src/pashage.sh @@ -89,10 +89,12 @@ set_LOCAL_RECIPIENT_FILE() { if ! [ -f "${PREFIX}${LOCAL_RECIPIENT_FILE}/.age-recipients" ]; then LOCAL_RECIPIENT_FILE= + LOCAL_RECIPIENTS= return 0 fi LOCAL_RECIPIENT_FILE="${PREFIX}${LOCAL_RECIPIENT_FILE}/.age-recipients" + LOCAL_RECIPIENTS="$(cat "${LOCAL_RECIPIENT_FILE}")" } # Count how many characters are in the first argument @@ -273,51 +275,10 @@ do_copy_move() { LOCAL_ACTION=do_copy_move_file fi - case "${DECISION}" in - force|interactive) - ANSWER=y - ;; - keep) - ANSWER=n - ;; - default) - if [ "${SRC}" = "${SRC%/}/" ]; then - # Handled in do_copy_move_dir - ANSWER=y - else - set_LOCAL_RECIPIENT_FILE "${SRC}" - SRC_FILE="${LOCAL_RECIPIENT_FILE}" - set_LOCAL_RECIPIENT_FILE "${DEST}" - DST_FILE="${LOCAL_RECIPIENT_FILE}" - - if [ "${SRC_FILE}" = "${DST_FILE}" ]; then - ANSWER=n - elif [ -n "${SRC_FILE}" ] \ - && [ -n "${DST_FILE}" ] \ - && diff "${SRC_FILE}" "${DST_FILE}" >/dev/null 2>&1 - then - ANSWER=n - else - ANSWER=y - fi - - unset DST_FILE - unset SRC_FILE - fi - ;; - *) - die "Unexpected DECISION value \"${DECISION}\"" - ;; - esac - scm_begin SCM_COMMIT_MSG="${ACTION} ${SRC} to ${DEST}" - if [ "${ANSWER}" = y ]; then - "${LOCAL_ACTION}" "${SRC}" "${DEST}" - else - "${SCM_ACTION}" "${SRC}" "${DEST}" - fi + "${LOCAL_ACTION}" "${SRC}" "${DEST}" scm_commit "${SCM_COMMIT_MSG}" @@ -337,29 +298,22 @@ do_copy_move_dir() { [ "$2" = "${2%/}/" ] || [ -z "$2" ] || die 'Internal error' [ -d "${PREFIX}/$1" ] || die 'Internal error' - if [ -e "${PREFIX}/$1.age-recipients" ] \ - && { [ "${DECISION}" = keep ] || [ "${DECISION}" = default ]; } - then - # Recipiends are transported too, no need to reencrypt - "${SCM_ACTION}" "$1" "$2" - else - [ -d "${PREFIX}/$2" ] || mkdir -p -- "${PREFIX}/${2%/}" + [ -d "${PREFIX}/$2" ] || mkdir -p -- "${PREFIX}/${2%/}" - for ARG in "${PREFIX}/$1".* "${PREFIX}/$1"*; do - SRC="${ARG#"${PREFIX}/"}" - DEST="$2$(basename "${ARG}")" + for ARG in "${PREFIX}/$1".* "${PREFIX}/$1"*; do + SRC="${ARG#"${PREFIX}/"}" + DEST="$2$(basename "${ARG}")" - if [ -f "${ARG}" ]; then - do_copy_move_file "${SRC}" "${DEST}" - elif [ -d "${ARG}" ] && [ "${ARG}" = "${ARG%/.*}" ] - then - do_copy_move_dir "${SRC}/" "${DEST}/" - fi - done + if [ -f "${ARG}" ]; then + do_copy_move_file "${SRC}" "${DEST}" + elif [ -d "${ARG}" ] && [ "${ARG}" = "${ARG%/.*}" ] + then + do_copy_move_dir "${SRC}/" "${DEST}/" + fi + done - unset ARG - rmdir -p -- "${PREFIX}/$1" 2>/dev/null || true - fi + unset ARG + rmdir -p -- "${PREFIX}/$1" 2>/dev/null || true } # Copy or move a secret file (depending on ${ACTION}) @@ -388,7 +342,22 @@ do_copy_move_file() { interactive) yesno "Reencrypt ${1%.age} into ${2%.age}?" ;; - default|force) + default) + set_LOCAL_RECIPIENT_FILE "$1" + SRC_RCPT="${LOCAL_RECIPIENTS}" + set_LOCAL_RECIPIENT_FILE "$2" + DST_RCPT="${LOCAL_RECIPIENTS}" + + if [ "${SRC_RCPT}" = "${DST_RCPT}" ]; then + ANSWER=n + else + ANSWER=y + fi + + unset DST_RCPT + unset SRC_RCPT + ;; + force) ANSWER=y ;; *)