Coverage for src / kdbxtool / templates.py: 99%
184 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-19 21:22 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-19 21:22 +0000
1"""Entry templates for common account types.
3This module provides typed template classes for creating entries with predefined
4fields and icons. Templates follow KeePass conventions for icon IDs and provide
5full IDE autocompletion for field names.
7Example:
8 >>> from kdbxtool import Database, Templates
9 >>> db = Database.create(password="secret")
10 >>> entry = db.root_group.create_entry(
11 ... title="GitHub",
12 ... template=Templates.Login(),
13 ... username="user@example.com",
14 ... password="secret123",
15 ... url="https://github.com",
16 ... )
17 >>> entry = db.root_group.create_entry(
18 ... title="Visa Card",
19 ... template=Templates.CreditCard(
20 ... card_number="4111111111111111",
21 ... expiry_date="12/25",
22 ... cvv="123",
23 ... ),
24 ... )
26Custom templates can be created by subclassing EntryTemplate:
27 >>> from dataclasses import dataclass
28 >>> from typing import ClassVar
29 >>> from kdbxtool import EntryTemplate, IconId
30 >>>
31 >>> @dataclass
32 ... class VPNConnection(EntryTemplate):
33 ... _icon_id: ClassVar[int] = IconId.TERMINAL_ENCRYPTED
34 ... _protected_fields: ClassVar[frozenset[str]] = frozenset({"certificate"})
35 ...
36 ... server: str | None = None
37 ... protocol: str = "OpenVPN"
38 ... certificate: str | None = None
39"""
41from __future__ import annotations
43from dataclasses import dataclass, fields
44from enum import IntEnum
45from typing import ClassVar
48class IconId(IntEnum):
49 """KeePass standard icon IDs.
51 These correspond to the PwIcon enumeration in KeePass. There are 69
52 standard icons (0-68) built into KeePass/KeePassXC.
53 """
55 KEY = 0
56 WORLD = 1
57 WARNING = 2
58 NETWORK_SERVER = 3
59 MARKED_DIRECTORY = 4
60 USER_COMMUNICATION = 5
61 PARTS = 6
62 NOTEPAD = 7
63 WORLD_SOCKET = 8
64 IDENTITY = 9
65 PAPER_READY = 10
66 DIGICAM = 11
67 IR_COMMUNICATION = 12
68 MULTI_KEYS = 13
69 ENERGY = 14
70 SCANNER = 15
71 WORLD_STAR = 16
72 CDROM = 17
73 MONITOR = 18
74 EMAIL = 19
75 CONFIGURATION = 20
76 CLIPBOARD_READY = 21
77 PAPER_NEW = 22
78 SCREEN = 23
79 ENERGY_CAREFUL = 24
80 EMAIL_BOX = 25
81 DISK = 26
82 DRIVE = 27
83 PAPER_Q = 28
84 TERMINAL_ENCRYPTED = 29
85 CONSOLE = 30
86 PRINTER = 31
87 PROGRAM_ICONS = 32
88 RUN = 33
89 SETTINGS = 34
90 WORLD_COMPUTER = 35
91 ARCHIVE = 36
92 HOMEBANKING = 37
93 DRIVE_WINDOWS = 38
94 CLOCK = 39
95 EMAIL_SEARCH = 40
96 PAPER_FLAG = 41
97 MEMORY = 42
98 TRASH_BIN = 43
99 NOTE = 44
100 EXPIRED = 45
101 INFO = 46
102 PACKAGE = 47
103 FOLDER = 48
104 FOLDER_OPEN = 49
105 FOLDER_PACKAGE = 50
106 LOCK_OPEN = 51
107 PAPER_LOCKED = 52
108 CHECKED = 53
109 PEN = 54
110 THUMBNAIL = 55
111 BOOK = 56
112 LIST = 57
113 USER_KEY = 58
114 TOOL = 59
115 HOME = 60
116 STAR = 61
117 TUX = 62
118 FEATHER = 63
119 APPLE = 64
120 WIKI = 65
121 MONEY = 66
122 CERTIFICATE = 67
123 BLACKBERRY = 68
126@dataclass
127class EntryTemplate:
128 """Base class for entry templates. Subclass to create custom templates.
130 Class variables to override:
131 _icon_id: Icon for entries using this template (default: KEY)
132 _include_standard: Whether to populate username/password/url (default: True)
133 _protected_fields: Field names that should be memory-protected
135 Example:
136 >>> @dataclass
137 ... class VPNConnection(EntryTemplate):
138 ... _icon_id: ClassVar[int] = IconId.TERMINAL_ENCRYPTED
139 ... _protected_fields: ClassVar[frozenset[str]] = frozenset({"certificate"})
140 ...
141 ... server: str | None = None
142 ... protocol: str = "OpenVPN"
143 ... certificate: str | None = None
144 ...
145 >>> entry = group.create_entry(
146 ... title="Work VPN",
147 ... template=VPNConnection(server="vpn.company.com"),
148 ... )
149 """
151 _icon_id: ClassVar[int] = IconId.KEY
152 _include_standard: ClassVar[bool] = True
153 _protected_fields: ClassVar[frozenset[str]] = frozenset()
155 def _get_fields(self) -> dict[str, tuple[str | None, bool]]:
156 """Return template field values with display names and protection status.
158 Returns:
159 Dict mapping display name to (value, is_protected) tuple.
160 Display names are derived from field names by replacing underscores
161 with spaces and title-casing.
162 """
163 result: dict[str, tuple[str | None, bool]] = {}
164 for field in fields(self):
165 if field.name.startswith("_"):
166 continue # Skip class variables
167 value = getattr(self, field.name)
168 display_name = field.name.replace("_", " ").title()
169 is_protected = field.name in self._protected_fields
170 result[display_name] = (value, is_protected)
171 return result
174# --- Built-in Templates ---
177@dataclass
178class Login(EntryTemplate):
179 """Standard login entry template.
181 Uses standard fields (title, username, password, url) without
182 additional custom fields.
183 """
186@dataclass
187class CreditCard(EntryTemplate):
188 """Credit card entry template.
190 Includes card number, expiry date, CVV, cardholder name, and PIN.
191 Sensitive fields (card_number, cvv, pin) are memory-protected.
192 Does not use standard username/password fields.
193 """
195 _icon_id: ClassVar[int] = IconId.MONEY
196 _include_standard: ClassVar[bool] = False
197 _protected_fields: ClassVar[frozenset[str]] = frozenset({"card_number", "cvv", "pin"})
199 card_number: str | None = None
200 expiry_date: str | None = None
201 cvv: str | None = None
202 cardholder_name: str | None = None
203 pin: str | None = None
206@dataclass
207class SecureNote(EntryTemplate):
208 """Secure note entry template.
210 For storing text without standard credential fields.
211 Use the notes parameter in create_entry() for content.
212 """
214 _icon_id: ClassVar[int] = IconId.NOTE
215 _include_standard: ClassVar[bool] = False
218@dataclass
219class Identity(EntryTemplate):
220 """Identity/personal information entry template.
222 Includes name, contact, and address fields.
223 Does not use standard username/password fields.
224 """
226 _icon_id: ClassVar[int] = IconId.IDENTITY
227 _include_standard: ClassVar[bool] = False
229 first_name: str | None = None
230 last_name: str | None = None
231 email: str | None = None
232 phone: str | None = None
233 address: str | None = None
234 city: str | None = None
235 state: str | None = None
236 postal_code: str | None = None
237 country: str | None = None
240@dataclass
241class BankAccount(EntryTemplate):
242 """Bank account entry template.
244 Includes account details and routing information.
245 Sensitive fields (account_number, iban, pin) are memory-protected.
246 Does not use standard username/password fields.
247 """
249 _icon_id: ClassVar[int] = IconId.HOMEBANKING
250 _include_standard: ClassVar[bool] = False
251 _protected_fields: ClassVar[frozenset[str]] = frozenset({"account_number", "iban", "pin"})
253 bank_name: str | None = None
254 account_type: str | None = None
255 account_number: str | None = None
256 routing_number: str | None = None
257 swift_bic: str | None = None
258 iban: str | None = None
259 pin: str | None = None
262@dataclass
263class Server(EntryTemplate):
264 """Server/SSH entry template.
266 Includes hostname, port, and SSH key fields.
267 Uses standard username/password fields for credentials.
268 SSH key is memory-protected.
269 """
271 _icon_id: ClassVar[int] = IconId.CONSOLE
272 _protected_fields: ClassVar[frozenset[str]] = frozenset({"ssh_key"})
274 hostname: str | None = None
275 port: str = "22"
276 ssh_key: str | None = None
279@dataclass
280class WirelessRouter(EntryTemplate):
281 """Wireless router entry template.
283 Includes SSID, security type, and admin credentials.
284 Admin password is memory-protected.
285 """
287 _icon_id: ClassVar[int] = IconId.IR_COMMUNICATION
288 _protected_fields: ClassVar[frozenset[str]] = frozenset({"admin_password"})
290 ssid: str | None = None
291 security_type: str = "WPA2"
292 admin_url: str | None = None
293 admin_username: str | None = None
294 admin_password: str | None = None
297@dataclass
298class Email(EntryTemplate):
299 """Email account entry template.
301 Includes email address and server settings.
302 Uses standard username/password fields for credentials.
303 """
305 _icon_id: ClassVar[int] = IconId.EMAIL
307 email_address: str | None = None
308 imap_server: str | None = None
309 imap_port: str = "993"
310 smtp_server: str | None = None
311 smtp_port: str = "587"
314@dataclass
315class SoftwareLicense(EntryTemplate):
316 """Software license entry template.
318 Includes license key and registration information.
319 License key is memory-protected.
320 Does not use standard username/password fields.
321 """
323 _icon_id: ClassVar[int] = IconId.PACKAGE
324 _include_standard: ClassVar[bool] = False
325 _protected_fields: ClassVar[frozenset[str]] = frozenset({"license_key"})
327 license_key: str | None = None
328 registered_email: str | None = None
329 registered_name: str | None = None
330 purchase_date: str | None = None
331 download_url: str | None = None
334@dataclass
335class DatabaseConnection(EntryTemplate):
336 """Database connection entry template.
338 Includes database type, host, port, and connection details.
339 Connection string is memory-protected.
340 Uses standard username/password fields for credentials.
341 """
343 _icon_id: ClassVar[int] = IconId.DRIVE
344 _protected_fields: ClassVar[frozenset[str]] = frozenset({"connection_string"})
346 database_type: str = "PostgreSQL"
347 host: str | None = None
348 port: str = "5432"
349 database_name: str | None = None
350 connection_string: str | None = None
353class Templates:
354 """Namespace containing all built-in entry templates.
356 Provides discoverability via IDE autocompletion.
358 Usage:
359 >>> from kdbxtool import Templates
360 >>>
361 >>> entry = group.create_entry(
362 ... title="My Card",
363 ... template=Templates.CreditCard(
364 ... card_number="4111111111111111",
365 ... cvv="123",
366 ... ),
367 ... )
369 Available templates:
370 - Login: Standard login (username, password, url)
371 - CreditCard: Card number, expiry, CVV, cardholder, PIN
372 - SecureNote: Notes-only entry
373 - Identity: Personal information (name, address, etc.)
374 - BankAccount: Account and routing numbers
375 - Server: Hostname, port, SSH key
376 - WirelessRouter: SSID, security type, admin credentials
377 - Email: Email address and server settings
378 - SoftwareLicense: License key and registration info
379 - Database: Database connection details
380 """
382 Login = Login
383 CreditCard = CreditCard
384 SecureNote = SecureNote
385 Identity = Identity
386 BankAccount = BankAccount
387 Server = Server
388 WirelessRouter = WirelessRouter
389 Email = Email
390 SoftwareLicense = SoftwareLicense
391 Database = DatabaseConnection