Initial commit
This commit is contained in:
28
pyproject.toml
Normal file
28
pyproject.toml
Normal 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
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
questionary
|
||||||
|
pyotp
|
||||||
|
pyperclip
|
||||||
|
typer
|
||||||
3
src/bw_get/__init__.py
Normal file
3
src/bw_get/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
if __name__ == "__main__":
|
||||||
|
from bw_get.cli import app
|
||||||
|
app()
|
||||||
82
src/bw_get/bw_get.py
Executable file
82
src/bw_get/bw_get.py
Executable 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
9
src/bw_get/cli.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import typer
|
||||||
|
|
||||||
|
from .bw_get import get_password
|
||||||
|
|
||||||
|
app = typer.Typer()
|
||||||
|
app.command()(get_password)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app()
|
||||||
Reference in New Issue
Block a user