From 7391701820148f3ade682b94e96e4c93dd871f1e Mon Sep 17 00:00:00 2001 From: Bernd Gempel Date: Mon, 20 Nov 2023 08:07:24 +0100 Subject: [PATCH] Add support to set or get AutoType/Association/Window --- .gitignore | 2 ++ README.rst | 2 +- pykeepass/entry.py | 20 ++++++++++++++++++-- pykeepass/xpath.py | 2 ++ tests/test3.kdbx | Bin 4142 -> 5390 bytes tests/tests.py | 7 +++++-- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 73cf0b10..10fde9b5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ dist/ *.egg-info/ build/ *.xml +Pipfile Pipfile.lock *.kdbx *.kdbx.out +.idea diff --git a/README.rst b/README.rst index bcd156fd..bcdce39b 100644 --- a/README.rst +++ b/README.rst @@ -66,7 +66,7 @@ Finding Entries **find_entries** (title=None, username=None, password=None, url=None, notes=None, otp=None, path=None, uuid=None, tags=None, string=None, group=None, recursive=True, regex=False, flags=None, history=False, first=False) -Returns entries which match all provided parameters, where ``title``, ``username``, ``password``, ``url``, ``notes``, ``otp``, and ``autotype_sequence`` are strings, ``path`` is a list, ``string`` is a dict, ``autotype_enabled`` is a boolean, ``uuid`` is a ``uuid.UUID`` and ``tags`` is a list of strings. This function has optional ``regex`` boolean and ``flags`` string arguments, which means to interpret search strings as `XSLT style`_ regular expressions with `flags`_. +Returns entries which match all provided parameters, where ``title``, ``username``, ``password``, ``url``, ``notes``, ``otp``, ``autotype_window`` and ``autotype_sequence`` are strings, ``path`` is a list, ``string`` is a dict, ``autotype_enabled`` is a boolean, ``uuid`` is a ``uuid.UUID`` and ``tags`` is a list of strings. This function has optional ``regex`` boolean and ``flags`` string arguments, which means to interpret search strings as `XSLT style`_ regular expressions with `flags`_. .. _XSLT style: https://www.xml.com/pub/a/2003/06/04/tr.html .. _flags: https://www.w3.org/TR/xpath-functions/#flags diff --git a/pykeepass/entry.py b/pykeepass/entry.py index 2ed59c5a..51ebc886 100644 --- a/pykeepass/entry.py +++ b/pykeepass/entry.py @@ -26,7 +26,7 @@ class Entry(BaseElement): def __init__(self, title=None, username=None, password=None, url=None, notes=None, otp=None, tags=None, expires=False, expiry_time=None, - icon=None, autotype_sequence=None, autotype_enabled=True, + icon=None, autotype_sequence=None, autotype_enabled=True, autotype_window=None, element=None, kp=None): self._kp = kp @@ -60,7 +60,11 @@ def __init__(self, title=None, username=None, password=None, url=None, E.AutoType( E.Enabled(str(autotype_enabled)), E.DataTransferObfuscation('0'), - E.DefaultSequence(str(autotype_sequence) if autotype_sequence else '') + E.DefaultSequence(str(autotype_sequence) if autotype_sequence else ''), + E.Association( + E.Window(str(autotype_window) if autotype_window else ''), + E.KeystrokeSequence('') + ) ) ) # FIXME: include custom_properties in constructor @@ -268,6 +272,18 @@ def autotype_sequence(self): def autotype_sequence(self, value): self._element.find('AutoType/DefaultSequence').text = value + @property + def autotype_window(self): + """str: get or set [autotype target window filter](https://keepass.info/help/base/autotype.html#autowindows)""" + sequence = self._element.find('AutoType/Association/Window') + if sequence is None or sequence.text == '': + return None + return sequence.text + + @autotype_window.setter + def autotype_window(self, value): + self._element.find('AutoType/Association/Window').text = value + @property def is_a_history_entry(self): """bool: check if entry is History entry""" diff --git a/pykeepass/xpath.py b/pykeepass/xpath.py index c4e6fea5..3769f338 100644 --- a/pykeepass/xpath.py +++ b/pykeepass/xpath.py @@ -31,6 +31,7 @@ 'tags': '/Tags[text()="{}"]/..', 'string': '/String/Key[text()="{}"]/../Value[text()="{}"]/../..', 'autotype_sequence': '/AutoType/DefaultSequence[text()="{}"]/../..', + 'autotype_window': '/AutoType/Association/Window[text()="{}"]/../../..', 'autotype_enabled': '/AutoType/Enabled[text()="{}"]/../..', 'otp': '/String/Key[text()="otp"]/../Value[text()="{}"]/../..', }, @@ -44,6 +45,7 @@ 'tags': '/Tags[re:test(text(), "{}", "{flags}")]/..', 'string': '/String/Key[text()="{}"]/../Value[re:test(text(), "{}", "{flags}")]/../..', 'autotype_sequence': '/AutoType/DefaultSequence[re:test(text(), "{}", "{flags}")]/../..', + 'autotype_window': '/AutoType/Association/Window[re:test(text(), "{}", "{flags}")]/../../..', 'autotype_enabled': '/AutoType/Enabled[re:test(text(), "{}", "{flags}")]/../..', 'otp': '/String/Key[text()="otp"]/../Value[re:test(text(), "{}", "{flags}")]/../..', } diff --git a/tests/test3.kdbx b/tests/test3.kdbx index ab02bbf7b802875c5dc90eec43f4235cb549c46c..e61a2299e04f83834d99f683bda8e43d8c960ca4 100644 GIT binary patch literal 5390 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 delta 4140 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 diff --git a/tests/tests.py b/tests/tests.py index 4c574bae..99f56dd3 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -8,7 +8,6 @@ from datetime import datetime, timedelta from dateutil import tz -from lxml.etree import Element from pathlib import Path from io import BytesIO @@ -16,7 +15,6 @@ from pykeepass import PyKeePass, icons from pykeepass.entry import Entry from pykeepass.group import Group -from pykeepass.kdbx_parsing import KDBX from pykeepass.exceptions import BinaryError, CredentialsError, HeaderChecksumError """ @@ -144,6 +142,11 @@ def test_find_entries_by_autotype_sequence(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].autotype_sequence, '{USERNAME}{TAB}{PASSWORD}{ENTER}') + def test_find_entries_by_autotype_window(self): + results = self.kp.find_entries(autotype_window='test', regex=True, flags="i") + self.assertEqual(len(results), 1) + self.assertEqual(results[0].autotype_window, 'TEST') + def test_find_entries_by_autotype_enabled(self): results = self.kp.find_entries(autotype_enabled=True) self.assertEqual(len(results), len(self.kp.entries) - 1)