Bump version to 1.1.0 and enhance TOTP handling

- Updated project version from 1.0.1 to 1.1.0 in pyproject.toml.
- Added `parse_totp` function for improved TOTP parsing and error handling.
- Refined item filtering to include only items of type 1 (logins).
- Enhanced username display logic in item selection.
This commit is contained in:
2025-07-13 23:59:48 +02:00
parent ccc2f92eed
commit ef2fdac599
2 changed files with 37 additions and 13 deletions

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "bw-get" name = "bw-get"
version = "1.0.1" version = "1.1.0"
authors = [ authors = [
{name="Fabian van Koppen", email="f@bianvk.nl"} {name="Fabian van Koppen", email="f@bianvk.nl"}
] ]

View File

@@ -12,6 +12,21 @@ from subprocess import run, DEVNULL
home = environ.get('HOME') home = environ.get('HOME')
def parse_totp(totp: str) -> None|str:
try:
# Attempt to parse the TOTP URI
totp = pyotp.parse_uri(totp).now()
except Exception as e:
try:
# Attempt to create a TOTP object using the TOTP function
totp = pyotp.TOTP(totp).now()
except Exception as e:
# Return False if both attempts fail
totp = None
return totp
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)
@@ -37,15 +52,19 @@ def get_password(search_string: Annotated[str, typer.Argument(help="The term to
items = json.loads(list_items_cmd.stdout) items = json.loads(list_items_cmd.stdout)
search_string_lower = search_string.lower()
# Filtering the list based on the search string # Filtering the list based on the search string
filtered_list = [ filtered_list = [
d for d in items if any( d for d in items
search_string in str(value) for key, value in d.items() if d.get('type') == 1 and ( # Only passwords
any(
search_string_lower in str(value).lower() for key, value in d.items()
if key != 'login' # Exclude the 'login' key entirely if key != 'login' # Exclude the 'login' key entirely
) or ( ) or (
'login' in d and ( 'login' in d and (
search_string in str(d['login'].get('username', '')) or search_string_lower in str(d['login'].get('username', '')).lower() or
search_string in str(d['login'].get('password', '')) search_string_lower in str(d['login'].get('password', '')).lower()
)
) )
) )
] ]
@@ -54,7 +73,12 @@ def get_password(search_string: Annotated[str, typer.Argument(help="The term to
if len(filtered_list) > 1: if len(filtered_list) > 1:
for item_index, item in enumerate(filtered_list): for item_index, item in enumerate(filtered_list):
choices.append(questionary.Choice(f"{item['name']} ({item['login']['username']})", item_index)) if item.get('login') and item['login'].get('username'):
username = f" ({item['login']['username']})"
else:
username = ""
choices.append(questionary.Choice(f"{item['name']}{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 len(filtered_list) == 1: elif len(filtered_list) == 1:
@@ -73,9 +97,9 @@ def get_password(search_string: Annotated[str, typer.Argument(help="The term to
print(f"username\t: {filtered_list[chosen_index]['login']['username']}") 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") 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'] and parse_totp(filtered_list[chosen_index]['login']['totp']):
totp = pyotp.parse_uri(filtered_list[chosen_index]['login']['totp']) totp = parse_totp(filtered_list[chosen_index]['login']['totp'])
print(f"TOTP\t\t: {totp.now()}") print(f"TOTP\t\t: {totp}")
try: try:
import pyperclip import pyperclip