Initial commit

This commit is contained in:
2025-07-13 21:07:01 +02:00
commit 5b566a4529
6 changed files with 126 additions and 0 deletions

0
README.md Normal file
View File

28
pyproject.toml Normal file
View File

@@ -0,0 +1,28 @@
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"
[project]
name = "bw-get"
version = "0.0.1"
authors = [
{name="Fabian van Koppen", email="f@bianvk.nl"}
]
description = "Wrapper script voor de Bitwarden CLI"
readme = "README.md"
requires-python = ">=3.9"
classifiers = [
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
dependencies = [
"questionary",
"pyotp",
"pyperclip",
"typer",
"python-gnupg",
"typing"
]
[project.scripts]
bw-get = "bw_get.cli:app"

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
questionary
pyotp
pyperclip
typer

3
src/bw_get/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
if __name__ == "__main__":
from bw_get.cli import app
app()

82
src/bw_get/bw_get.py Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python
import json
import gnupg
import questionary
import pyperclip
import argparse
import typer
from typing_extensions import Annotated
from sys import exit
from os import environ
from subprocess import run, DEVNULL
home = environ.get('HOME')
gpg = gnupg.GPG(gnupghome=f"{home}/.gnupg", use_agent=True)
def check_lock() -> bool:
check = run(['bw', 'unlock', '--check'], stdout=DEVNULL, stderr=DEVNULL)
if check.returncode != 0:
return False
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():
with open(f"{home}/.secret/bw-master.txt.gpg", 'rb') as f:
password = str(gpg.decrypt_file(f)).strip()
unlock_command = run(['bw', 'unlock', password, '--raw'], capture_output=True, text=True)
environ["BW_SESSION"] = str(unlock_command.stdout)
if check_lock() is False:
print("Something went wrong unlocking the vault")
exit(1)
def get_password(search_string: Annotated[str, typer.Argument(help="The term to search for in Vaultwarden")] = ""):
decrypt_session()
if check_lock() is False:
new_session()
list_items_cmd = run(['bw', 'list', 'items'], capture_output=True)
items = json.loads(list_items_cmd.stdout)
# Filtering the list based on the search string
filtered_list = [
d for d in items if any(
search_string in str(value) for key, value in d.items()
if key != 'login' # Exclude the 'login' key entirely
) or (
'login' in d and (
search_string in str(d['login'].get('username', '')) or
search_string in str(d['login'].get('password', ''))
)
)
]
choices = []
if len(filtered_list) > 1:
for item_index, item in enumerate(filtered_list):
choices.append(questionary.Choice(f"{item['name']} ({item['login']['username']})", item_index))
chosen_index = questionary.select("Select item", choices, use_shortcuts=True).ask()
elif (filtered_list) == 1:
chosen_index = 0
else: chosen_index = None
if chosen_index is not None:
pyperclip.copy(filtered_list[chosen_index]['login']['password'])
if filtered_list[chosen_index]['login']['totp']:
print("Todo, fix OTP")
else:
print("No item found")
exit(1)

9
src/bw_get/cli.py Normal file
View File

@@ -0,0 +1,9 @@
import typer
from .bw_get import get_password
app = typer.Typer()
app.command()(get_password)
if __name__ == "__main__":
app()