[Python-modules-commits] [keyrings.alt] 01/04: Import keyrings.alt_2.2.orig.tar.gz

Dmitry Shachnev mitya57 at moszumanska.debian.org
Mon Mar 27 17:57:01 UTC 2017


This is an automated email from the git hooks/post-receive script.

mitya57 pushed a commit to branch master
in repository keyrings.alt.

commit 1082fb647a48f6780204e14aa51282bd09b5bf72
Author: Dmitry Shachnev <mitya57 at gmail.com>
Date:   Mon Mar 27 20:44:02 2017 +0300

    Import keyrings.alt_2.2.orig.tar.gz
---
 .coveragerc                            |  29 ++++++
 .travis.yml                            |   5 +-
 CHANGES.rst                            |  20 ++++
 PKG-INFO                               |  18 ++--
 README.rst                             |  13 +--
 docs/conf.py                           |  31 +++++--
 docs/index.rst                         |   5 -
 keyrings.alt.egg-info/PKG-INFO         |  18 ++--
 keyrings.alt.egg-info/SOURCES.txt      |   4 +-
 keyrings.alt.egg-info/entry_points.txt |   1 -
 keyrings.alt.egg-info/requires.txt     |   1 +
 keyrings/alt/Gnome.py                  |   5 +-
 keyrings/alt/Google.py                 |   4 +-
 keyrings/alt/file.py                   | 102 ++++++++++++++++-----
 keyrings/alt/file_base.py              |  90 ++++++++++++++----
 keyrings/alt/kwallet.py                | 114 -----------------------
 keyrings/alt/pyfs.py                   |   2 +-
 setup.cfg                              |   3 -
 setup.py                               |  13 +--
 tests/mocks.py                         |   6 +-
 tests/requirements.txt                 |   3 +-
 tests/test_Google.py                   |   4 +-
 tests/test_file.py                     | 163 ++++++++++++++++++++++++++++++++-
 tests/test_kwallet.py                  |  81 ----------------
 24 files changed, 433 insertions(+), 302 deletions(-)

diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..f565452
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,29 @@
+[run]
+branch = True
+
+[report]
+exclude_lines =
+    # Have to re-enable the standard pragma
+    pragma: no cover
+
+    # Don't complain about missing debug-only code:
+    def __repr__
+
+    # Don't complain if tests don't hit defensive assertion code:
+    raise AssertionError
+    raise NotImplementedError
+
+    # Don't complain if non-runnable code isn't run:
+    if 0:
+    if __name__ == .__main__.:
+
+    # don't try to cover abstracts
+    @abc.abstractmethod
+    @abc.abstractproperty
+    
+    # don't try to cover special properties
+    @properties.NonDataProperty
+
+show_missing = True
+ignore_errors = True
+
diff --git a/.travis.yml b/.travis.yml
index e2e715f..390133e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ sudo: false
 language: python
 python:
 - 2.7
-- 3.5
+- 3.6
 install:
 - pip install tox "setuptools>=28.2"
 script:
@@ -16,8 +16,9 @@ deploy:
   on:
     tags: true
     all_branches: true
-    python: 3.5
+    python: 3.6
   user: jaraco
   distributions: dists
+  skip_upload_docs: true
   password:
     secure: iRtbY9yEN8qwJ7wiycL8N5NdZhRDENT5M8Bkpm7YSN5OiM41LRwFYnHDoQhYpz9L/UdfbovgEvv1XsQX/o8XRTn5efmhXRgS/FY3cA7Ry0GG41hmk2fnAgYuivA8EGoPveS9CnbU71ikL0HjRHv93+7Pz1kyMEYXRUpbMZ16K74o1J1MLOoCdQXXlKWfYjqAV/BglWwjIkwXVmNM2JwGV7U2hJ8zjsX1Bn7XqsZdC2KB6jK1frVjEHDfbv3NLQYIzQYqGw8GMWa+9EGVfFzqPnQmUC3E5GA93rC1SceBUb2RDBfCUpyFkyin3lQ05EvyVJoIaZO56gJ9Py1XzzSinSTjylgTxvFWx4uweowv5oM9OWnV+3SsZDw5+55fW5CYiMjdqjLso83gTkc8jbDHeK73Yh0sju2+QnOejnMzKRaBtCBubUkL+TPjEzoeUYSKLgW+iVHIKNKEKNME/WLzNtEhQwEFbLD5 [...]
