dotfiles/pass/.local/bin/pass-pick

362 lines
11 KiB
Text
Raw Normal View History

Switch to wayland Added a simple wayland configuration. Currently set up simple wayland configuration based on river window manager and waybar. Rivercarro is the layout manager, being the same in principle as rivertile, the default layout manager for river, only it comes with smart gaps (gaps turn off if there is only one window open) and monocle mode (give one window all space). Runs `keyd` in the background to replace the old `xcape` capslock switching (capslock is escape and if held control). Uses `swaybg` to set a wallpaper. Added powermenu and lockscreen scripts. Improved lockscreen script to detect and work for wayland. Moved old rofi mode 'powermenu' to more general powermenu script, which works with any rofi-like selector (dmenu, bemenu, wofi, etc.) Loses some of its design quality but since it was wonky anyway, and I rarely see the menu, we could repurpose its functionality for a more general powermenu concept. Currently hardcoded for `bemenu` but can be easily swapped and possibly even extended back to rofi. Fixed file upload link sharing to clipboard. Updated rofi-pass to pass-pick. Made rofi-pass universal and less integrated to rofi - that's also the reason for the name change. `pass-pick` works with rofi (default), bemenu or dmenu. In theory it should also work with any other picker that contains a stdin listing function similar to dmenu. It has been definitely tested both on rofi and bemenu. The best user experience still reigns on rofi, where available keys are displayed on the picker and the keys themselves make the most sense. But all functions can be reached from bemenu as well, though the key mappings are more arbitrary and can not be changed as in rofi. The autofilling tool works with both xdotool and ydotool, so should work both on X11 and on Wayland. Ydotool ideally requires its daemon to be running, otherwise some of the typing may get gut off. Otherwise no change should be necessary. Updated qutebrowser open_download for bemenu. Updated download opening script to work with both rofi and bemenu. Prefers original rofi implementation but works with both, and can be set to use a custom dmenu-like file picker as well. Add brightnessctl and removed custom audio / brightness scripts since they became unnecessary. Updated bootstrap script to include system files: With `keyd` taking its configuration from the `/etc` directory and not home, a second stow stage was necessary. These stow files are in a module called `system-packages` inside the top-level `bootstrap` stow package. They will not be installed by the default dotfile stow invocation but have been integrated as an extra step into the install script. Installing this module requires sudo privileges! Switched vifm überzug to sixel graphics rendering. überzug relies on X11 functionality to work, while sixel does not. Unfortunately, alacritty does not work with sixel graphics yet, only foot does (somewhat). Waybar currently runs the gruvbox dark soft color scheme. Added the old polybar archupdates script to waybar and extended it to output json format with additional metadata that waybar can read. Can still output the old plaintext format that polybar expects. Added a wireguard connection to waybar,shows if currently connected to either a wireguard or tun VPN service. If so, shows an icon in the waybar - that can be hovered over to show the full assigned IP address. Added an upcoming event display to waybar, a simple event indicator to show upcoming events on the calendar, on hovering over it the tooltip lists all upcoming events. Added `screenshot` script to take simple screenshots and rectangle region shots of the current output. Can be invoked through the river shortcut PrintScr: `PrintScr` - Fullscreen screenshot `Mod+PrintScr` - Region screenshot `Shift+PrintScr` - Fullscreen screenshot and file upload `Mod+Shift+PrintScr` - Region screenshot and file upload Extended `sharefile` to take paths through stdin and make use of `fd` if it is found on the system.
2021-11-26 06:59:35 +00:00
#!/usr/bin/env bash
#
# Pass picker
#
# Use a dmenu-like list selector to display and autofill your pass passwords.
# Can work with rofi, bemenu and dmenu, or a custom picker given as an option.
# Invoke it with `pass-pick`. You can set options through environment variables
# or through a configuration file.
#
# Keys:
# By default shows the available keys on rofi, but not on bemenu/dmenu.
# ROFI mapped keys (main password list):
# return autofill username/password combination
# alt+return enter entry submenu
# alt+u autofill username
# alt+p autofill password
# alt+ctrl+u send username to clipboard
# alt+ctrl+p send password to clipboard
# ROFI mapped keys (individual entry):
# return autofill selected field
# alt+return send selected field to clipboard
# alt+s reveal hidden password field
# alt+backspace back to main password menu
# Those options also work on bemenu, but have different (and fixed) mappings.
# BEMENU mapped keys (main password list):
# return autofill username/password combination
# alt+2 send username to clipboard
# alt+3 send password to clipboard
# alt+4 autofill username
# alt+5 autofill password
# alt+6 enter entry submenu
# BEMENU mapped keys (individual entry):
# return autofill selected field
# alt+2 send selected field to clipboard
# alt+3 back to main password menu
# alt+4 reveal hidden password field
# Selector wrapper
# Prefers rofi if found, otherwise bemenu or dmenu if found, complains if no selector available.
# Passes along any options given to main script.
rofi_opts=("$@")
_picker() {
if [ -n "$PICKER" ]; then
"${PICKER[@]}"
elif command -v rofi 1>/dev/null 2>/dev/null; then
rofi -dmenu -no-auto-select -i "${rofi_opts[@]}" "$@" -p "entry"
elif command -v bemenu 1>/dev/null 2>/dev/null; then
bemenu -l 20 -i -p "entry >"
elif command -v dmenu 1>/dev/null 2>/dev/null; then
dmenu -i -p "entry >"
else
printf "%s: 📦 %s must be installed for %s function.\n" "critical" "rofi/dmenu" "this" >&2
notify-send "📦 rofi/dmenu" --urgency="critical" "must be installed for this function."
exit 1
fi
}
# parse, see https://unix.stackexchange.com/a/331965/8541
_parse_config() {
(grep -e "^$2=" -m 1 "$1" 2>/dev/null || printf "var=__UNDEFINED__\n") | head -n1 | cut -d '=' -f 2-
}
# read config file
get_config() {
local locations=(
"$PP_CONFIGURATION_FILE"
"${xdg_config_home:-$HOME/.config}/pass-picker/pass-picker.conf"
"$HOME/.pass-picker.conf"
"/etc/pass-picker.conf"
)
# return the first config file with a valid path
for config in "${locations[@]}"; do
if [[ -n "$config" && -f "$config" ]]; then
# see if the config has been given a value
local val
val="$(_parse_config "$config" "$1")"
break
fi
done
# if there was a config file but no value
# or there was no config file at all
if [ "$val" = "__UNDEFINED__" ] || [ -z "$val" ]; then
val="$2"
fi
printf -- "%s" "$val"
}
set_defaults() {
# The location of the pass-picker config file
# PP_CONFIGURATION_FILE="~/.config/pass-picker/pass-picker.conf"
# set options, leaving already set environment variables intact
# try to read any settings from config files
PICKER="${PP_PICKER:-$(get_config PICKER)}"
KEY_AUTOFILL="${PP_KEY_AUTOFILL:-$(get_config KEY_AUTOFILL Return)}"
KEY_ENTRY_OPEN="${PP_KEY_ENTRY_OPEN:-$(get_config KEY_ENTRY_OPEN Alt+Return)}"
KEY_FILL_USER="${PP_KEY_FILL_USER:-$(get_config KEY_FILL_USER Alt+u)}"
KEY_CLIP_USER="${PP_KEY_CLIP_USER:-$(get_config KEY_CLIP_USER Ctrl+Alt+u)}"
KEY_FILL_PASS="${PP_KEY_FILL_PASS:-$(get_config KEY_FILL_PASS Alt+p)}"
KEY_CLIP_PASS="${PP_KEY_CLIP_PASS:-$(get_config KEY_CLIP_PASS Ctrl+Alt+p)}"
KEY_ENTRYMENU_FILL="${PP_KEY_ENTRYMENU_FILL:-$(get_config KEY_ENTRYMENU_FILL Return)}"
KEY_ENTRYMENU_CLIP="${PP_KEY_ENTRYMENU_CLIP:-$(get_config KEY_ENTRYMENU_CLIP Alt+Return)}"
KEY_ENTRYMENU_SHOWFIELD="${KEY_ENTRYMENU_SHOWFIELD:-$(get_config KEY_ENTRYMENU_SHOWFIELD Alt+s)}"
KEY_ENTRYMENU_QUIT="${PP_KEY_ENTRYMENU_QUIT:-$(get_config KEY_ENTRYMENU_QUIT Alt+BackSpace)}"
AUTOFILL_BACKEND="${PP_AUTOFILL_BACKEND:-$(get_config AUTOFILL_BACKEND ydotool)}"
AUTOFILL_CHAIN="${PP_AUTOENTRY_CHAIN:-$(get_config AUTOFILL_CHAIN 'username :tab password')}"
AUTOFILL_DELAY="${PP_AUTOENTRY_DELAY:-$(get_config AUTOFILL_DELAY 30)}"
PASS_USERNAME_FIELD="${PP_PASS_USERNAME_FIELD:-$(get_config PASS_USERNAME_FIELD 'username user login')}"
}
# exit on escape pressed
exit_check() {
[ "$1" -eq 1 ] && exit
}
# simply return a list of all passwords in pass store
# TODO only show website names (+ folder names), and account names for multiple accounts on one site
list_passwords() {
shopt -s nullglob globstar
prefix=${PASSWORD_STORE_DIR:-~/.password-store}
password_files=("$prefix"/**/*.gpg)
password_files=("${password_files[@]#"$prefix"/}")
password_files=("${password_files[@]%.gpg}")
printf '%s\n' "${password_files[@]}"
}
# return password for argument passed
show_password() {
pass show "$1" | head -n1
}
# send password to clipboard
clip_password() {
pass show -c "$1"
}
# attempt to return the field specified
# attempts all (space separated) fields until the
# first one successfully returned
_p_get_field() {
local gp_entry="$1"
local gp_field="$2"
local clip="$3"
# return on first successfully returned key
for key in $gp_field; do
local value
value=$(_p_get_key_value "$gp_entry" "$key")
# found entry
if [ -n "$value" ]; then
if [ -n "$clip" ]; then
# copies to clipboard, removes any trailing newlines,
# and only keeps it in for 1 paste (1 loop to read in script, 1 to output)
if command -v wl-copy; then
echo "$value" | wl-copy -o && break
elif command -v xclip; then
echo "$value" | xclip -i -selection 'clipboard' -loops 2 -rmlastnl && break
elif command -v xsel; then
echo "$value" | xsel -b && break
else
notify-send "No clipboard utility" "Install wl-copy, xclip or xsel."
fi
else
echo "$value" && break
fi
fi
done
}
# returns the corresponding value for the key passed in
# arguments:
# $1: pass (file) entry to search through
# $2: string name of the containting key
_p_get_key_value() {
local value
value=$(list_fields "$1" | grep "$2")
# get everything after first colon, remove whitespace
echo "$value" | cut -d':' -f2- | tr -d '[:blank:]'
}
# return username for argument passed
show_username() {
_p_get_field "$1" "${PASS_USERNAME_FIELD}"
}
clip_username() {
_p_get_field "$1" "${PASS_USERNAME_FIELD}" "-c"
}
show_field() {
_p_get_field "$1" "$2"
}
clip_field() {
_p_get_field "$1" "$2" "-c"
}
list_fields() {
pass show "$1" | tail -n+2
}
# invoke the dotool to type inputs
_type() {
local tool="${AUTOFILL_BACKEND}"
local toolmode="$1"
local key="$2"
if [ "$tool" = "xdotool" ]; then
"$tool" "$toolmode" --delay "${AUTOFILL_DELAY}" "$key"
elif [ "$tool" = "ydotool" ]; then
"$tool" "$toolmode" --key-delay "${AUTOFILL_DELAY}" "$key"
else
"$tool" "$toolmode" "$key"
fi
}
# automatically fill out fields
# transform special chain entries into valid dotool commands
autofill() {
local selected="${1}"
local autoentry_chain="${2}"
for part in $autoentry_chain; do
case "$part" in
":tab") _type key Tab ;;
":return") _type key Return ;;
":space") _type key space ;;
"username") _type type "$(show_username "$selected")" ;;
"password") _type type "$(show_password "$selected")" ;;
":direct") _type type "$selected" ;;
*) printf '%s' "$selected" ;;
esac
done
}
# opens a menu for the specified pass entry, containing its individual fields
entrymenu() {
local entry="$1"
local deobfuscate="$2"
local k_entrymenu_fill="${KEY_ENTRYMENU_FILL}"
local k_entrymenu_clip="${KEY_ENTRYMENU_CLIP}"
local k_entrymenu_showfield="${KEY_ENTRYMENU_SHOWFIELD}"
local k_entrymenu_quit="${KEY_ENTRYMENU_QUIT}"
local pass
if [ "$deobfuscate" = "true" ]; then
pass="$(show_password "$entry")"
else
pass="(hidden)"
fi
local field
field=$(
printf "password: %s\n%s" "$pass" "$(list_fields "$entry")" |
_picker \
-kb-accept-entry "" \
-kb-custom-1 "$k_entrymenu_fill" \
-kb-custom-2 "$k_entrymenu_clip" \
-kb-custom-3 "$k_entrymenu_quit" \
-kb-custom-4 "$k_entrymenu_showfield" \
-mesg " ᐊ $k_entrymenu_quit ᐊ | $k_entrymenu_fill: fill selection | $k_entrymenu_clip: clip selection | $k_entrymenu_showfield: reveal password"
)
exit_value=$?
exit_check "$exit_value"
# get field name
field=${field%%:*}
case "$exit_value" in
"0" | "10")
if [ "$field" = "password" ]; then
autofill "$entry" "password"
else
autofill "$(show_field "$entry" "$field")" ":direct"
fi
exit 0
;;
"11")
if [ "$field" = "password" ]; then
clip_password "$entry"
else
clip_field "$entry" "$field"
fi
exit 0
;;
"12")
main
;;
"13")
local toggle
if [ "$deobfuscate" = "true" ]; then
toggle=false
else
toggle=true
fi
entrymenu "$entry" "$toggle"
;;
esac
}
main() {
local autoentry_chain="${AUTOFILL_CHAIN}"
local k_autofill="${KEY_AUTOFILL}"
local k_fill_user="${KEY_FILL_USER}"
local k_clip_user="${KEY_CLIP_USER}"
local k_fill_pass="${KEY_FILL_PASS}"
local k_clip_pass="${KEY_CLIP_PASS}"
local k_submenu="${KEY_ENTRY_OPEN}"
entry="$(
list_passwords |
_picker -kb-accept-entry "" \
-kb-custom-1 "$k_autofill" \
-kb-custom-2 "$k_clip_user" \
-kb-custom-3 "$k_clip_pass" \
-kb-custom-4 "$k_fill_user" \
-kb-custom-5 "$k_fill_pass" \
-kb-custom-6 "$k_submenu" \
-mesg "| $k_autofill: fill credentials | $k_submenu: open entry | $k_fill_user: fill username | $k_fill_pass: fill password | $k_clip_user: clip username | $k_clip_pass: clip password |"
)"
exit_value=$?
echo "$entry"
exit_check "$exit_value"
case "$exit_value" in
"0" | "10")
autofill "$entry" "$autoentry_chain"
exit 0
;;
"11")
clip_username "$entry"
exit 0
;;
"12")
clip_password "$entry"
exit
;;
"13")
autofill "$entry" "username"
exit
;;
"14")
autofill "$entry" "password"
exit
;;
"15")
entrymenu "$entry"
exit
;;
esac
}
set_defaults
main