From f7526687dc03a994bafe036b84ae760fd01209c1 Mon Sep 17 00:00:00 2001 From: binaryDiv Date: Fri, 17 Dec 2021 23:20:38 +0100 Subject: [PATCH] Add passrofi version 0.1.0 --- _local/roxy/.config/i3/config | 2 +- bin/passrofi | 334 ++++++++++++++++++++++++++++++++++ 2 files changed, 335 insertions(+), 1 deletion(-) create mode 100755 bin/passrofi diff --git a/_local/roxy/.config/i3/config b/_local/roxy/.config/i3/config index 54a3364..e698258 100644 --- a/_local/roxy/.config/i3/config +++ b/_local/roxy/.config/i3/config @@ -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 diff --git a/bin/passrofi b/bin/passrofi new file mode 100755 index 0000000..d4d3d69 --- /dev/null +++ b/bin/passrofi @@ -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 +# +# --------------------------------------------------------------------------------