From b6130ac807501a63ccaee1f38fb6cafae6dd933d Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Mon, 14 Oct 2019 16:59:58 -0500 Subject: [PATCH 01/12] show secondary actions menu when item is chosen --- bwmenu | 86 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/bwmenu b/bwmenu index a9c342d..0d86cd8 100755 --- a/bwmenu +++ b/bwmenu @@ -20,7 +20,7 @@ AUTOTYPE_MODE= CLIPBOARD_MODE= # Specify what happens when pressing Enter on an item. -# Defaults to copy_password, can be changed to (auto_type all) or (auto_type password) +# Defaults to copy_password, can be changed to (auto_type all) or (auto_type password) or show_item_actions ENTER_CMD=copy_password # Keyboard shortcuts @@ -275,6 +275,10 @@ clipboard-set() { clipboard-${CLIPBOARD_MODE}-set } +clipboard-set-primary() { + clipboard-${CLIPBOARD_MODE}-set-primary +} + clipboard-get() { clipboard-${CLIPBOARD_MODE}-get } @@ -287,6 +291,10 @@ clipboard-xclip-set() { xclip -selection clipboard -r } +clipboard-xclip-set-primary() { + xclip -selection primary -r +} + clipboard-xclip-get() { xclip -selection clipboard -o } @@ -299,6 +307,10 @@ clipboard-xsel-set() { xsel --clipboard --input } +clipboard-xsel-set-primary() { + xsel --clipboard --input --primary +} + clipboard-xsel-get() { xsel --clipboard } @@ -311,6 +323,10 @@ clipboard-wayland-set() { wl-copy } +clipboard-wayland-set-primary() { + wl-copy --primary +} + clipboard-wayland-get() { wl-paste } @@ -319,6 +335,51 @@ clipboard-wayland-clear() { wl-copy --clear } +show_item_actions() { + if not_unique "$1"; then + ITEMS="$1" + show_full_items + else + actions="$(actions_for_item "$1")" + if selection="$(cut -d : -f 1 <<< "$actions" | select_action)"; then + selected_action="$(sed "$(( $selection + 1 ))q;d" <<< "$actions")" + eval "$(cut -d : -f 2- <<< "$selected_action")" + fi + fi +} + +actions_for_item() { + actions=() + username="" + password="" + while IFS=' ' read path label ; do + value="$(jq -re ".[0].$path" <<< "$1")" + if [ "$value" != "null" ]; then + actions+=("Copy ${label}:echo -n ${value@Q} | clipboard-set") + [ "$path" == "login.username" ] && username="$value" + [ "$path" == "login.password" ] && password="$value" + fi + done <<< "login.username username +login.password password +login.totp TOTP" + uri="$(jq -r 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" + if [ "$uri" != "null" ] ; then + actions+=("Open URI:xdg-open ${uri@Q}") + if [ -n "$password" ] && [ -n "$username" ] ; then + actions+=("Copy username and password, open URI:echo -n ${username@Q} | clipboard-set-primary \ + && echo -n ${password@Q} | clipboard-set \ + && xdg-open ${uri@Q}") + elif [ -n "$password" ] ; then + actions+=("Copy password, open URI:echo -n ${password@Q} | clipboard-set && xdg-open ${uri@Q}") + fi + fi + printf '%s\n' "${actions[@]}" +} + +select_action() { + rofi -dmenu -p "Action" "$1" -format i +} + # Copy the password # copy to clipboard and give the user feedback that the password is copied # $1: json array of items @@ -364,29 +425,6 @@ lock_vault() { keyctl purge user bw_session &>/dev/null } -# Show notification about the password being copied. -# $1: json item -show_copy_notification() { - local title - local body="" - local extra_options=() - - title="$(echo "$1" | jq -r '.name') copied" - - if [[ $SHOW_PASSWORD == "yes" ]]; then - pass=$(echo "$1" | jq -r '.login.password') - body="${pass:0:4}****" - fi - - if [[ $CLEAR -gt 0 ]]; then - body="$body
Will be cleared in ${CLEAR} seconds." - # Keep notification visible while the clipboard contents are active. - extra_options+=("-t" "$((CLEAR * 1000))") - fi - # not sure if icon will be present everywhere, /usr/share/icons is default icon location - notify-send "$title" "$body" "${extra_options[@]}" -i /usr/share/icons/hicolor/64x64/apps/bitwarden.png -} - parse_cli_arguments() { # Use GNU getopt to parse command line arguments if ! ARGUMENTS=$(getopt -o c:C --long auto-lock:,clear:,no-clear,show-password,state-path:,help,version -- "$@"); then From f8c2137a17e21d6865c2694a762f7292ab58bd4a Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Mon, 14 Oct 2019 23:28:26 -0500 Subject: [PATCH 02/12] re-implement copy notifications --- bwmenu | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/bwmenu b/bwmenu index 0d86cd8..fb82ec1 100755 --- a/bwmenu +++ b/bwmenu @@ -343,7 +343,8 @@ show_item_actions() { actions="$(actions_for_item "$1")" if selection="$(cut -d : -f 1 <<< "$actions" | select_action)"; then selected_action="$(sed "$(( $selection + 1 ))q;d" <<< "$actions")" - eval "$(cut -d : -f 2- <<< "$selected_action")" + eval "$(cut -d : -f 3- <<< "$selected_action")" + show_copy_notification "$(cut -d : -f 2 <<< "$selected_action")" fi fi } @@ -355,7 +356,7 @@ actions_for_item() { while IFS=' ' read path label ; do value="$(jq -re ".[0].$path" <<< "$1")" if [ "$value" != "null" ]; then - actions+=("Copy ${label}:echo -n ${value@Q} | clipboard-set") + actions+=("Copy ${label}:${label^} copied to clipboard:echo -n ${value@Q} | clipboard-set") [ "$path" == "login.username" ] && username="$value" [ "$path" == "login.password" ] && password="$value" fi @@ -364,13 +365,14 @@ login.password password login.totp TOTP" uri="$(jq -r 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" if [ "$uri" != "null" ] ; then - actions+=("Open URI:xdg-open ${uri@Q}") + actions+=("Open URI::xdg-open ${uri@Q}") if [ -n "$password" ] && [ -n "$username" ] ; then - actions+=("Copy username and password, open URI:echo -n ${username@Q} | clipboard-set-primary \ - && echo -n ${password@Q} | clipboard-set \ - && xdg-open ${uri@Q}") + actions+=("Copy username and password, open URI:Username copied to primary selection
Password copied to clipboard: \ + echo -n ${username@Q} | clipboard-set-primary \ + && echo -n ${password@Q} | clipboard-set \ + && xdg-open ${uri@Q}") elif [ -n "$password" ] ; then - actions+=("Copy password, open URI:echo -n ${password@Q} | clipboard-set && xdg-open ${uri@Q}") + actions+=("Copy password, open URI:Password copied to clipboard:echo -n ${password@Q} | clipboard-set && xdg-open ${uri@Q}") fi fi printf '%s\n' "${actions[@]}" @@ -420,6 +422,11 @@ copy_totp() { fi } +show_copy_notification() { + # not sure if icon will be present everywhere, /usr/share/icons is default icon location + notify-send "$1" -i /usr/share/icons/hicolor/64x64/apps/bitwarden.png +} + # Lock the vault by purging the key used to store the session hash lock_vault() { keyctl purge user bw_session &>/dev/null From 758509d1b9fa993df506675288e99c6988d74f2b Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Thu, 17 Oct 2019 12:04:15 -0500 Subject: [PATCH 03/12] add actions to copy username & password, show notes --- bwmenu | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bwmenu b/bwmenu index fb82ec1..327f8e0 100755 --- a/bwmenu +++ b/bwmenu @@ -363,6 +363,11 @@ actions_for_item() { done <<< "login.username username login.password password login.totp TOTP" + if [ -n "$password" ] && [ -n "$username" ] ; then + actions+=("Copy username and password:Username copied to primary selection
Password copied to clipboard: \ + echo -n ${username@Q} | clipboard-set-primary \ + && echo -n ${password@Q} | clipboard-set") + fi uri="$(jq -r 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" if [ "$uri" != "null" ] ; then actions+=("Open URI::xdg-open ${uri@Q}") @@ -375,11 +380,14 @@ login.totp TOTP" actions+=("Copy password, open URI:Password copied to clipboard:echo -n ${password@Q} | clipboard-set && xdg-open ${uri@Q}") fi fi + if notes="$(jq -re '.[0].notes' <<< "$1")" ; then + actions+=("Display notes::exit_error 0 ${notes@Q}") + fi printf '%s\n' "${actions[@]}" } select_action() { - rofi -dmenu -p "Action" "$1" -format i + rofi -dmenu -p "Action" "$1" -format i -i -no-custom "${ROFI_OPTIONS[@]}" } # Copy the password From 59fa06f7e99d7008199f30c4ba04a1aaebecf46b Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Fri, 18 Oct 2019 20:46:33 -0500 Subject: [PATCH 04/12] include actions to copy custom fields --- bwmenu | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bwmenu b/bwmenu index 327f8e0..b832d32 100755 --- a/bwmenu +++ b/bwmenu @@ -380,6 +380,10 @@ login.totp TOTP" actions+=("Copy password, open URI:Password copied to clipboard:echo -n ${password@Q} | clipboard-set && xdg-open ${uri@Q}") fi fi + declare -a "custom_field_actions=($(jq -re '.[0].fields | map("Copy " + .name + ":" + .name + " copied to clipboard:clipboard-set " + (.value | @sh)) | @sh' <<< "$1"))" + for action in "${custom_field_actions[@]}" ; do + actions+=("$action") + done if notes="$(jq -re '.[0].notes' <<< "$1")" ; then actions+=("Display notes::exit_error 0 ${notes@Q}") fi From e6ed5e97deeb890e4c8faa541fc3d107b65f71f3 Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Fri, 18 Oct 2019 22:00:53 -0500 Subject: [PATCH 05/12] properly copy TOTP --- bwmenu | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/bwmenu b/bwmenu index b832d32..3544113 100755 --- a/bwmenu +++ b/bwmenu @@ -361,8 +361,7 @@ actions_for_item() { [ "$path" == "login.password" ] && password="$value" fi done <<< "login.username username -login.password password -login.totp TOTP" +login.password password" if [ -n "$password" ] && [ -n "$username" ] ; then actions+=("Copy username and password:Username copied to primary selection
Password copied to clipboard: \ echo -n ${username@Q} | clipboard-set-primary \ @@ -380,6 +379,9 @@ login.totp TOTP" actions+=("Copy password, open URI:Password copied to clipboard:echo -n ${password@Q} | clipboard-set && xdg-open ${uri@Q}") fi fi + if jq -e '.[0].login.totp' > /dev/null <<< "$1" ; then + actions+=("Copy TOTP:TOTP copied to clipboard:_copy_totp ${1@Q}") + fi declare -a "custom_field_actions=($(jq -re '.[0].fields | map("Copy " + .name + ":" + .name + " copied to clipboard:clipboard-set " + (.value | @sh)) | @sh' <<< "$1"))" for action in "${custom_field_actions[@]}" ; do actions+=("$action") @@ -416,6 +418,16 @@ copy_password() { fi } +_copy_totp() { + id=$(echo "$1" | jq -r ".[0].id") + + if ! totp=$(bw --session "$BW_HASH" get totp "$id"); then + exit_error 1 "$totp" + fi + + echo -n "$totp" | clipboard-set +} + # Copy the TOTP # $1: item array copy_totp() { @@ -423,13 +435,7 @@ copy_totp() { ITEMS="$item_array" show_full_items else - id=$(echo "$1" | jq -r ".[0].id") - - if ! totp=$(bw --session "$BW_HASH" get totp "$id"); then - exit_error 1 "$totp" - fi - - echo -n "$totp" | clipboard-set + _copy_totp "$1" notify-send "TOTP Copied" fi } From 96a6f5fbc18be6b850ed395355e87913d95edd03 Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Fri, 18 Oct 2019 22:39:21 -0500 Subject: [PATCH 06/12] tidy up a bit --- bwmenu | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/bwmenu b/bwmenu index 3544113..f75fbab 100755 --- a/bwmenu +++ b/bwmenu @@ -351,24 +351,18 @@ show_item_actions() { actions_for_item() { actions=() - username="" - password="" - while IFS=' ' read path label ; do - value="$(jq -re ".[0].$path" <<< "$1")" - if [ "$value" != "null" ]; then - actions+=("Copy ${label}:${label^} copied to clipboard:echo -n ${value@Q} | clipboard-set") - [ "$path" == "login.username" ] && username="$value" - [ "$path" == "login.password" ] && password="$value" - fi - done <<< "login.username username -login.password password" + if username="$(jq -re ".[0].login.username" <<< "$1")"; then + actions+=("Copy username:Username copied to clipboard:echo -n ${username@Q} | clipboard-set") + fi + if password="$(jq -re ".[0].login.password" <<< "$1")"; then + actions+=("Copy password:Password copied to clipboard:echo -n ${password@Q} | clipboard-set") + fi if [ -n "$password" ] && [ -n "$username" ] ; then actions+=("Copy username and password:Username copied to primary selection
Password copied to clipboard: \ echo -n ${username@Q} | clipboard-set-primary \ && echo -n ${password@Q} | clipboard-set") fi - uri="$(jq -r 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" - if [ "$uri" != "null" ] ; then + if uri="$(jq -re 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" ; then actions+=("Open URI::xdg-open ${uri@Q}") if [ -n "$password" ] && [ -n "$username" ] ; then actions+=("Copy username and password, open URI:Username copied to primary selection
Password copied to clipboard: \ From 068f7171617614eb4736e9284cecffc75e11b18c Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Wed, 23 Oct 2019 11:52:01 -0500 Subject: [PATCH 07/12] clear copied fields after configured timeout --- bwmenu | 86 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/bwmenu b/bwmenu index f75fbab..d7eecdf 100755 --- a/bwmenu +++ b/bwmenu @@ -283,10 +283,18 @@ clipboard-get() { clipboard-${CLIPBOARD_MODE}-get } +clipboard-get-primary() { + clipboard-${CLIPBOARD_MODE}-get-primary +} + clipboard-clear() { clipboard-${CLIPBOARD_MODE}-clear } +clipboard-clear-primary() { + clipboard-${CLIPBOARD_MODE}-clear-primary +} + clipboard-xclip-set() { xclip -selection clipboard -r } @@ -299,10 +307,18 @@ clipboard-xclip-get() { xclip -selection clipboard -o } +clipboard-xclip-get-primary() { + xclip -selection primary -o +} + clipboard-xclip-clear() { echo -n "" | xclip -selection clipboard -r } +clipboard-xclip-clear-primary() { + echo -n "" | xclip -selection primary -r +} + clipboard-xsel-set() { xsel --clipboard --input } @@ -315,10 +331,18 @@ clipboard-xsel-get() { xsel --clipboard } +clipboard-xsel-get-primary() { + xsel --primary +} + clipboard-xsel-clear() { xsel --clipboard --delete } +clipboard-xsel-clear-primary() { + xsel --primary --delete +} + clipboard-wayland-set() { wl-copy } @@ -331,10 +355,18 @@ clipboard-wayland-get() { wl-paste } +clipboard-wayland-get-primary() { + wl-paste --primary +} + clipboard-wayland-clear() { wl-copy --clear } +clipboard-wayland-clear-primary() { + wl-copy --clear --primary +} + show_item_actions() { if not_unique "$1"; then ITEMS="$1" @@ -352,31 +384,31 @@ show_item_actions() { actions_for_item() { actions=() if username="$(jq -re ".[0].login.username" <<< "$1")"; then - actions+=("Copy username:Username copied to clipboard:echo -n ${username@Q} | clipboard-set") + actions+=("Copy username:Username copied to clipboard:copy_and_clear clipboard ${username@Q}") fi if password="$(jq -re ".[0].login.password" <<< "$1")"; then - actions+=("Copy password:Password copied to clipboard:echo -n ${password@Q} | clipboard-set") + actions+=("Copy password:Password copied to clipboard:copy_and_clear clipboard ${password@Q}") fi if [ -n "$password" ] && [ -n "$username" ] ; then actions+=("Copy username and password:Username copied to primary selection
Password copied to clipboard: \ - echo -n ${username@Q} | clipboard-set-primary \ - && echo -n ${password@Q} | clipboard-set") + copy_and_clear primary ${username@Q} \ + && copy_and_clear clipboard ${password@Q}") fi if uri="$(jq -re 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" ; then actions+=("Open URI::xdg-open ${uri@Q}") if [ -n "$password" ] && [ -n "$username" ] ; then - actions+=("Copy username and password, open URI:Username copied to primary selection
Password copied to clipboard: \ - echo -n ${username@Q} | clipboard-set-primary \ - && echo -n ${password@Q} | clipboard-set \ + actions+=("Copy username and password, open URI:Username copied to clipboard
Password copied to primary selection: \ + copy_and_clear clipboard ${username@Q} \ + && copy_and_clear primary ${password@Q} \ && xdg-open ${uri@Q}") elif [ -n "$password" ] ; then - actions+=("Copy password, open URI:Password copied to clipboard:echo -n ${password@Q} | clipboard-set && xdg-open ${uri@Q}") + actions+=("Copy password, open URI:Password copied to clipboard:copy_and_clear ${password@Q} && xdg-open ${uri@Q}") fi fi if jq -e '.[0].login.totp' > /dev/null <<< "$1" ; then actions+=("Copy TOTP:TOTP copied to clipboard:_copy_totp ${1@Q}") fi - declare -a "custom_field_actions=($(jq -re '.[0].fields | map("Copy " + .name + ":" + .name + " copied to clipboard:clipboard-set " + (.value | @sh)) | @sh' <<< "$1"))" + declare -a "custom_field_actions=($(jq -re '.[0].fields | map("Copy " + .name + ":" + .name + " copied to clipboard:copy_and_clear " + (.value | @sh)) | @sh' <<< "$1"))" for action in "${custom_field_actions[@]}" ; do actions+=("$action") done @@ -401,14 +433,7 @@ copy_password() { pass="$(echo "$1" | jq -r '.[0].login.password')" show_copy_notification "$(echo "$1" | jq -r '.[0]')" - echo -n "$pass" | clipboard-set - - if [[ $CLEAR -gt 0 ]]; then - sleep "$CLEAR" - if [[ "$(clipboard-get)" == "$pass" ]]; then - clipboard-clear - fi - fi + copy_and_clear clipboard "$pass" fi } @@ -419,7 +444,7 @@ _copy_totp() { exit_error 1 "$totp" fi - echo -n "$totp" | clipboard-set + copy_and_clear clipboard "$totp" } # Copy the TOTP @@ -434,6 +459,31 @@ copy_totp() { fi } +copy_and_clear() { + if [ "$1" = "primary" ]; then + clipboard-set-primary <<< "$2" + if [[ $CLEAR -gt 0 ]]; then + ( + sleep "$CLEAR" + if [[ "$(clipboard-get-primary)" == "$2" ]]; then + clipboard-clear-primary + fi + ) & + fi + else + clipboard-set <<< "$2" + if [[ $CLEAR -gt 0 ]]; then + ( + sleep "$CLEAR" + if [[ "$(clipboard-get)" == "$2" ]]; then + clipboard-clear + fi + ) & + fi + fi + +} + show_copy_notification() { # not sure if icon will be present everywhere, /usr/share/icons is default icon location notify-send "$1" -i /usr/share/icons/hicolor/64x64/apps/bitwarden.png From 3d7522bd1cea6a745d8a6522189c4cf8ea7be6d0 Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Wed, 23 Oct 2019 11:51:10 -0500 Subject: [PATCH 08/12] add action to display everything (rudimentary) --- bwmenu | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/bwmenu b/bwmenu index d7eecdf..439c0ec 100755 --- a/bwmenu +++ b/bwmenu @@ -17,11 +17,11 @@ ITEMS= AUTOTYPE_MODE= # Stores which command will be used to deal with clipboards -CLIPBOARD_MODE= +CLIPBOARD_MODE=wayland # Specify what happens when pressing Enter on an item. # Defaults to copy_password, can be changed to (auto_type all) or (auto_type password) or show_item_actions -ENTER_CMD=copy_password +ENTER_CMD=${ENTER_CMD:=copy_password} # Keyboard shortcuts KB_SYNC="Alt+r" @@ -50,7 +50,7 @@ source "$DIR/lib-bwmenu" ask_password() { mpw=$(printf '' | rofi -dmenu -p "Master Password" -password -lines 0) || exit $? - echo "$mpw" | bw unlock 2>/dev/null | grep 'export' | sed -E 's/.*export BW_SESSION="(.*==)"$/\1/' || exit_error $? "Could not unlock vault" + echo "$mpw" | bw unlock 2>/dev/null | grep 'export' | sed -E 's/.*export BW_SESSION="(.*==)"$/\1/' || display_then_exit $? "Could not unlock vault" } get_session_key() { @@ -60,7 +60,7 @@ get_session_key() { else if ! key_id=$(keyctl request user bw_session 2>/dev/null); then session=$(ask_password) - [[ -z "$session" ]] && exit_error 1 "Could not unlock vault" + [[ -z "$session" ]] && display_then_exit 1 "Could not unlock vault" key_id=$(echo "$session" | keyctl padd user bw_session @u) fi @@ -75,11 +75,11 @@ get_session_key() { # Pre fetch all the items load_items() { if ! ITEMS=$(bw list items --session "$BW_HASH" 2>/dev/null); then - exit_error $? "Could not load items" + display_then_exit $? "Could not load items" fi } -exit_error() { +display_then_exit() { local code="$1" local message="$2" @@ -189,7 +189,7 @@ show_folders() { # re-sync the BitWarden items with the server sync_bitwarden() { - bw sync --session "$BW_HASH" &>/dev/null || exit_error 1 "Failed to sync bitwarden" + bw sync --session "$BW_HASH" &>/dev/null || display_then_exit 1 "Failed to sync bitwarden" load_items show_items @@ -266,8 +266,12 @@ select_copy_command() { CLIPBOARD_MODE=xclip elif hash xsel 2>/dev/null; then CLIPBOARD_MODE=xsel + elif hash wl-copy 2>/dev/null; then + CLIPBOARD_MODE=wayland + else + display_then_exit 1 "No clipboard command found. Please install either xclip, xsel, or wl-clipboard." fi - [ -z "$CLIPBOARD_MODE" ] && exit_error 1 "No clipboard command found. Please install either xclip, xsel, or wl-clipboard." + [ -z "$CLIPBOARD_MODE" ] && display_then_exit 1 "No clipboard command found. Please install either xclip, xsel, or wl-clipboard." fi } @@ -413,8 +417,9 @@ actions_for_item() { actions+=("$action") done if notes="$(jq -re '.[0].notes' <<< "$1")" ; then - actions+=("Display notes::exit_error 0 ${notes@Q}") + actions+=("Display notes::display_then_exit 0 ${notes@Q}") fi + actions+=("Display everything::_show_everything ${1@Q}") printf '%s\n' "${actions[@]}" } @@ -441,7 +446,7 @@ _copy_totp() { id=$(echo "$1" | jq -r ".[0].id") if ! totp=$(bw --session "$BW_HASH" get totp "$id"); then - exit_error 1 "$totp" + display_then_exit 1 "$totp" fi copy_and_clear clipboard "$totp" @@ -481,7 +486,11 @@ copy_and_clear() { ) & fi fi +} +_show_everything() { + echo "EERTHANG: $1" >&2 + display_then_exit 0 "$(jq -re '.[0] | reduce (tostream|select(length==2)) as $i ({}; .[[$i[0][]|tostring]|join(".")] = $i[1]) | with_entries( select( .value != null and .value != "" and .value != [] and .value != {} and .key != "type") ) | del(.revisionDate, .object, .id, .folderId, .type, .favorite)' <<< "$1")" } show_copy_notification() { @@ -497,7 +506,7 @@ lock_vault() { parse_cli_arguments() { # Use GNU getopt to parse command line arguments if ! ARGUMENTS=$(getopt -o c:C --long auto-lock:,clear:,no-clear,show-password,state-path:,help,version -- "$@"); then - exit_error 1 "Failed to parse command-line arguments" + display_then_exit 1 "Failed to parse command-line arguments" fi eval set -- "$ARGUMENTS" @@ -593,7 +602,7 @@ USAGE break ;; * ) - exit_error 1 "Unknown option $1" + display_then_exit 1 "Unknown option $1" esac done } From 7d9b97c7bc315ca89097e4c28b035c1f993245d6 Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Wed, 23 Oct 2019 23:17:57 -0500 Subject: [PATCH 09/12] improve presentation of 'Display everything' option --- bwmenu | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/bwmenu b/bwmenu index 439c0ec..c9aa833 100755 --- a/bwmenu +++ b/bwmenu @@ -63,7 +63,7 @@ get_session_key() { [[ -z "$session" ]] && display_then_exit 1 "Could not unlock vault" key_id=$(echo "$session" | keyctl padd user bw_session @u) fi - + if [ $AUTO_LOCK -gt 0 ]; then keyctl timeout "$key_id" $AUTO_LOCK fi @@ -489,8 +489,19 @@ copy_and_clear() { } _show_everything() { - echo "EERTHANG: $1" >&2 - display_then_exit 0 "$(jq -re '.[0] | reduce (tostream|select(length==2)) as $i ({}; .[[$i[0][]|tostring]|join(".")] = $i[1]) | with_entries( select( .value != null and .value != "" and .value != [] and .value != {} and .key != "type") ) | del(.revisionDate, .object, .id, .folderId, .type, .favorite)' <<< "$1")" + display_then_exit 0 "$( + jq -re '.[0] | + .URIs = (.login.uris | map(.uri)) | + .Username = .login.username | + .Password = .login.password | + reduce .fields[] as $field (.; .[$field.name] = $field.value) | + del(.id, .folderId, .object, .revisionDate, .type, .favorite, .login, .fields) | + reduce (tostream | select(length==2)) as $i ({}; .[[$i[0][] | tostring] | join(" ")] = $i[1]) | + to_entries | + map(if (.value | length) > 0 then .key + ": " + .value else empty end) | + join("\n") + ' <<< "$1" + )" } show_copy_notification() { @@ -556,7 +567,7 @@ Quick Actions: $KB_TYPEALL Autotype the username and password [needs xdotool or ydotool] $KB_TYPEUSER Autotype the username [needs xdotool or ydotool] $KB_TYPEPASS Autotype the password [needs xdotool or ydotool] - + $KB_LOCK Lock your vault Examples: From 405fd2881b0d60250f8b292dd58008c91c360081 Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Wed, 23 Oct 2019 23:42:33 -0500 Subject: [PATCH 10/12] add actions to auto-type username and password --- bwmenu | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bwmenu b/bwmenu index c9aa833..e0e39b4 100755 --- a/bwmenu +++ b/bwmenu @@ -389,14 +389,17 @@ actions_for_item() { actions=() if username="$(jq -re ".[0].login.username" <<< "$1")"; then actions+=("Copy username:Username copied to clipboard:copy_and_clear clipboard ${username@Q}") + [ -n "$AUTOTYPE_MODE" ] && actions+=("Type username::auto_type username ${1@Q}") fi if password="$(jq -re ".[0].login.password" <<< "$1")"; then actions+=("Copy password:Password copied to clipboard:copy_and_clear clipboard ${password@Q}") + [ -n "$AUTOTYPE_MODE" ] && actions+=("Type password::auto_type password ${1@Q}") fi if [ -n "$password" ] && [ -n "$username" ] ; then actions+=("Copy username and password:Username copied to primary selection
Password copied to clipboard: \ copy_and_clear primary ${username@Q} \ && copy_and_clear clipboard ${password@Q}") + [ -n "$AUTOTYPE_MODE" ] && actions+=("Type username and password::auto_type all ${1@Q}") fi if uri="$(jq -re 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" ; then actions+=("Open URI::xdg-open ${uri@Q}") From f25687648dce4ed05fc76839a58d8b53c4980725 Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Tue, 20 Apr 2021 15:56:38 -0500 Subject: [PATCH 11/12] handle logging in when email address is not set --- bwmenu | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/bwmenu b/bwmenu index e0e39b4..f6672a7 100755 --- a/bwmenu +++ b/bwmenu @@ -48,26 +48,37 @@ DEDUP_MARK="(+)" DIR="$(dirname "$(readlink -f "$0")")" source "$DIR/lib-bwmenu" +ask_login() { + email=$(rofi -dmenu -p "User Email" -lines 0) || exit $? + mpw=$(rofi -dmenu -p "Master Password" -password -lines 0) || exit $? + echo "$mpw" | bw login "$email" 2>/dev/null | grep 'export' | sed -E 's/.*export BW_SESSION="(.*==)"$/\1/' || display_then_exit $? "Could not log in" +} + ask_password() { mpw=$(printf '' | rofi -dmenu -p "Master Password" -password -lines 0) || exit $? echo "$mpw" | bw unlock 2>/dev/null | grep 'export' | sed -E 's/.*export BW_SESSION="(.*==)"$/\1/' || display_then_exit $? "Could not unlock vault" } get_session_key() { - if [ $AUTO_LOCK -eq 0 ]; then + if [[ $AUTO_LOCK -eq 0 ]]; then keyctl purge user bw_session &>/dev/null - BW_HASH=$(ask_password) + fi + + if key_id=$(keyctl request user bw_session 2>/dev/null); then + BW_HASH=$(keyctl pipe "$key_id") + elif [[ "$(bw status | jq '.userEmail')" == null ]]; then + BW_HASH=$(ask_login) else - if ! key_id=$(keyctl request user bw_session 2>/dev/null); then - session=$(ask_password) - [[ -z "$session" ]] && display_then_exit 1 "Could not unlock vault" - key_id=$(echo "$session" | keyctl padd user bw_session @u) - fi + BW_HASH=$(ask_password) + fi - if [ $AUTO_LOCK -gt 0 ]; then + [[ -z "$BW_HASH" ]] && display_then_exit 1 "Could not unlock vault" + + if [[ $AUTO_LOCK -ne 0 ]]; then + key_id=$(echo "$BW_HASH" | keyctl padd user bw_session @u) + if [[ $AUTO_LOCK -gt 0 ]]; then keyctl timeout "$key_id" $AUTO_LOCK fi - BW_HASH=$(keyctl pipe "$key_id") fi } From b89162711c0744d17fbd1fa4f3f204c1be74cde4 Mon Sep 17 00:00:00 2001 From: Nathan Wallace Date: Tue, 20 Apr 2021 15:59:07 -0500 Subject: [PATCH 12/12] fix copy to refer to correct clipboards --- bwmenu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bwmenu b/bwmenu index f6672a7..b6888f4 100755 --- a/bwmenu +++ b/bwmenu @@ -407,9 +407,9 @@ actions_for_item() { [ -n "$AUTOTYPE_MODE" ] && actions+=("Type password::auto_type password ${1@Q}") fi if [ -n "$password" ] && [ -n "$username" ] ; then - actions+=("Copy username and password:Username copied to primary selection
Password copied to clipboard: \ - copy_and_clear primary ${username@Q} \ - && copy_and_clear clipboard ${password@Q}") + actions+=("Copy username and password:Username copied to clipboard
Password copied to primary selection: \ + copy_and_clear clipboard ${username@Q} \ + && copy_and_clear primary ${password@Q}") [ -n "$AUTOTYPE_MODE" ] && actions+=("Type username and password::auto_type all ${1@Q}") fi if uri="$(jq -re 'map(.login.uris[0].uri | select(. != null)) | first' <<< "$1")" ; then