import os.path from contextlib import contextmanager from typing import Any, Generator import keyring from injectable import injectable from pykeepass import PyKeePass, create_database class KeyRequest: def __init__(self, prompt: str): self.field_name = None self.entry_name = None self.path = None self._parse_prompt(prompt) def _parse_prompt(self, prompt: str): prompt_parts = prompt.split("/") key = None match len(prompt_parts): case 1: self.field_name = 'password' key = prompt_parts[0] case 2: self.field_name = prompt_parts[1] key = prompt_parts[0] case _: key = None if key is None: return key_parts = key.split(".") self.path = key_parts[:] if len(key_parts) > 1 else None self.entry_name = key_parts[-1] @injectable(singleton=True) class Passwords: def __init__(self): from app.config import get_settings settings = get_settings() with open(settings.kp.secret, "r") as fh: keyring.set_password("karl", "kp", fh.read().splitlines()[0]) self._path = settings.kp.file @contextmanager def open(self, mode: str = "r") -> Generator[PyKeePass | Any, Any, None]: kp = PyKeePass(self._path, password=keyring.get_password("karl", "kp")) \ if os.path.exists(self._path) else create_database(self._path, password=keyring.get_password("karl", "kp")) yield kp if mode == "rw": kp.save() def get_values(self, keys: list[str]) -> dict[str, str]: output = {} for k in keys: request = KeyRequest(k) with self.open() as kp: kp_entry = kp.find_entries(path=request.path, first=True, title=request.entry_name) output[k] = self._get_field_value(kp_entry, request.field_name) return output @staticmethod def _get_field_value(kp_entry, field_name): if kp_entry is None: return None match field_name: case "username": return kp_entry.username case "password": return kp_entry.password case "url": return kp_entry.url case _: return kp_entry.get_custom_property(field_name)