Source code for kdbxtool.templates

"""Entry templates for common account types.

This module provides typed template classes for creating entries with predefined
fields and icons. Templates follow KeePass conventions for icon IDs and provide
full IDE autocompletion for field names.

Example:
    >>> from kdbxtool import Database, Templates
    >>> db = Database.create(password="secret")
    >>> entry = db.root_group.create_entry(
    ...     title="GitHub",
    ...     template=Templates.Login(),
    ...     username="user@example.com",
    ...     password="secret123",
    ...     url="https://github.com",
    ... )
    >>> entry = db.root_group.create_entry(
    ...     title="Visa Card",
    ...     template=Templates.CreditCard(
    ...         card_number="4111111111111111",
    ...         expiry_date="12/25",
    ...         cvv="123",
    ...     ),
    ... )

Custom templates can be created by subclassing EntryTemplate:
    >>> from dataclasses import dataclass
    >>> from typing import ClassVar
    >>> from kdbxtool import EntryTemplate, IconId
    >>>
    >>> @dataclass
    ... class VPNConnection(EntryTemplate):
    ...     _icon_id: ClassVar[int] = IconId.TERMINAL_ENCRYPTED
    ...     _protected_fields: ClassVar[frozenset[str]] = frozenset({"certificate"})
    ...
    ...     server: str | None = None
    ...     protocol: str = "OpenVPN"
    ...     certificate: str | None = None
"""

from __future__ import annotations

from dataclasses import dataclass, fields
from enum import IntEnum
from typing import ClassVar


