Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pykeepass for openSUSE:Factory checked in at 2022-06-25 10:24:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pykeepass (Old) and /work/SRC/openSUSE:Factory/.python-pykeepass.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pykeepass" Sat Jun 25 10:24:27 2022 rev:8 rq:984909 version:4.0.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pykeepass/python-pykeepass.changes 2022-06-03 14:17:18.597366665 +0200 +++ /work/SRC/openSUSE:Factory/.python-pykeepass.new.1548/python-pykeepass.changes 2022-06-25 10:24:49.922722567 +0200 @@ -1,0 +2,7 @@ +Tue Jun 21 23:10:46 UTC 2022 - Atri Bhattacharya <badshah...@gmail.com> + +- Update to version 4.0.3: + * Add otp support. + * Add debug_setup() function. + +------------------------------------------------------------------- Old: ---- pykeepass-4.0.2.tar.gz New: ---- pykeepass-4.0.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pykeepass.spec ++++++ --- /var/tmp/diff_new_pack.uC6mgW/_old 2022-06-25 10:24:50.682723652 +0200 +++ /var/tmp/diff_new_pack.uC6mgW/_new 2022-06-25 10:24:50.686723658 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pykeepass -Version: 4.0.2 +Version: 4.0.3 Release: 0 Summary: Low-level library to interact with keepass databases License: GPL-3.0-only ++++++ pykeepass-4.0.2.tar.gz -> pykeepass-4.0.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykeepass-4.0.2/CHANGELOG.rst new/pykeepass-4.0.3/CHANGELOG.rst --- old/pykeepass-4.0.2/CHANGELOG.rst 2022-05-22 01:06:31.000000000 +0200 +++ new/pykeepass-4.0.3/CHANGELOG.rst 2022-06-21 18:19:30.000000000 +0200 @@ -1,10 +1,14 @@ -4.0.2 - +4.0.3 - 2022-06-21 +------------------ +- add otp support +- add debug_setup() function + +4.0.2 - 2022-05-21 ------------------ - added support for argon2id key derivation function - added credential expiry functions - fixes #223 - safe saving - 4.0.1 - 2021-05-22 ------------------ - added Entry.delete_history() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykeepass-4.0.2/README.rst new/pykeepass-4.0.3/README.rst --- old/pykeepass-4.0.2/README.rst 2022-05-22 01:06:31.000000000 +0200 +++ new/pykeepass-4.0.3/README.rst 2022-06-21 18:19:30.000000000 +0200 @@ -22,7 +22,7 @@ .. _#pykeepass\:matrix.org: https://matrix.to/#/%23pykeepass:matrix.org Example --------------- +------- .. code:: python from pykeepass import PyKeePass @@ -58,12 +58,15 @@ >>> kp.save() +.. + TODO: add `Entry` and `Group` sections to document attributes of each + Finding Entries ----------------------- +--------------- -**find_entries** (title=None, username=None, password=None, url=None, notes=None, path=None, uuid=None, tags=None, string=None, group=None, recursive=True, regex=False, flags=None, history=False, first=False) +**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``, 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``, 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 @@ -101,14 +104,19 @@ 'facebook.com' >>> entry.title 'foo_entry' + >>> entry.title = 'hello' >>> group = kp.find_group(name='social', first=True) >>> kp.find_entries(title='facebook', group=group, recursive=False, first=True) Entry: "social/facebook (myusername)" + >>> entry.otp + otpauth://totp/test:lkj?secret=TEST%3D%3D%3D%3D&period=30&digits=6&issuer=test + + Finding Groups ----------------------- +-------------- **find_groups** (name=None, path=None, uuid=None, notes=None, group=None, recursive=True, regex=False, flags=None, first=False) @@ -156,7 +164,7 @@ Entry Functions --------------- +--------------- **add_entry** (destination_group, title, username, password, url=None, notes=None, tags=None, expiry_time=None, icon=None, force_creation=False) **delete_entry** (entry) @@ -206,7 +214,7 @@ **empty_group** (group) -delete all entries and subgroups of a group. ``group`` is an instance of ``Group**. +delete all entries and subgroups of a group. ``group`` is an instance of ``Group``. **move_group** (group, destination_group) @@ -360,7 +368,7 @@ where ``filename``, ``password``, and ``keyfile`` are strings. ``filename`` is the path to the database, ``password`` is the master password string, and ``keyfile`` is the path to the database keyfile. At least one of ``password`` and ``keyfile`` is required. Alternatively, the derived key can be supplied directly through ``transformed_key``. -Can raise ``CredentialsError``, ``HeaderChecksumError**, or ``PayloadChecksumError**. +Can raise ``CredentialsError``, ``HeaderChecksumError``, or ``PayloadChecksumError``. **reload** () @@ -402,7 +410,20 @@ pretty print database XML to file -Tests -------------- -To run them issue :code:`python tests/tests.py` +Tests and Debugging +------------------- + +Run tests with :code:`python tests/tests.py` + +Enable debugging when doing tests in console: + + >>> from pykeepass.pykeepass import debug_setup + >>> debug_setup() + >>> kp.entries[0] + DEBUG:pykeepass.pykeepass:xpath query: //Entry + DEBUG:pykeepass.pykeepass:xpath query: (ancestor::Group)[last()] + DEBUG:pykeepass.pykeepass:xpath query: (ancestor::Group)[last()] + DEBUG:pykeepass.pykeepass:xpath query: String/Key[text()="Title"]/../Value + DEBUG:pykeepass.pykeepass:xpath query: String/Key[text()="UserName"]/../Value + Entry: "root_entry (foobar_user)" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykeepass-4.0.2/pykeepass/entry.py new/pykeepass-4.0.3/pykeepass/entry.py --- old/pykeepass-4.0.2/pykeepass/entry.py 2022-05-22 01:06:31.000000000 +0200 +++ new/pykeepass-4.0.3/pykeepass/entry.py 2022-06-21 18:19:30.000000000 +0200 @@ -24,7 +24,8 @@ 'IconID', 'Times', 'History', - 'Notes' + 'Notes', + 'otp' ] # FIXME python2 @@ -32,7 +33,7 @@ class Entry(BaseElement): def __init__(self, title=None, username=None, password=None, url=None, - notes=None, tags=None, expires=False, expiry_time=None, + notes=None, otp=None, tags=None, expires=False, expiry_time=None, icon=None, autotype_sequence=None, autotype_enabled=True, element=None, kp=None): @@ -55,6 +56,8 @@ self._element.append(E.String(E.Key('URL'), E.Value(url))) if notes: self._element.append(E.String(E.Key('Notes'), E.Value(notes))) + if otp: + self._element.append(E.String(E.Key('otp'), E.Value(otp))) if tags: self._element.append( E.Tags(';'.join(tags) if type(tags) is list else tags) @@ -178,6 +181,14 @@ return self._set_subelement_text('Tags', v) @property + def otp(self): + return self._get_string_field('otp') + + @otp.setter + def otp(self, value): + return self._set_string_field('otp', value) + + @property def history(self): if self._element.find('History') is not None: return [HistoryEntry(element=x, kp=self._kp) for x in self._element.find('History').findall('Entry')] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykeepass-4.0.2/pykeepass/pykeepass.py new/pykeepass-4.0.3/pykeepass/pykeepass.py --- old/pykeepass-4.0.2/pykeepass/pykeepass.py 2022-05-22 01:06:31.000000000 +0200 +++ new/pykeepass-4.0.3/pykeepass/pykeepass.py 2022-06-21 18:19:30.000000000 +0200 @@ -365,8 +365,8 @@ for key, value in kwargs.items(): if key not in keys_xp[regex].keys(): raise TypeError('Invalid keyword argument "{}"'.format(key)) - - xp += keys_xp[regex][key].format(value, flags=flags) + if value is not None: + xp += keys_xp[regex][key].format(value, flags=flags) res = self._xpath( xp, @@ -614,7 +614,7 @@ def add_entry(self, destination_group, title, username, password, url=None, notes=None, expiry_time=None, - tags=None, icon=None, force_creation=False): + tags=None, otp=None, icon=None, force_creation=False): entries = self.find_entries( title=title, @@ -637,6 +637,7 @@ username=username, password=password, notes=notes, + otp=otp, url=url, tags=tags, expires=True if expiry_time else False, @@ -911,3 +912,9 @@ keepass_instance.save(transformed_key) return keepass_instance + +def debug_setup(): + """Convenience function to quickly enable debug messages""" + + logging.basicConfig() + logger.setLevel(logging.DEBUG) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykeepass-4.0.2/pykeepass/version.py new/pykeepass-4.0.3/pykeepass/version.py --- old/pykeepass-4.0.2/pykeepass/version.py 2022-05-22 01:06:31.000000000 +0200 +++ new/pykeepass-4.0.3/pykeepass/version.py 2022-06-21 18:19:30.000000000 +0200 @@ -1,3 +1,3 @@ -__version__ = "4.0.2" +__version__ = "4.0.3" __all__= ["__version__"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykeepass-4.0.2/pykeepass/xpath.py new/pykeepass-4.0.3/pykeepass/xpath.py --- old/pykeepass-4.0.2/pykeepass/xpath.py 2022-05-22 01:06:31.000000000 +0200 +++ new/pykeepass-4.0.3/pykeepass/xpath.py 2022-06-21 18:19:30.000000000 +0200 @@ -35,6 +35,7 @@ 'string': '/String/Key[text()="{}"]/../Value[text()="{}"]/../..', 'autotype_sequence': '/AutoType/DefaultSequence[text()="{}"]/../..', 'autotype_enabled': '/AutoType/Enabled[text()="{}"]/../..', + 'otp': '/String/Key[text()="otp"]/../Value[text()="{}"]/../..', }, True: { 'title': '/String/Key[text()="Title"]/../Value[re:test(text(), "{}", "{flags}")]/../..', @@ -47,6 +48,7 @@ 'string': '/String/Key[text()="{}"]/../Value[re:test(text(), "{}", "{flags}")]/../..', 'autotype_sequence': '/AutoType/DefaultSequence[re:test(text(), "{}", "{flags}")]/../..', 'autotype_enabled': '/AutoType/Enabled[re:test(text(), "{}", "{flags}")]/../..', + 'otp': '/String/Key[text()="otp"]/../Value[re:test(text(), "{}", "{flags}")]/../..', } } Binary files old/pykeepass-4.0.2/tests/test3.kdbx and new/pykeepass-4.0.3/tests/test3.kdbx differ Binary files old/pykeepass-4.0.2/tests/test4.kdbx and new/pykeepass-4.0.3/tests/test4.kdbx differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pykeepass-4.0.2/tests/tests.py new/pykeepass-4.0.3/tests/tests.py --- old/pykeepass-4.0.2/tests/tests.py 2022-05-22 01:06:31.000000000 +0200 +++ new/pykeepass-4.0.3/tests/tests.py 2022-06-21 18:19:30.000000000 +0200 @@ -151,6 +151,11 @@ results = self.kp.find_entries(autotype_enabled=True) self.assertEqual(len(results), len(self.kp.entries) - 1) + def test_find_entries_by_otp(self): + results = self.kp.find_entries(otp='otpsecret', regex=True, flags='i') + self.assertEqual(len(results), 1) + self.assertEqual('foobar_entry', results[0].title) + def test_find_entries(self): results = self.kp.find_entries(title='Root_entry', regex=True) self.assertEqual(len(results), 0) @@ -434,6 +439,7 @@ url='url', notes='notes', tags='tags', + otp='otp', expires=True, expiry_time=time, icon=icons.KEY, @@ -446,6 +452,7 @@ self.assertEqual(entry.url, 'url') self.assertEqual(entry.notes, 'notes') self.assertEqual(entry.tags, ['tags']) + self.assertEqual(entry.otp, 'otp') self.assertEqual(entry.expires, True) self.assertEqual(entry.expiry_time, time.replace(tzinfo=tz.gettz()).astimezone(tz.gettz('UTC'))) @@ -498,6 +505,7 @@ entry.icon = icons.GLOBE entry.set_custom_property('foo', 'bar') entry.set_custom_property('multiline', 'hello\nworld') + entry.otp = "otpsecret" self.assertEqual(entry.title, changed_string + 'title') self.assertEqual(entry.username, changed_string + 'username') @@ -507,6 +515,7 @@ self.assertEqual(entry.icon, icons.GLOBE) self.assertEqual(entry.get_custom_property('foo'), 'bar') self.assertEqual(entry.get_custom_property('multiline'), 'hello\nworld') + self.assertEqual(entry.otp, 'otpsecret') self.assertIn('foo', entry.custom_properties) entry.delete_custom_property('foo') self.assertEqual(entry.get_custom_property('foo'), None) @@ -920,6 +929,14 @@ self.kp_tmp.keyfile = self.keyfile_tmp PyKeePass(stream, self.password, self.keyfile_tmp) + def test_issue308(self): + # find_entries/find_groups() break when supplied None values directly + + results = self.kp.find_entries(title='foobar_entry') + results2 = self.kp.find_entries(title='foobar_entry', username=None) + + self.assertEqual(results, results2) + class EntryFindTests4(KDBX4Tests, EntryFindTests3): pass