diff --git a/CHANGES.rst b/CHANGES.rst
index a3bec54..e16fa3e 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,23 @@
+2.2
+===
+
+#17: Drop dependency on keyring.py27compat and use six
+instead.
+
+#16: Minor tweaks to file-based backends.
+
+2.1
+===
+
+Add persistent scheme and version tags for file based backends.
+Prepare for associated data handling in file based schemes.
+
+2.0
+===
+
+#12: Drop kwallet support, now superseded by the dual kwallet
+support in keyring.
+
 1.3
 ===
 
diff --git a/PKG-INFO b/PKG-INFO
index 30df9d9..b5f5da3 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: keyrings.alt
-Version: 1.3
+Version: 2.2
 Summary: Alternate keyring implementations
 Home-page: https://github.com/jaraco/keyrings.alt
 Author: Jason R. Coombs
@@ -19,15 +19,12 @@ Description: .. image:: https://img.shields.io/pypi/v/keyrings.alt.svg
         Alternate keyring backend implementations for use with the
         `keyring package <https://pypi.python.org/pypi/keyring>`_.
         
-        Docs
-        ====
+        License
+        =======
         
-        There's `no good mechanism for publishing documentation
-        <https://github.com/pypa/python-packaging-user-guide/pull/266>`_
-        easily. If there's a documentation link above, it's probably
-        stale because PyPI-based documentation is deprecated. This
-        project may have documentation published at ReadTheDocs, but
-        probably not. Good luck finding it.
+        License is indicated in the project metadata (typically one or more
+        of the Trove classifiers). For more details, see `this explanation
+        <https://github.com/jaraco/skeleton/issues/1>`_.
         
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
@@ -35,3 +32,4 @@ Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
+Requires-Python: >=2.7
diff --git a/README.rst b/README.rst
index 6fb3ff5..6b1a79f 100644
--- a/README.rst
+++ b/README.rst
@@ -11,12 +11,9 @@
 Alternate keyring backend implementations for use with the
 `keyring package <https://pypi.python.org/pypi/keyring>`_.
 
-Docs
-====
+License
+=======
 
-There's `no good mechanism for publishing documentation
-<https://github.com/pypa/python-packaging-user-guide/pull/266>`_
-easily. If there's a documentation link above, it's probably
-stale because PyPI-based documentation is deprecated. This
-project may have documentation published at ReadTheDocs, but
-probably not. Good luck finding it.
+License is indicated in the project metadata (typically one or more
+of the Trove classifiers). For more details, see `this explanation
+<https://github.com/jaraco/skeleton/issues/1>`_.
diff --git a/docs/conf.py b/docs/conf.py
index f69e839..20beec2 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,7 +1,13 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
-import pkg_resources
+import os
+import sys
+import subprocess
+import datetime
+
+if 'check_output' not in dir(subprocess):
+	import subprocess32 as subprocess
 
 extensions = [
     'sphinx.ext.autodoc',
@@ -9,11 +15,19 @@ extensions = [
 ]
 
 # General information about the project.
-project = 'keyrings.alt'
-copyright = '2016 Jason R. Coombs'
 
-# The short X.Y version.
-version = pkg_resources.require(project)[0].version
+root = os.path.join(os.path.dirname(__file__), '..')
+setup_script = os.path.join(root, 'setup.py')
+fields = ['--name', '--version', '--url', '--author']
+dist_info_cmd = [sys.executable, setup_script] + fields
+output_bytes = subprocess.check_output(dist_info_cmd, cwd=root)
+project, version, url, author = output_bytes.decode('utf-8').strip().split('\n')
+
+origin_date = datetime.date(2016,1,1)
+today = datetime.date.today()
+
+copyright = '{origin_date.year}-{today.year} {author}'.format(**locals())
+
 # The full version, including alpha/beta/rc tags.
 release = version
 
@@ -24,16 +38,21 @@ link_files = {
 		using=dict(
 			GH='https://github.com',
 			project=project,
+			url=url,
 		),
 		replace=[
 			dict(
 				pattern=r"(Issue )?#(?P<issue>\d+)",
-				url='{GH}/jaraco/{project}/issues/{issue}',
+				url='{url}/issues/{issue}',
 			),
 			dict(
 				pattern=r"^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n",
 				with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n",
 			),
+			dict(
+				pattern=r"PEP[- ](?P<pep_number>\d+)",
+				url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/',
+			),
 		],
 	),
 }
diff --git a/docs/index.rst b/docs/index.rst
index 4426074..8798b95 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -27,11 +27,6 @@ Welcome to keyrings.alt documentation!
     :undoc-members:
     :show-inheritance:
 
-.. automodule:: keyrings.alt.kwallet
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
 .. automodule:: keyrings.alt.multi
     :members:
     :undoc-members:
diff --git a/keyrings.alt.egg-info/PKG-INFO b/keyrings.alt.egg-info/PKG-INFO
index 30df9d9..b5f5da3 100644
--- a/keyrings.alt.egg-info/PKG-INFO
+++ b/keyrings.alt.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
 Name: keyrings.alt
-Version: 1.3
+Version: 2.2
 Summary: Alternate keyring implementations
 Home-page: https://github.com/jaraco/keyrings.alt
 Author: Jason R. Coombs
@@ -19,15 +19,12 @@ Description: .. image:: https://img.shields.io/pypi/v/keyrings.alt.svg
         Alternate keyring backend implementations for use with the
         `keyring package <https://pypi.python.org/pypi/keyring>`_.
         
-        Docs
-        ====
+        License
+        =======
         
-        There's `no good mechanism for publishing documentation
-        <https://github.com/pypa/python-packaging-user-guide/pull/266>`_
-        easily. If there's a documentation link above, it's probably
-        stale because PyPI-based documentation is deprecated. This
-        project may have documentation published at ReadTheDocs, but
-        probably not. Good luck finding it.
+        License is indicated in the project metadata (typically one or more
+        of the Trove classifiers). For more details, see `this explanation
+        <https://github.com/jaraco/skeleton/issues/1>`_.
         
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
@@ -35,3 +32,4 @@ Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: MIT License
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
+Requires-Python: >=2.7
diff --git a/keyrings.alt.egg-info/SOURCES.txt b/keyrings.alt.egg-info/SOURCES.txt
index 2afd3fd..d9eb05f 100644
--- a/keyrings.alt.egg-info/SOURCES.txt
+++ b/keyrings.alt.egg-info/SOURCES.txt
@@ -1,3 +1,4 @@
+.coveragerc
 .travis.yml
 CHANGES.rst
 LICENSE
@@ -17,6 +18,7 @@ keyrings.alt.egg-info/SOURCES.txt
 keyrings.alt.egg-info/dependency_links.txt
 keyrings.alt.egg-info/entry_points.txt
 keyrings.alt.egg-info/namespace_packages.txt
+keyrings.alt.egg-info/requires.txt
 keyrings.alt.egg-info/top_level.txt
 keyrings/alt/Gnome.py
 keyrings/alt/Google.py
@@ -26,7 +28,6 @@ keyrings/alt/_win_crypto.py
 keyrings/alt/file.py
 keyrings/alt/file_base.py
 keyrings/alt/keyczar.py
-keyrings/alt/kwallet.py
 keyrings/alt/multi.py
 keyrings/alt/pyfs.py
 tests/__init__.py
@@ -38,6 +39,5 @@ tests/test_Windows.py
 tests/test_crypto.py
 tests/test_file.py
 tests/test_keyczar.py
-tests/test_kwallet.py
 tests/test_multi.py
 tests/test_pyfs.py
\ No newline at end of file
diff --git a/keyrings.alt.egg-info/entry_points.txt b/keyrings.alt.egg-info/entry_points.txt
index cdc6313..0adff50 100644
--- a/keyrings.alt.egg-info/entry_points.txt
+++ b/keyrings.alt.egg-info/entry_points.txt
@@ -4,7 +4,6 @@ Google = keyrings.alt.Google
 Windows (alt) = keyrings.alt.Windows
 file = keyrings.alt.file
 keyczar = keyrings.alt.keyczar
-kwallet = keyrings.alt.kwallet
 multi = keyrings.alt.multi
 pyfs = keyrings.alt.pyfs
 