[docs] class IconId(IntEnum): """KeePass standard icon IDs. These correspond to the PwIcon enumeration in KeePass. There are 69 standard icons (0-68) built into KeePass/KeePassXC. """ KEY = 0 WORLD = 1 WARNING = 2 NETWORK_SERVER = 3 MARKED_DIRECTORY = 4 USER_COMMUNICATION = 5 PARTS = 6 NOTEPAD = 7 WORLD_SOCKET = 8 IDENTITY = 9 PAPER_READY = 10 DIGICAM = 11 IR_COMMUNICATION = 12 MULTI_KEYS = 13 ENERGY = 14 SCANNER = 15 WORLD_STAR = 16 CDROM = 17 MONITOR = 18 EMAIL = 19 CONFIGURATION = 20 CLIPBOARD_READY = 21 PAPER_NEW = 22 SCREEN = 23 ENERGY_CAREFUL = 24 EMAIL_BOX = 25 DISK = 26 DRIVE = 27 PAPER_Q = 28 TERMINAL_ENCRYPTED = 29 CONSOLE = 30 PRINTER = 31 PROGRAM_ICONS = 32 RUN = 33 SETTINGS = 34 WORLD_COMPUTER = 35 ARCHIVE = 36 HOMEBANKING = 37 DRIVE_WINDOWS = 38 CLOCK = 39 EMAIL_SEARCH = 40 PAPER_FLAG = 41 MEMORY = 42 TRASH_BIN = 43 NOTE = 44 EXPIRED = 45 INFO = 46 PACKAGE = 47 FOLDER = 48 FOLDER_OPEN = 49 FOLDER_PACKAGE = 50 LOCK_OPEN = 51 PAPER_LOCKED = 52 CHECKED = 53 PEN = 54 THUMBNAIL = 55 BOOK = 56 LIST = 57 USER_KEY = 58 TOOL = 59 HOME = 60 STAR = 61 TUX = 62 FEATHER = 63 APPLE = 64 WIKI = 65 MONEY = 66 CERTIFICATE = 67 BLACKBERRY = 68
[docs] @dataclass class EntryTemplate: """Base class for entry templates. Subclass to create custom templates. Class variables to override: _icon_id: Icon for entries using this template (default: KEY) _include_standard: Whether to populate username/password/url (default: True) _protected_fields: Field names that should be memory-protected Example: >>> @dataclass ... class VPNConnection(EntryTemplate): ... _icon_id: ClassVar[int] = IconId.TERMINAL_ENCRYPTED ... _protected_fields: ClassVar[frozenset[str]] = frozenset({"certificate"}) ... ... server: str | None = None ... protocol: str = "OpenVPN" ... certificate: str | None = None ... >>> entry = group.create_entry( ... title="Work VPN", ... template=VPNConnection(server="vpn.company.com"), ... ) """ _icon_id: ClassVar[int] = IconId.KEY _include_standard: ClassVar[bool] = True _protected_fields: ClassVar[frozenset[str]] = frozenset() def _get_fields(self) -> dict[str, tuple[str | None, bool]]: """Return template field values with display names and protection status. Returns: Dict mapping display name to (value, is_protected) tuple. Display names are derived from field names by replacing underscores with spaces and title-casing. """ result: dict[str, tuple[str | None, bool]] = {} for field in fields(self): if field.name.startswith("_"): continue # Skip class variables value = getattr(self, field.name) display_name = field.name.replace("_", " ").title() is_protected = field.name in self._protected_fields result[display_name] = (value, is_protected) return result
# --- Built-in Templates ---
[docs] @dataclass class Login(EntryTemplate): """Standard login entry template. Uses standard fields (title, username, password, url) without additional custom fields. """
[docs] @dataclass class CreditCard(EntryTemplate): """Credit card entry template. Includes card number, expiry date, CVV, cardholder name, and PIN. Sensitive fields (card_number, cvv, pin) are memory-protected. Does not use standard username/password fields. """ _icon_id: ClassVar[int] = IconId.MONEY _include_standard: ClassVar[bool] = False _protected_fields: ClassVar[frozenset[str]] = frozenset({"card_number", "cvv", "pin"}) card_number: str | None = None expiry_date: str | None = None cvv: str | None = None cardholder_name: str | None = None pin: str | None = None
[docs] @dataclass class SecureNote(EntryTemplate): """Secure note entry template. For storing text without standard credential fields. Use the notes parameter in create_entry() for content. """ _icon_id: ClassVar[int] = IconId.NOTE _include_standard: ClassVar[bool] = False
[docs] @dataclass class Identity(EntryTemplate): """Identity/personal information entry template. Includes name, contact, and address fields. Does not use standard username/password fields. """ _icon_id: ClassVar[int] = IconId.IDENTITY _include_standard: ClassVar[bool] = False first_name: str | None = None last_name: str | None = None email: str | None = None phone: str | None = None address: str | None = None city: str | None = None state: str | None = None postal_code: str | None = None country: str | None = None
[docs] @dataclass class BankAccount(EntryTemplate): """Bank account entry template. Includes account details and routing information. Sensitive fields (account_number, iban, pin) are memory-protected. Does not use standard username/password fields. """ _icon_id: ClassVar[int] = IconId.HOMEBANKING _include_standard: ClassVar[bool] = False _protected_fields: ClassVar[frozenset[str]] = frozenset({"account_number", "iban", "pin"}) bank_name: str | None = None account_type: str | None = None account_number: str | None = None routing_number: str | None = None swift_bic: str | None = None iban: str | None = None pin: str | None = None
[docs] @dataclass class Server(EntryTemplate): """Server/SSH entry template. Includes hostname, port, and SSH key fields. Uses standard username/password fields for credentials. SSH key is memory-protected. """ _icon_id: ClassVar[int] = IconId.CONSOLE _protected_fields: ClassVar[frozenset[str]] = frozenset({"ssh_key"}) hostname: str | None = None port: str = "22" ssh_key: str | None = None
[docs] @dataclass class WirelessRouter(EntryTemplate): """Wireless router entry template. Includes SSID, security type, and admin credentials. Admin password is memory-protected. """ _icon_id: ClassVar[int] = IconId.IR_COMMUNICATION _protected_fields: ClassVar[frozenset[str]] = frozenset({"admin_password"}) ssid: str | None = None security_type: str = "WPA2" admin_url: str | None = None admin_username: str | None = None admin_password: str | None = None
[docs] @dataclass class Email(EntryTemplate): """Email account entry template. Includes email address and server settings. Uses standard username/password fields for credentials. """ _icon_id: ClassVar[int] = IconId.EMAIL email_address: str | None = None imap_server: str | None = None imap_port: str = "993" smtp_server: str | None = None smtp_port: str = "587"
[docs] @dataclass class SoftwareLicense(EntryTemplate): """Software license entry template. Includes license key and registration information. License key is memory-protected. Does not use standard username/password fields. """ _icon_id: ClassVar[int] = IconId.PACKAGE _include_standard: ClassVar[bool] = False _protected_fields: ClassVar[frozenset[str]] = frozenset({"license_key"}) license_key: str | None = None registered_email: str | None = None registered_name: str | None = None purchase_date: str | None = None download_url: str | None = None
[docs] @dataclass class DatabaseConnection(EntryTemplate): """Database connection entry template. Includes database type, host, port, and connection details. Connection string is memory-protected. Uses standard username/password fields for credentials. """ _icon_id: ClassVar[int] = IconId.DRIVE _protected_fields: ClassVar[frozenset[str]] = frozenset({"connection_string"}) database_type: str = "PostgreSQL" host: str | None = None port: str = "5432" database_name: str | None = None connection_string: str | None = None
[docs] class Templates: """Namespace containing all built-in entry templates. Provides discoverability via IDE autocompletion. Usage: >>> from kdbxtool import Templates >>> >>> entry = group.create_entry( ... title="My Card", ... template=Templates.CreditCard( ... card_number="4111111111111111", ... cvv="123", ... ), ... ) Available templates: - Login: Standard login (username, password, url) - CreditCard: Card number, expiry, CVV, cardholder, PIN - SecureNote: Notes-only entry - Identity: Personal information (name, address, etc.) - BankAccount: Account and routing numbers - Server: Hostname, port, SSH key - WirelessRouter: SSID, security type, admin credentials - Email: Email address and server settings - SoftwareLicense: License key and registration info - Database: Database connection details """ Login = Login CreditCard = CreditCard SecureNote = SecureNote Identity = Identity BankAccount = BankAccount Server = Server WirelessRouter = WirelessRouter Email = Email SoftwareLicense = SoftwareLicense Database = DatabaseConnection