Add passrofi version 0.1.0

This commit is contained in:
Lexi / Zoe 2021-12-17 23:20:38 +01:00
parent 6485bc1deb
commit f7526687dc
Signed by: binaryDiv
GPG Key ID: F8D4956E224DA232
2 changed files with 335 additions and 1 deletions

View File

@ -384,7 +384,7 @@ bindsym Pause exec --no-startup-id "volctl mute toggle"
#bindsym XF86LaunchA exec --no-startup-id touchpad-toggle
# ~~ Password manager: passmenu
bindsym $mod+Shift+p exec --no-startup-id passmenu
bindsym $mod+Shift+p exec passrofi
# ~~ Screenshot tool
bindsym $mod+Print exec flameshot gui

334
bin/passrofi Executable file
View File

@ -0,0 +1,334 @@
#!/bin/bash
# passrofi -- Rofi script for pass (passwordstore.org)
# Version: 0.1.0
# Author: binaryDiv
# Strict mode
set -euo pipefail
IFS=$'\n\t'
# Set some generic variables
SCRIPTNAME=$0
BASENAME=$(basename $0)
# Usage
# -----
usage() {
cat << END_OF_USAGE
Usage: $BASENAME [-d DIR] [-p PROMPT]
Simple rofi script to query passwords from pass (passwordstore.org).
Calling this script directly will start rofi in script mode.
General options:
-h, --help Print this help message
-v, --verbose Enables debug logging via stderr
-d DIR Sets the password-store directory (default: ~/.password-store)
-p PROMPT Sets the rofi prompt to PROMPT (default: "Password")
END_OF_USAGE
}
# Helper functions
# ----------------
# Logs a message to stderr if verbose mode is enabled (with a '#' prefix)
log() {
if [[ -n $PASSROFI_VERBOSE ]]; then
echo "# $@" >&2
fi
}
# Prints a message to stderr and exits with status 1
fail() {
echo "$@" >&2
exit 1
}
# Main entrypoint
# ---------------
# Main entrypoint: Checks if the script was called from the command line or as a rofi script, then
# calls either main_cli (first case) or main_rofi (second case).
main() {
# Set defaults for environment variables
set_default_env
# If the script was called by rofi, this env variable will be defined
if [[ -v ROFI_RETV ]]; then
main_rofi "$@"
else
main_cli "$@"
fi
}
set_default_env() {
# Set default values for environment variables (unless already set)
PASSWORD_STORE_DIR=${PASSWORD_STORE_DIR:-~/.password-store}
PASSROFI_VERBOSE=${PASSROFI_VERBOSE:-}
PASSROFI_PROMPT=${PASSROFI_PROMPT:-Password}
}
# CLI mode (called from command line)
# -----------------------------------
# Entrypoint when called from the command line: Parses command line arguments, sets some environment
# variables, and starts rofi (which then calls this script again, using main_rofi as entrypoint).
main_cli() {
# Parse command line arguments (sets variables)
parse_cli_args "$@"
# Start rofi
exec_rofi
}
# Parses command line arguments and sets variables accordingly
parse_cli_args() {
# Use "-:" to sort of parse long options
while getopts ":hvd:p:-:" option; do
# getopts does not support long options, so we build a workaround
if [[ $option == "-" ]]; then
option="-$OPTARG"
OPTARG=""
fi
case $option in
h|-help)
usage
exit 0
;;
v|-verbose)
export PASSROFI_VERBOSE=1
log "I can be very verbose if you want me to, HOOT HOOT"
;;
d)
export PASSWORD_STORE_DIR="$OPTARG"
log "Setting password store directory to $PASSWORD_STORE_DIR"
;;
p)
export PASSROFI_PROMPT="$OPTARG"
log "Setting rofi prompt to $PASSROFI_PROMPT"
;;
:)
fail "$BASENAME: option -$OPTARG requires an argument."
;;
?|*)
fail "$BASENAME: invalid option: -${OPTARG:-$option}"
;;
esac
done
}
# Executes rofi with the needed parameters and environment variables
exec_rofi() {
exec rofi -show pass \
-modi "pass:$SCRIPTNAME" \
-kb-custom-1 "Alt-h" \
-kb-custom-2 "Alt-u" \
-kb-custom-3 "Alt-v"
}
# Rofi script mode (called by rofi)
# ---------------------------------
# Entrypoint when called by rofi as a rofi script: Lists available password files and parses input.
main_rofi() {
# Debug output
log "Called by rofi: ROFI_RETV=${ROFI_RETV:-}"
log " ARGS: $@"
log " ROFI_INFO: ${ROFI_INFO:-}"
log " PASSWORD_STORE_DIR: ${PASSWORD_STORE_DIR:-}"
# Ensure all environment variables are set correctly, directories exist etc.
ensure_environment
# Set rofi mode options (outputs specially formatted strings)
set_rofi_mode_options
# Get user selection from arguments
USER_SELECTION="$@"
# Check current script state and decide which action to take next
handle_rofi_state
}
# Checks the current script state and executes the next action
handle_rofi_state() {
# First, check if we're coming from the help screen
if [[ ${ROFI_INFO:-} == 'help_menu' ]]; then
# Change state so that we show the password list again
ROFI_RETV=0
ROFI_INFO=""
fi
# Check state to determine what to do
case $ROFI_RETV in
# Initial call of script
0)
# Generate a list of password files for rofi to show
generate_password_list
;;
# User selected an entry from the list (1) or a custom entry (2)
1|2)
# Use pass to decrypt and copy the password
handle_user_selection
;;
# Custom keybinding 1 (Alt-h): Show help for keybindings
10)
show_help_menu
;;
# Custom keybinding 2 (Alt-u): Copy username
11)
# Copy username to clipboard
handle_copy_username
;;
# Custom keybinding 3 (Alt-v): View password file in terminal
12)
# Open password file in a less inside a terminal
handle_view_file
;;
# Unused custom keybindings: Show help menu
1[3-9]|2[0-8])
log "Unused keybinding $((ROFI_RETV - 9)), showing help instead."
show_help_menu
;;
# Unknown state
*)
fail "Unknown ROFI_RETV state: $ROFI_RETV"
;;
esac
}
# Ensures all environment variables are set correctly
ensure_environment() {
# Check if the password store directory exists
[[ -d $PASSWORD_STORE_DIR ]] || fail "Invalid password store directory: $PASSWORD_STORE_DIR"
}
# Sets (outputs) rofi mode options
set_rofi_mode_options() {
# This enables custom keybindings for the script
echo -en "\0use-hot-keys\x1ftrue\n"
# Disable custom input
echo -en "\0no-custom\x1ftrue\n"
# Set default prompt and message
echo -en "\0prompt\x1f$PASSROFI_PROMPT\n"
echo -en "\0message\x1f\n"
}
# Custom keybinding (Alt-h) to show a help menu with keybindings
show_help_menu() {
# Set prompt and message
echo -en "\0prompt\x1fHelp\n"
echo -en "\0message\x1fThis is a list of custom keybindings for passrofi.\n"
# Helper function that adds an info string to the entry
_echo() {
echo -en "$@\0info\x1fhelp_menu\n"
}
# Print help page
_echo "Alt-h: Shows this help page"
_echo " "
_echo "Alt-u: Copy username"
_echo "Alt-v: View password file"
}
# Lists all password files as input for rofi
generate_password_list() {
# Get list of all .gpg files in password-store
password_store_dir="${PASSWORD_STORE_DIR%/}"
for password_file in $(find "$password_store_dir" -type f -name '*.gpg' | sort); do
# Strip prefix and suffix
password_file="${password_file#"$password_store_dir"/}"
password_file="${password_file%.gpg}"
# Output filename
echo $password_file
done
}
# Parse user selection: Use pass to decrypt and copy password
handle_user_selection() {
log "User selected entry: $USER_SELECTION"
# External programs need to be launched asynchronously, otherwise rofi blocks
coproc (
# Copy password to clipboard (pass will automatically clear the clipboard after 45 seconds)
pass show -c "$USER_SELECTION" &>/dev/null
)
# End here
exit 0
}
# Copys the username of a selected password file to the clipboard
handle_copy_username() {
log "User wants to copy username of: $USER_SELECTION"
# We can get the username directly from the filename
username=${USER_SELECTION##*/}
log "Username is: $username"
# Copy to clipboard
coproc (
echo "$username" | xclip -selection clipboard -r -silent &>/dev/null
)
# End here
exit 0
}
# Views the selected password file in a pager inside a terminal
handle_view_file() {
log "User wants to view file: $USER_SELECTION"
# Get full filename
password_store_dir="${PASSWORD_STORE_DIR%/}"
filename="$password_store_dir/$USER_SELECTION.gpg"
# Open file in a pager inside a terminal
coproc (
rofi-sensible-terminal -e "bash -ic 'pass show \"$USER_SELECTION\" | less'" &>/dev/null
)
}
# Run script
main "$@"
# --------------------------------------------------------------------------------
#
# CHANGELOG
# =========
#
# Version 0.1.0:
#
# - First version
# - Built-in help page
# - List password files, copy password, copy username, view file in terminal
#
# --------------------------------------------------------------------------------