diff --git a/keyrings.alt.egg-info/requires.txt b/keyrings.alt.egg-info/requires.txt
new file mode 100644
index 0000000..ffe2fce
--- /dev/null
+++ b/keyrings.alt.egg-info/requires.txt
@@ -0,0 +1 @@
+six
diff --git a/keyrings/alt/Gnome.py b/keyrings/alt/Gnome.py
index 16bca90..c4e02bf 100644
--- a/keyrings/alt/Gnome.py
+++ b/keyrings/alt/Gnome.py
@@ -5,10 +5,11 @@ try:
 except (ImportError, ValueError):
     pass
 
+import six
+
 from keyring.backend import KeyringBackend
 from keyring.errors import PasswordSetError, PasswordDeleteError
 from keyring.util import properties
-from keyring.py27compat import unicode_str
 
 
 class Keyring(KeyringBackend):
@@ -65,7 +66,7 @@ class Keyring(KeyringBackend):
             return None
 
         secret = items[0].secret
-        return secret if isinstance(secret, unicode_str) else secret.decode('utf-8')
+        return secret if isinstance(secret, six.text_type) else secret.decode('utf-8')
 
     def set_password(self, service, username, password):
         """Set password for the username of the service
diff --git a/keyrings/alt/Google.py b/keyrings/alt/Google.py
index 66affe0..705992e 100644
--- a/keyrings/alt/Google.py
+++ b/keyrings/alt/Google.py
@@ -6,6 +6,9 @@ import copy
 import codecs
 import base64
 import io
+import pickle
+
+from six.moves import input
 
 try:
     import gdata.docs.service
@@ -15,7 +18,6 @@ except ImportError:
 from . import keyczar
 from keyring import errors
 from keyring import credentials
-from keyring.py27compat import input, pickle
 from keyring.backend import KeyringBackend
 from keyring.util import properties
 from keyring.errors import ExceptionRaisedContext
diff --git a/keyrings/alt/file.py b/keyrings/alt/file.py
index 89b3142..317c140 100644
--- a/keyrings/alt/file.py
+++ b/keyrings/alt/file.py
@@ -1,34 +1,36 @@
 from __future__ import with_statement
 
 import os
-import getpass
-import base64
 import sys
 import json
+import getpass
 
-from keyring.py27compat import configparser
+from six.moves import configparser
 
 from keyring.util import properties
 from keyring.util.escape import escape as escape_for_ini
 
-from . import file_base
-
+from keyrings.alt.file_base import (
+        Keyring, decodebytes, encodebytes,
+)
 
-class PlaintextKeyring(file_base.Keyring):
+class PlaintextKeyring(Keyring):
     """Simple File Keyring with no encryption"""
 
     priority = .5
     "Applicable for all platforms, but not recommended"
 
     filename = 'keyring_pass.cfg'
+    scheme = 'no encyption'
+    version = '1.0'
 
-    def encrypt(self, password):
-        """Directly return the password itself.
+    def encrypt(self, password, assoc = None):
+        """Directly return the password itself, ignore associated data.
         """
         return password
 
-    def decrypt(self, password_encrypted):
-        """Directly return encrypted password.
+    def decrypt(self, password_encrypted, assoc = None):
+        """Directly return encrypted password, ignore associated data.
         """
         return password_encrypted
 
@@ -37,7 +39,8 @@ class Encrypted(object):
     """
     PyCrypto-backed Encryption support
     """
-
+    scheme = '[PBKDF2] AES256.CFB'
+    version = '1.0'
     block_size = 32
 
     def _create_cipher(self, password, salt, IV):
@@ -54,17 +57,17 @@ class Encrypted(object):
             password = getpass.getpass(
                 "Please set a password for your new keyring: ")
             confirm = getpass.getpass('Please confirm the password: ')
-            if password != confirm:
+            if password != confirm:     # pragma: no cover
                 sys.stderr.write("Error: Your passwords didn't match\n")
                 continue
-            if '' == password.strip():
+            if '' == password.strip():  # pragma: no cover
                 # forbid the blank password
                 sys.stderr.write("Error: blank passwords aren't allowed.\n")
                 continue
             return password
 
 
-class EncryptedKeyring(Encrypted, file_base.Keyring):
+class EncryptedKeyring(Encrypted, Keyring):
     """PyCrypto File Keyring"""
 
     filename = 'crypted_pass.cfg'
@@ -78,9 +81,9 @@ class EncryptedKeyring(Encrypted, file_base.Keyring):
             __import__('Crypto.Cipher.AES')
             __import__('Crypto.Protocol.KDF')
             __import__('Crypto.Random')
-        except ImportError:
+        except ImportError:     # pragma: no cover
             raise RuntimeError("PyCrypto required")
-        if not json:
+        if not json:            # pragma: no cover
             raise RuntimeError("JSON implementation such as simplejson "
                 "required.")
         return .6
@@ -101,8 +104,15 @@ class EncryptedKeyring(Encrypted, file_base.Keyring):
         self.keyring_key = self._get_new_password()
         # set a reference password, used to check that the password provided
         #  matches for subsequent checks.
-        self.set_password('keyring-setting', 'password reference',
-            'password reference value')
+        self.set_password('keyring-setting',
+                          'password reference',
+                          'password reference value')
+        self._write_config_value('keyring-setting',
+                                 'scheme',
+                                 self.scheme)
+        self._write_config_value('keyring-setting',
+                                 'version',
+                                 self.version)
 
     def _check_file(self):
         """
