Last active
April 9, 2025 16:35
-
-
Save tree-s/85cd8c8eff9524e63f76e4aafbb1f6de to your computer and use it in GitHub Desktop.
Windows compatable version of user login scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import os | |
import json | |
import subprocess | |
import tempfile | |
import re | |
import sys | |
import tkinter as tk | |
from urllib.parse import urlparse | |
from tkinter import simpledialog | |
# Get environment variables | |
QUTE_URL = os.getenv("QUTE_URL", "") | |
QUTE_FIFO = os.getenv("QUTE_FIFO", "") | |
# Function to execute shell commands and return output | |
def run_command(command): | |
try: | |
result = subprocess.run(command, encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
return result.stdout.strip() | |
except Exception: | |
return "" | |
def run_input_command(command, data): | |
try: | |
result = subprocess.run(command, input=data, encoding='utf-8', stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
return result.stdout.strip() | |
except Exception: | |
return "" | |
# qute command helpers | |
def qute_command(command): | |
with open(QUTE_FIFO, 'a') as fifo: | |
fifo.write(command + '\n') | |
fifo.flush() | |
def fake_key_raw(text): | |
for character in text: | |
# Escape all characters by default, space requires special handling | |
sequence = '" "' if character == ' ' else r'\{}'.format(character) | |
qute_command('fake-key {}'.format(sequence)) | |
# Function to prompt for the master password using a GUI popup | |
def prompt_password(): | |
root = tk.Tk() | |
root.withdraw() # Hide the main window | |
password = simpledialog.askstring("1Password Login", "Enter your 1Password Master Password:", show="*") | |
root.destroy() | |
return password | |
def main(arguments): | |
# Extract domain from URL | |
URL = re.sub(r"https?://(www\.)?", "", QUTE_URL).split('/')[0] | |
# Token cache file location | |
TOKEN_CACHE = tempfile.gettempdir() + "\\1pass_session.txt" | |
# Notify user | |
qute_command(f"message-info 'Looking for password for {URL}...'") | |
# Get 1Password session token | |
TOKEN = "" | |
if os.path.exists(TOKEN_CACHE): | |
with open(TOKEN_CACHE, "r") as f: | |
TOKEN = f.read().strip() | |
if run_command(f"op signin --force --session={TOKEN} --raw") == "": | |
TOKEN = "" | |
else: | |
TOKEN = "" | |
if not TOKEN: | |
master_password = prompt_password() | |
if master_password: | |
TOKEN = run_input_command( "op signin --force --raw", master_password) | |
if TOKEN: | |
with open(TOKEN_CACHE, "w") as f: | |
f.write(TOKEN) | |
else: | |
qute_command("message-error 'Master password required'") | |
sys.exit(1) | |
# Fetch login entry | |
if TOKEN: | |
command = f'op item list --format=json --session="{TOKEN}"' | |
items_json = run_command(command) | |
if items_json: | |
items = json.loads(items_json) | |
matching_entries = [ | |
entry for entry in items | |
if any( | |
urlparse(url.get("href", "")).netloc == URL | |
for url in entry.get("urls", []) | |
) | |
] | |
if not matching_entries: | |
qute_command(f"message-error 'No entry found for {URL}'") | |
sys.exit(1) | |
UUID = matching_entries[0]["id"] | |
# Get item details | |
item_json = run_command(f'op item get --format=json --session="{TOKEN}" "{UUID}"') | |
item = json.loads(item_json) if item_json else {} | |
# Extract credentials | |
username = "" | |
password = "" | |
for field in item.get("fields", []): | |
if field.get("purpose") == "USERNAME": | |
username = field.get("value", "") | |
if field.get("purpose") == "PASSWORD": | |
password = field.get("value", "") | |
# Handle TOTP (if available) | |
totp = run_command(f'op get totp --session="{TOKEN}" "{UUID}"') | |
if arguments.username_only: | |
fake_key_raw(username) | |
elif arguments.password_only: | |
fake_key_raw(password) | |
elif arguments.totp_only: | |
fake_key_raw(totp) | |
else: | |
fake_key_raw(username) | |
qute_command('fake-key <Tab>') | |
fake_key_raw(password) | |
else: | |
qute_command("message-error 'Error retrieving 1Password items'") | |
else: | |
qute_command("message-error 'Wrong master password'") | |
return 0 | |
if __name__ == '__main__': | |
argument_parser = argparse.ArgumentParser( | |
description="Insert login information using 1Password CLI with Windows compatibility", | |
) | |
argument_parser.add_argument('url', nargs='?', default=QUTE_URL) | |
argument_parser.add_argument('--totp', '-t', action='store_true', | |
help="Copy TOTP key to clipboard") | |
argument_parser.add_argument('--io-encoding', '-i', default='UTF-8', | |
help='Encoding used to communicate with subprocesses') | |
group = argument_parser.add_mutually_exclusive_group() | |
group.add_argument('--username-only', '-e', action='store_true', help='Only insert username') | |
group.add_argument('--password-only', '-w', action='store_true', help='Only insert password') | |
group.add_argument('--totp-only', '-T', action='store_true', help='Only insert TOTP code') | |
arguments = argument_parser.parse_args() | |
sys.exit(main(arguments)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import os | |
import sys | |
import json | |
import tldextract | |
import tempfile | |
import subprocess | |
import pyautogui | |
import time | |
import tkinter as tk | |
from tkinter import simpledialog | |
# Get environment variables | |
QUTE_URL = os.getenv("QUTE_URL", "") | |
QUTE_FIFO = os.getenv("QUTE_FIFO", "") | |
def qute_command(command): | |
with open(QUTE_FIFO, 'a') as fifo: | |
fifo.write(command + '\n') | |
fifo.flush() | |
def fake_key_raw(text): | |
for character in text: | |
# Escape all characters by default, space requires special handling | |
sequence = '" "' if character == ' ' else r'\{}'.format(character) | |
qute_command('fake-key {}'.format(sequence)) | |
def ask_password(): | |
root = tk.Tk() | |
root.withdraw() | |
return simpledialog.askstring("Bitwarden", "Enter Master Password:", show='*') | |
def select_candidate(candidates): | |
if len(candidates) == 1: | |
return candidates[0] | |
root = tk.Tk() | |
root.title("Select a Bitwarden Entry") | |
root.geometry("500x100") | |
selected = [] | |
listbox = tk.Listbox(root, selectmode=tk.SINGLE) | |
for c in candidates: | |
entry_display = f"{c['name']} | {c['login']['username']}" | |
listbox.insert(tk.END, entry_display) | |
listbox.pack(fill=tk.BOTH, expand=True) | |
def on_enter_key(event): | |
selection = listbox.curselection() | |
if selection: | |
selected.append(candidates[selection[0]]) | |
root.destroy() | |
listbox.bind("<Return>", on_enter_key) | |
root.mainloop() | |
return selected[0] if selected else None | |
def store_session_key(session_key): | |
with open(tempfile.gettempdir() + "\\bitwarden_session.txt", "w") as f: | |
f.write(session_key) | |
def get_session_key(auto_lock): | |
session_file = tempfile.gettempdir() + "\\bitwarden_session.txt" | |
if os.path.exists(session_file): | |
with open(session_file, "r") as f: | |
return f.read().strip() | |
master_pass = ask_password() | |
if not master_pass: | |
sys.exit("No password entered. Exiting.") | |
process = subprocess.run(['bw', 'unlock', '--raw'], | |
input=master_pass, encoding='utf-8', | |
stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
if process.returncode != 0: | |
sys.exit("Could not unlock vault") | |
session_key = process.stdout.strip() | |
store_session_key(session_key) | |
return session_key | |
def pass_(domain, encoding, auto_lock): | |
session_key = get_session_key(auto_lock) | |
if not session_key: | |
return '[]' | |
process = subprocess.run(['bw', 'list', 'items', '--session', session_key, '--url', domain], | |
capture_output=True, text=True) | |
return process.stdout.strip() | |
def main(arguments): | |
# Notify user | |
qute_command(f"message-info 'Looking for password for {arguments.url}...'") | |
if not arguments.url: | |
qute_command("message-error 'No URL provided.'") | |
sys.exit("No URL provided.") | |
extract_result = tldextract.extract(arguments.url) | |
domain = extract_result.registered_domain | |
candidates = json.loads(pass_(domain, arguments.io_encoding, arguments.auto_lock)) | |
if not candidates: | |
qute_command(f"message-error 'No credentials found for {arguments.url}'") | |
sys.exit(f'No credentials found for {arguments.url}') | |
selection = select_candidate(candidates) | |
if not selection: | |
qute_command(f"message-error 'No selection made.'") | |
sys.exit("No selection made.") | |
username = selection['login']['username'] | |
password = selection['login']['password'] | |
totp = selection.get('login', {}).get('totp') | |
if arguments.username_only: | |
fake_key_raw(username) | |
elif arguments.password_only: | |
fake_key_raw(password) | |
elif arguments.totp_only: | |
fake_key_raw(totp if totp else "No TOTP available") | |
else: | |
fake_key_raw(username) | |
qute_command('fake-key <Tab>') | |
fake_key_raw(password) | |
return 0 | |
if __name__ == '__main__': | |
argument_parser = argparse.ArgumentParser( | |
description="Insert login information using Bitwarden CLI with Windows compatibility", | |
) | |
argument_parser.add_argument('url', nargs='?', default=QUTE_URL) | |
argument_parser.add_argument('--totp', '-t', action='store_true', | |
help="Copy TOTP key to clipboard") | |
argument_parser.add_argument('--io-encoding', '-i', default='UTF-8', | |
help='Encoding used to communicate with subprocesses') | |
argument_parser.add_argument('--auto-lock', type=int, default=900, | |
help='Automatically lock the vault after this many seconds') | |
group = argument_parser.add_mutually_exclusive_group() | |
group.add_argument('--username-only', '-e', action='store_true', help='Only insert username') | |
group.add_argument('--password-only', '-w', action='store_true', help='Only insert password') | |
group.add_argument('--totp-only', '-T', action='store_true', help='Only insert TOTP code') | |
arguments = argument_parser.parse_args() | |
sys.exit(main(arguments)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment