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

1"""Entry templates for common account types. 

2 

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. 

6 

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 ... ) 

25 

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""" 

40 

41from __future__ import annotations 

42 

43from dataclasses import dataclass, fields 

44from enum import IntEnum 

45from typing import ClassVar 

46 

47 

48class IconId(IntEnum): 

49 """KeePass standard icon IDs. 

50 

51 These correspond to the PwIcon enumeration in KeePass. There are 69 

52 standard icons (0-68) built into KeePass/KeePassXC. 

53 """ 

54 

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 

124 

125 

126@dataclass 

127class EntryTemplate: 

128 """Base class for entry templates. Subclass to create custom templates. 

129 

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 

134 

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 """ 

150 

151 _icon_id: ClassVar[int] = IconId.KEY 

152 _include_standard: ClassVar[bool] = True 

153 _protected_fields: ClassVar[frozenset[str]] = frozenset() 

154 

155 def _get_fields(self) -> dict[str, tuple[str | None, bool]]: 

156 """Return template field values with display names and protection status. 

157 

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 

172 

173 

174# --- Built-in Templates --- 

175 

176 

177@dataclass 

178class Login(EntryTemplate): 

179 """Standard login entry template. 

180 

181 Uses standard fields (title, username, password, url) without 

182 additional custom fields. 

183 """ 

184 

185 

186@dataclass 

187class CreditCard(EntryTemplate): 

188 """Credit card entry template. 

189 

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 """ 

194 

195 _icon_id: ClassVar[int] = IconId.MONEY 

196 _include_standard: ClassVar[bool] = False 

197 _protected_fields: ClassVar[frozenset[str]] = frozenset({"card_number", "cvv", "pin"}) 

198 

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 

204 

205 

206@dataclass 

207class SecureNote(EntryTemplate): 

208 """Secure note entry template. 

209 

210 For storing text without standard credential fields. 

211 Use the notes parameter in create_entry() for content. 

212 """ 

213 

214 _icon_id: ClassVar[int] = IconId.NOTE 

215 _include_standard: ClassVar[bool] = False 

216 

217 

218@dataclass 

219class Identity(EntryTemplate): 

220 """Identity/personal information entry template. 

221 

222 Includes name, contact, and address fields. 

223 Does not use standard username/password fields. 

224 """ 

225 

226 _icon_id: ClassVar[int] = IconId.IDENTITY 

227 _include_standard: ClassVar[bool] = False 

228 

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 

238 

239 

240@dataclass 

241class BankAccount(EntryTemplate): 

242 """Bank account entry template. 

243 

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 """ 

248 

249 _icon_id: ClassVar[int] = IconId.HOMEBANKING 

250 _include_standard: ClassVar[bool] = False 

251 _protected_fields: ClassVar[frozenset[str]] = frozenset({"account_number", "iban", "pin"}) 

252 

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 

260 

261 

262@dataclass 

263class Server(EntryTemplate): 

264 """Server/SSH entry template. 

265 

266 Includes hostname, port, and SSH key fields. 

267 Uses standard username/password fields for credentials. 

268 SSH key is memory-protected. 

269 """ 

270 

271 _icon_id: ClassVar[int] = IconId.CONSOLE 

272 _protected_fields: ClassVar[frozenset[str]] = frozenset({"ssh_key"}) 

273 

274 hostname: str | None = None 

275 port: str = "22" 

276 ssh_key: str | None = None 

277 

278 

279@dataclass 

280class WirelessRouter(EntryTemplate): 

281 """Wireless router entry template. 

282 

283 Includes SSID, security type, and admin credentials. 

284 Admin password is memory-protected. 

285 """ 

286 

287 _icon_id: ClassVar[int] = IconId.IR_COMMUNICATION 

288 _protected_fields: ClassVar[frozenset[str]] = frozenset({"admin_password"}) 

289 

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 

295 

296 

297@dataclass 

298class Email(EntryTemplate): 

299 """Email account entry template. 

300 

301 Includes email address and server settings. 

302 Uses standard username/password fields for credentials. 

303 """ 

304 

305 _icon_id: ClassVar[int] = IconId.EMAIL 

306 

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" 

312 

313 

314@dataclass 

315class SoftwareLicense(EntryTemplate): 

316 """Software license entry template. 

317 

318 Includes license key and registration information. 

319 License key is memory-protected. 

320 Does not use standard username/password fields. 

321 """ 

322 

323 _icon_id: ClassVar[int] = IconId.PACKAGE 

324 _include_standard: ClassVar[bool] = False 

325 _protected_fields: ClassVar[frozenset[str]] = frozenset({"license_key"}) 

326 

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 

332 

333 

334@dataclass 

335class DatabaseConnection(EntryTemplate): 

336 """Database connection entry template. 

337 

338 Includes database type, host, port, and connection details. 

339 Connection string is memory-protected. 

340 Uses standard username/password fields for credentials. 

341 """ 

342 

343 _icon_id: ClassVar[int] = IconId.DRIVE 

344 _protected_fields: ClassVar[frozenset[str]] = frozenset({"connection_string"}) 

345 

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 

351 

352 

353class Templates: 

354 """Namespace containing all built-in entry templates. 

355 

356 Provides discoverability via IDE autocompletion. 

357 

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 ... ) 

368 

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 """ 

381 

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