From 0335159f126f14ae87bcd1ccb45459cbae96eaa0 Mon Sep 17 00:00:00 2001 From: Trevor Vallender Date: Mon, 12 Jun 2023 09:20:50 +0100 Subject: [PATCH] Adding 1Password to Qutebrowser --- users/tsv/home.nix | 1 + users/tsv/qutebrowser/userscripts/qute-1pass | 124 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 users/tsv/qutebrowser/userscripts/qute-1pass diff --git a/users/tsv/home.nix b/users/tsv/home.nix index d9727cd..570d845 100644 --- a/users/tsv/home.nix +++ b/users/tsv/home.nix @@ -78,6 +78,7 @@ xdg.configFile."rofi/config.rasi".source = ./rofi/config.rasi; xdg.configFile."qutebrowser/config.py".source = ./qutebrowser/config.py; xdg.configFile."qutebrowser/gruvbox.py".source = ./qutebrowser/gruvbox.py; + home.file.".local/share/qutebrowser/userscripts/qute-1pass".source = .qutebrowser/userscripts/qute-1pass; home.file.".ssh/config".source = ./ssh/config; home.file.".LESS_TERMCAP".source = ./LESS_TERMCAP; diff --git a/users/tsv/qutebrowser/userscripts/qute-1pass b/users/tsv/qutebrowser/userscripts/qute-1pass new file mode 100644 index 0000000..091f841 --- /dev/null +++ b/users/tsv/qutebrowser/userscripts/qute-1pass @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +set +e + +# JS field injection code from https://github.com/qutebrowser/qutebrowser/blob/master/misc/userscripts/password_fill +javascript_escape() { + # print the first argument in an escaped way, such that it can safely + # be used within javascripts double quotes + # shellcheck disable=SC2001 + sed "s,[\\\\'\"\/],\\\\&,g" <<< "$1" +} + +js() { +cat < 0 && elem.offsetHeight > 0; + }; + function hasPasswordField(form) { + var inputs = form.getElementsByTagName("input"); + for (var j = 0; j < inputs.length; j++) { + var input = inputs[j]; + if (input.type == "password") { + return true; + } + } + return false; + }; + function loadData2Form (form) { + var inputs = form.getElementsByTagName("input"); + for (var j = 0; j < inputs.length; j++) { + var input = inputs[j]; + if (isVisible(input) && (input.type == "text" || input.type == "email")) { + input.focus(); + input.value = "$(javascript_escape "${USERNAME}")"; + input.dispatchEvent(new Event('change')); + input.blur(); + } + if (input.type == "password") { + input.focus(); + input.value = "$(javascript_escape "${PASSWORD}")"; + input.dispatchEvent(new Event('change')); + input.blur(); + } + } + }; + var forms = document.getElementsByTagName("form"); + if("$(javascript_escape "${QUTE_URL}")" == window.location.href) { + for (i = 0; i < forms.length; i++) { + if (hasPasswordField(forms[i])) { + loadData2Form(forms[i]); + } + } + } else { + alert("Secrets will not be inserted.\nUrl of this page and the one where the user script was started differ."); + } +EOF +} + +URL=$(echo "$QUTE_URL" | awk -F/ '{print $3}' | sed 's/www.//g') +TOKEN_TMPDIR="${TMPDIR:-/tmp}" +TOKEN_CACHE="$TOKEN_TMPDIR/1pass.token" + +echo "message-info 'Looking for password for $URL...'" >> "$QUTE_FIFO" + +if [ -f "$TOKEN_CACHE" ]; then + TOKEN=$(cat "$TOKEN_CACHE") + if ! op signin --session="$TOKEN" --output=raw > /dev/null; then + TOKEN=$(rofi -dmenu -password -p "1password: "| op signin --output=raw) || TOKEN="" + echo "$TOKEN" > "$TOKEN_CACHE" + fi +else + TOKEN=$(rofi -dmenu -password -p "1password: "| op signin --output=raw) || TOKEN="" + install -m 600 /dev/null "$TOKEN_CACHE" + echo "$TOKEN" > "$TOKEN_CACHE" +fi + + +if [ -n "$TOKEN" ]; then + UUID=$(op list items --cache --session="$TOKEN" | jq --arg url "$URL" -r '[.[] | {uuid, url: [.overview.URLs[]?.u, .overview.url][]?} | select(.uuid != null) | select(.url != null) | select(.url|test(".*\($url).*"))][.0].uuid') || UUID="" + + if [ -z "$UUID" ] || [ "$UUID" == "null" ];then + echo "message-error 'No entry found for $URL'" >> "$QUTE_FIFO" + TITLE=$(op list items --cache --session="$TOKEN" | jq -r '.[].overview.title' | rofi -dmenu -i) || TITLE="" + if [ -n "$TITLE" ]; then + UUID=$(op list items --cache --session="$TOKEN" | jq --arg title "$TITLE" -r '[.[] | {uuid, title:.overview.title}|select(.title|test("\($title)"))][.0].uuid') || UUID="" + else + UUID="" + fi + fi + + if [ -n "$UUID" ];then + ITEM=$(op get item --cache --session="$TOKEN" "$UUID") + + PASSWORD=$(echo "$ITEM" | jq -r '.details.fields | .[] | select(.designation=="password") | .value') + + if [ -n "$PASSWORD" ]; then + TITLE=$(echo "$ITEM" | jq -r '.overview.title') + USERNAME=$(echo "$ITEM" | jq -r '.details.fields | .[] | select(.designation=="username") | .value') + + printjs() { + js | sed 's,//.*$,,' | tr '\n' ' ' + } + echo "jseval -q $(printjs)" >> "$QUTE_FIFO" + + TOTP=$(echo "$ITEM" | op get totp --cache --session="$TOKEN" "$UUID") || TOTP="" + if [ -n "$TOTP" ]; then + echo "$TOTP" | xclip -in -selection clipboard + echo "message-info 'Pasted one time password for $TITLE to clipboard'" >> "$QUTE_FIFO" + fi + else + echo "message-error 'No password found for $URL'" >> "$QUTE_FIFO" + fi + else + echo "message-error 'Entry not found for $UUID'" >> "$QUTE_FIFO" + fi +else + echo "message-error 'Wrong master password'" >> "$QUTE_FIFO" +fi