First release version

- Added TOTP support
- Removed GPG requirement
- Display more information about item
This commit is contained in:
2025-07-13 22:43:12 +02:00
parent 5b566a4529
commit 223924c242

View File

@@ -1,11 +1,11 @@
#!/usr/bin/env python #!/usr/bin/env python
import json import json
import gnupg
import questionary import questionary
import pyperclip
import argparse import argparse
import typer import typer
import pyotp
from typing_extensions import Annotated from typing_extensions import Annotated
from sys import exit from sys import exit
from os import environ from os import environ
@@ -13,24 +13,14 @@ from subprocess import run, DEVNULL
home = environ.get('HOME') home = environ.get('HOME')
gpg = gnupg.GPG(gnupghome=f"{home}/.gnupg", use_agent=True)
def check_lock() -> bool: def check_lock() -> bool:
check = run(['bw', 'unlock', '--check'], stdout=DEVNULL, stderr=DEVNULL) check = run(['bw', 'unlock', '--check'], stdout=DEVNULL, stderr=DEVNULL)
if check.returncode != 0: if check.returncode != 0:
return False return False
else: return True else: return True
def decrypt_session() -> str:
with open(f"{home}/.secret/bw-session.txt.gpg", 'rb') as f:
master_pw = str(gpg.decrypt_file(f)).strip()
environ["BW_SESSION"] = f"{master_pw}"
f.close()
def new_session(): def new_session():
with open(f"{home}/.secret/bw-master.txt.gpg", 'rb') as f: password = questionary.password("Master password").ask()
password = str(gpg.decrypt_file(f)).strip()
unlock_command = run(['bw', 'unlock', password, '--raw'], capture_output=True, text=True) unlock_command = run(['bw', 'unlock', password, '--raw'], capture_output=True, text=True)
environ["BW_SESSION"] = str(unlock_command.stdout) environ["BW_SESSION"] = str(unlock_command.stdout)
@@ -41,8 +31,7 @@ def new_session():
def get_password(search_string: Annotated[str, typer.Argument(help="The term to search for in Vaultwarden")] = ""): def get_password(search_string: Annotated[str, typer.Argument(help="The term to search for in Vaultwarden")] = ""):
decrypt_session() if check_lock is False:
if check_lock() is False:
new_session() new_session()
list_items_cmd = run(['bw', 'list', 'items'], capture_output=True) list_items_cmd = run(['bw', 'list', 'items'], capture_output=True)
@@ -69,14 +58,30 @@ def get_password(search_string: Annotated[str, typer.Argument(help="The term to
choices.append(questionary.Choice(f"{item['name']} ({item['login']['username']})", item_index)) choices.append(questionary.Choice(f"{item['name']} ({item['login']['username']})", item_index))
chosen_index = questionary.select("Select item", choices, use_shortcuts=True).ask() chosen_index = questionary.select("Select item", choices, use_shortcuts=True).ask()
elif (filtered_list) == 1: elif len(filtered_list) == 1:
chosen_index = 0 chosen_index = 0
else: chosen_index = None else: chosen_index = None
if chosen_index is not None: if chosen_index is not None:
pyperclip.copy(filtered_list[chosen_index]['login']['password'])
print(f"name\t\t: {filtered_list[chosen_index]['name']}")
print(f"id\t\t: {filtered_list[chosen_index]['id']}")
# Loop over URIs
print(f"username\t: {filtered_list[chosen_index]['login']['username']}")
print(f"password\t: \033[34m\033[44m{filtered_list[chosen_index]['login']['password']}\033[0m")
if filtered_list[chosen_index]['login']['totp']: if filtered_list[chosen_index]['login']['totp']:
print("Todo, fix OTP") totp = pyotp.parse_uri(filtered_list[chosen_index]['login']['totp'])
print(f"TOTP\t\t: {totp.now()}")
try:
import pyperclip
except ImportError:
print("\nHint: install pyperclip to automatically copy the password to the clipboard.")
else:
pyperclip.copy(filtered_list[chosen_index]['login']['password'])
print("\nPassword has been copied to the clipboard.")
else: else:
print("No item found") print("No item found")
exit(1) exit(1)