@@ -120,6 +130,50 @@ class EncryptedKeyring(Encrypted, file_base.Keyring):
             )
         except (configparser.NoSectionError, configparser.NoOptionError):
             return False
+        try:
+            self._check_scheme(config)
+        except AttributeError:
+            # accept a missing scheme
+            return True
+        return self._check_version(config)
+
+    def _check_scheme(self, config):
+        """
+        check for a valid scheme
+
+        raise ValueError otherwise
+        raise AttributeError if missing
+        """
+        try:
+            scheme = config.get(
+                escape_for_ini('keyring-setting'),
+                escape_for_ini('scheme'),
+            )
+        except (configparser.NoSectionError, configparser.NoOptionError):
+            raise AttributeError("Encryption scheme missing")
+
+        # remove pointless crypto module name
+        if scheme.startswith('PyCrypto '):
+            scheme = scheme[9:]
+
+        if scheme != self.scheme:
+            raise ValueError("Encryption scheme mismatch "
+                             "(exp.: %s, found: %s)" % (self.scheme, scheme))
+
+    def _check_version(self, config):
+        """
+        check for a valid version
+        an existing scheme implies an existing version as well
+
+        return True, if version is valid, and False otherwise
+        """
+        try:
+            self.file_version = config.get(
+                    escape_for_ini('keyring-setting'),
+                    escape_for_ini('version'),
+            )
+        except (configparser.NoSectionError, configparser.NoOptionError):
+            return False
         return True
 
     def _unlock(self):
@@ -142,7 +196,8 @@ class EncryptedKeyring(Encrypted, file_base.Keyring):
         """
         del self.keyring_key
 
-    def encrypt(self, password):
+    def encrypt(self, password, assoc = None):
+        # encrypt password, ignore associated data
         from Crypto.Random import get_random_bytes
         salt = get_random_bytes(self.block_size)
         from Crypto.Cipher import AES
@@ -154,14 +209,15 @@ class EncryptedKeyring(Encrypted, file_base.Keyring):
             salt=salt, IV=IV, password_encrypted=password_encrypted,
         )
         for key in data:
-            data[key] = base64.encodestring(data[key]).decode()
+            # spare a few bytes: throw away newline from base64 encoding
+            data[key] = encodebytes(data[key]).decode()[:-1]
         return json.dumps(data).encode()
 
-    def decrypt(self, password_encrypted):
-        # unpack the encrypted payload
+    def decrypt(self, password_encrypted, assoc = None):
+        # unpack the encrypted payload, ignore associated data
         data = json.loads(password_encrypted.decode())
         for key in data:
-            data[key] = base64.decodestring(data[key].encode())
+            data[key] = decodebytes(data[key].encode())
         cipher = self._create_cipher(self.keyring_key, data['salt'],
             data['IV'])
         plaintext = cipher.decrypt(data['password_encrypted'])
diff --git a/keyrings/alt/file_base.py b/keyrings/alt/file_base.py
index f0e4cd2..62d42dd 100644
--- a/keyrings/alt/file_base.py
+++ b/keyrings/alt/file_base.py
@@ -1,16 +1,26 @@
 from __future__ import with_statement
 
 import os
-import base64
 import abc
+import base64
 
-from keyring.py27compat import configparser
+from six.moves import configparser
 
 from keyring.errors import PasswordDeleteError
 from keyring.backend import KeyringBackend
 from keyring.util import platform_, properties
 from keyring.util.escape import escape as escape_for_ini
 
+try:
+    encodebytes = base64.encodebytes
+except AttributeError:  # pragma: no cover
+    encodebytes = base64.encodestring
+
+try:
+    decodebytes = base64.decodebytes
+except AttributeError:  # pragma: no cover
+    decodebytes = base64.decodestring
+
 
 class FileBacked(object):
     @abc.abstractproperty
@@ -27,8 +37,30 @@ class FileBacked(object):
         """
         return os.path.join(platform_.data_root(), self.filename)
 
+    @abc.abstractproperty
+    def scheme(self):
+        """
+        The encryption scheme used to store the passwords.
+        """
+        return 'not defined'
+
+    @abc.abstractproperty
+    def version(self):
+        """
+        The encryption version used to store the passwords.
+        """
+        return None
+
+    @properties.NonDataProperty
+    def file_version(self):
+        """
+        The encryption version used in file to store the passwords.
+        """
+        return None
+
     def __repr__(self):
-        tmpl = "<{self.__class__.__name__} at {self.file_path}>"
+        tmpl = "<{self.__class__.__name__} with {self.scheme} " \
+               "v.{self.version} at {self.file_path}>"
         return tmpl.format(**locals())
 
 
@@ -43,22 +75,28 @@ class Keyring(FileBacked, KeyringBackend):
     """
 
     @abc.abstractmethod
-    def encrypt(self, password):
+    def encrypt(self, password, assoc = None):
         """
-        Given a password (byte string), return an encrypted byte string.
+        Given a password (byte string) and assoc (byte string, optional),
+        return an encrypted byte string.
+
+        assoc provides associated data (typically: service and username)
         """
 
     @abc.abstractmethod
-    def decrypt(self, password_encrypted):
+    def decrypt(self, password_encrypted, assoc = None):
         """
-        Given a password encrypted by a previous call to `encrypt`, return
-        the original byte string.
+        Given a password encrypted by a previous call to `encrypt`, and assoc
+        (byte string, optional), return the original byte string.
+
+        assoc provides associated data (typically: service and username)
         """
 
     def get_password(self, service, username):
         """
         Read the password from the file.
         """
+        assoc = self._generate_assoc(service, username)
         service = escape_for_ini(service)
         username = escape_for_ini(username)
 
@@ -71,9 +109,13 @@ class Keyring(FileBacked, KeyringBackend):
         try:
             password_base64 = config.get(service, username).encode()
             # decode with base64
-            password_encrypted = base64.decodestring(password_base64)
-            # decrypted the password
-            password = self.decrypt(password_encrypted).decode('utf-8')
+            password_encrypted = decodebytes(password_base64)
+            # decrypt the password with associated data
+            try:
+                password = self.decrypt(password_encrypted, assoc).decode('utf-8')
+            except ValueError:
+                # decrypt the password without associated data
+                password = self.decrypt(password_encrypted).decode('utf-8')
         except (configparser.NoOptionError, configparser.NoSectionError):
             password = None
         return password
@@ -81,14 +123,21 @@ class Keyring(FileBacked, KeyringBackend):
     def set_password(self, service, username, password):
         """Write the password in the file.
         """
-        service = escape_for_ini(service)
-        username = escape_for_ini(username)
-
+        assoc = self._generate_assoc(service, username)
         # encrypt the password
-        password_encrypted = self.encrypt(password.encode('utf-8'))
-        # encode with base64
-        password_base64 = base64.encodestring(password_encrypted).decode()
+        password_encrypted = self.encrypt(password.encode('utf-8'), assoc)
+        # encode with base64 and add line break to untangle config file
+        password_base64 = '\n' + encodebytes(password_encrypted).decode()
+
+        self._write_config_value(service, username, password_base64)
+
+    def _generate_assoc(self, service, username):
+        """Generate tamper resistant bytestring of associated data
+        """
+        return (escape_for_ini(service) + '\0' +
+                escape_for_ini(username)).encode()
 
+    def _write_config_value(self, service, key, value):
         # ensure the file exists
         self._ensure_file_path()
 
@@ -96,10 +145,13 @@ class Keyring(FileBacked, KeyringBackend):
         config = configparser.RawConfigParser()
         config.read(self.file_path)
 
+        service = escape_for_ini(service)
+        key = escape_for_ini(key)
+
         # update the keyring with the password
         if not config.has_section(service):
             config.add_section(service)
-        config.set(service, username, password_base64)
+        config.set(service, key, value)
 
         # save the keyring back to the file
         with open(self.file_path, 'w') as config_file:
@@ -111,7 +163,7 @@ class Keyring(FileBacked, KeyringBackend):
         If it doesn't, create it with "go-rwx" permissions.
         """
         storage_root = os.path.dirname(self.file_path)
-        if storage_root and not os.path.isdir(storage_root):
+        if storage_root and not os.path.isdir(storage_root):    # pragma: no cover
             os.makedirs(storage_root)
         if not os.path.isfile(self.file_path):
             # create the file without group/world permissions
diff --git a/keyrings/alt/kwallet.py b/keyrings/alt/kwallet.py
deleted file mode 100644
index e293236..0000000
--- a/keyrings/alt/kwallet.py
+++ /dev/null
@@ -1,114 +0,0 @@
-from __future__ import absolute_import
-
-import os
-import sys
-
-from keyring.py27compat import unicode_str
-from keyring.backend import KeyringBackend
-from keyring.errors import PasswordDeleteError
-from keyring.errors import PasswordSetError, ExceptionRaisedContext
-from keyring.util import properties
-
-# mixing Qt4 & Qt5 causes errors and may segfault
-if 'PyQt5' not in sys.modules:
-    try:
-        from PyKDE4.kdeui import KWallet
-        from PyQt4 import QtGui
-    except ImportError:
-        pass
-
-kwallet = None
-
-def open_kwallet(kwallet_module=None, qt_module=None):
-
-    # If we specified the kwallet_module and/or qt_module, surely we won't need
-    # the cached kwallet object...
-    if kwallet_module is None and qt_module is None:
-        global kwallet
-        if not kwallet is None:
-            return kwallet
-
-    # Allow for the injection of module-like objects for testing purposes.
-    if kwallet_module is None:
-        kwallet_module = KWallet.Wallet
-    if qt_module is None:
-        qt_module = QtGui
-
-    # KDE wants us to instantiate an application object.
-    app = None
-    if qt_module.qApp.instance() == None:
-        app = qt_module.QApplication([])
-    try:
-        window = qt_module.QWidget()
-        kwallet = kwallet_module.openWallet(
-            kwallet_module.NetworkWallet(),
-            window.winId(),
-            kwallet_module.Synchronous)
-        if kwallet is not None:
-            if not kwallet.hasFolder('Python'):
-                kwallet.createFolder('Python')
-            kwallet.setFolder('Python')
-            return kwallet
-    finally:
-        if app:
-            app.exit()
-
-class QtKeyring(KeyringBackend):
-    """KDE KWallet"""
-
-    @properties.ClassProperty
-    @classmethod
-    def priority(cls):
-        with ExceptionRaisedContext() as exc:
-            KWallet.__name__
-        if exc:
-            raise RuntimeError("KDE libraries not available")
-        if "DISPLAY" not in os.environ:
-            raise RuntimeError("cannot connect to X server")
-        # Infer if KDE environment is active based on environment vars.
-        # TODO: Does PyKDE provide a better indicator?
-        kde_session_keys = (
-            'KDE_SESSION_ID', # most environments
-            'KDE_FULL_SESSION', # openSUSE
-        )
-        if not set(os.environ).intersection(kde_session_keys):
-            return 0
-        return 5
-
-    def get_password(self, service, username):
-        """Get password of the username for the service
-        """
-        key = username + '@' + service
-        network = KWallet.Wallet.NetworkWallet()
-        wallet = open_kwallet()
-        if wallet is None:
-            # the user pressed "cancel" when prompted to unlock their keyring.
-            return None
-        if wallet.keyDoesNotExist(network, 'Python', key):
-            return None
-
-        result = wallet.readPassword(key)[1]
-        # The string will be a PyQt4.QtCore.QString, so turn it into a unicode
-        # object.
-        return unicode_str(result)
-
-    def set_password(self, service, username, password):
-        """Set password for the username of the service
-        """
-        wallet = open_kwallet()
-        if wallet is None:
-            # the user pressed "cancel" when prompted to unlock their keyring.
-            raise PasswordSetError("Cancelled by user")
-        wallet.writePassword(username+'@'+service, password)
-
-    def delete_password(self, service, username):
-        """Delete the password for the username of the service.
-        """
-        key = username + '@' + service
-        wallet = open_kwallet()
-        if wallet is None:
-            # the user pressed "cancel" when prompted to unlock their keyring.
-            raise PasswordDeleteError("Cancelled by user")
-        if wallet.keyDoesNotExist(wallet.walletName(), 'Python', key):
-            raise PasswordDeleteError("Password not found")
-        wallet.removeEntry(key)
diff --git a/keyrings/alt/pyfs.py b/keyrings/alt/pyfs.py
index 7896b4b..0165cd5 100644
--- a/keyrings/alt/pyfs.py
+++ b/keyrings/alt/pyfs.py
@@ -2,7 +2,7 @@ import os
 import base64
 import sys
 
-from keyring.py27compat import configparser
+from six.moves import configparser
 
 from keyring import errors
 from keyring.util.escape import escape as escape_for_ini
diff --git a/setup.cfg b/setup.cfg
index c1794d2..447e8f2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -5,9 +5,6 @@ dists = clean --all sdist bdist_wheel
 [wheel]
 universal = 1
 
-[upload]
-repository = https://upload.pypi.org/legacy/
-
 [egg_info]
 tag_build = 
 tag_date = 0
diff --git a/setup.py b/setup.py
index 90f447a..9e074bd 100644
--- a/setup.py
+++ b/setup.py
@@ -3,20 +3,16 @@
 # Project skeleton maintained at https://github.com/jaraco/skeleton
 
 import io
-import sys
 
 import setuptools
 
 with io.open('README.rst', encoding='utf-8') as readme:
     long_description = readme.read()
 
-needs_wheel = {'release', 'bdist_wheel', 'dists'}.intersection(sys.argv)
-wheel = ['wheel'] if needs_wheel else []
-
 name = 'keyrings.alt'
 description = 'Alternate keyring implementations'
 
-setup_params = dict(
+params = dict(
     name=name,
     use_scm_version=True,
     author="Jason R. Coombs",
@@ -27,13 +23,15 @@ setup_params = dict(
     packages=setuptools.find_packages(exclude=['tests']),
     include_package_data=True,
     namespace_packages=name.split('.')[:-1],
+    python_requires='>=2.7',
     install_requires=[
+        'six',
     ],
     extras_require={
     },
     setup_requires=[
         'setuptools_scm>=1.15.0',
-    ] + wheel,
+    ],
     classifiers=[
         "Development Status :: 5 - Production/Stable",
         "Intended Audience :: Developers",
@@ -47,7 +45,6 @@ setup_params = dict(
             'Gnome = keyrings.alt.Gnome',
             'Google = keyrings.alt.Google',
             'keyczar = keyrings.alt.keyczar',
-            'kwallet = keyrings.alt.kwallet',
             'multi = keyrings.alt.multi',
             'pyfs = keyrings.alt.pyfs',
             'Windows (alt) = keyrings.alt.Windows',
@@ -55,4 +52,4 @@ setup_params = dict(
     },
 )
 if __name__ == '__main__':
-    setuptools.setup(**setup_params)
+    setuptools.setup(**params)
diff --git a/tests/mocks.py b/tests/mocks.py
index 38d95c5..91c1195 100644
--- a/tests/mocks.py
+++ b/tests/mocks.py
@@ -4,8 +4,9 @@ Various mock objects for testing
 
 import base64
 import io
+import pickle
 
-from keyring.py27compat import pickle, unicode_str
+import six
 
 
 class MockAtom(object):
@@ -113,8 +114,7 @@ class MockDocumentService(MockGDataService):
             else:
                 raise put_err()
         # save the data for asserting against
-        assert isinstance(data, str) or isinstance(data, unicode_str), \
-            'Should be a string'
+        assert isinstance(data, six.string_types), 'Should be a string'
         self._put_data =  pickle.loads(base64.urlsafe_b64decode(data))
         self._put_count += 1
         return MockEntry('', 'mockentry%3A' + '')
diff --git a/tests/requirements.txt b/tests/requirements.txt
index 21a1a09..cc5a1e9 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -1,6 +1,7 @@
 pytest >= 2.8
+subprocess32; python_version=="2.6"
 backports.unittest_mock
-keyring[test]
... 309 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/keyrings.alt.git



More information about the Python-modules-commits mailing list