Hello community,
here is the log from the commit of package python-keyrings.alt for
openSUSE:Factory checked in at 2017-06-07 09:55:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-keyrings.alt (Old)
and /work/SRC/openSUSE:Factory/.python-keyrings.alt.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-keyrings.alt"
Wed Jun 7 09:55:40 2017 rev:4 rq:501399 version:2.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-keyrings.alt/python-keyrings.alt.changes
2017-05-10 20:36:10.904213834 +0200
+++
/work/SRC/openSUSE:Factory/.python-keyrings.alt.new/python-keyrings.alt.changes
2017-06-07 09:56:27.997837806 +0200
@@ -1,0 +2,7 @@
+Tue Jun 6 12:41:35 UTC 2017 - [email protected]
+
+- update to 2.2.:
+ * Drop dependency on keyring.py27compat and use six
+ instead.
+
+-------------------------------------------------------------------
Old:
----
keyrings.alt-2.0.tar.gz
New:
----
keyrings.alt-2.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-keyrings.alt.spec ++++++
--- /var/tmp/diff_new_pack.rDMKUO/_old 2017-06-07 09:56:28.873714033 +0200
+++ /var/tmp/diff_new_pack.rDMKUO/_new 2017-06-07 09:56:28.873714033 +0200
@@ -18,7 +18,7 @@
Name: python-keyrings.alt
-Version: 2.0
+Version: 2.2
Release: 0
Summary: Alternate keyring implementations
License: MIT
@@ -38,11 +38,13 @@
BuildRequires: python-pycrypto
BuildRequires: python-pytest
BuildRequires: python-pytest-runner
+BuildRequires: python-six
Requires: python-fs >= 0.5
Requires: python-gdata
Requires: python-keyczar
Requires: python-keyring
Requires: python-pycrypto
+Requires: python-six
# Testing requirements
BuildRoot: %{_tmppath}/%{name}-%{version}-build
%if 0%{?suse_version} && 0%{?suse_version} <= 1110
++++++ keyrings.alt-2.0.tar.gz -> keyrings.alt-2.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/.coveragerc
new/keyrings.alt-2.2/.coveragerc
--- old/keyrings.alt-2.0/.coveragerc 1970-01-01 01:00:00.000000000 +0100
+++ new/keyrings.alt-2.2/.coveragerc 2017-03-25 21:20:01.000000000 +0100
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/.travis.yml
new/keyrings.alt-2.2/.travis.yml
--- old/keyrings.alt-2.0/.travis.yml 2016-12-22 18:51:45.000000000 +0100
+++ new/keyrings.alt-2.2/.travis.yml 2017-03-25 21:20:01.000000000 +0100
@@ -2,7 +2,7 @@
language: python
python:
- 2.7
-- 3.5
+- 3.6
install:
- pip install tox "setuptools>=28.2"
script:
@@ -16,8 +16,9 @@
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/WLzNtEhQwEFbLD5u/0bmHMBFPt40cXv3kSz6tz7NSjCHyJbtsBnyadYWRQTTRoSPnNIt0P716feUY/R5/Am5TIo2ZHKkGbRkuqFcJoJYhKau2M24WkRA4og8uBSyliShovHkrbahe1MpbJ/ZtmIU583FQjxIlGQQtNPN4QTSnQ1fUpvyhQY89GupbSxuek7jlmxmIm9SgAy6w9PUDZ5FKuJzm4=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/CHANGES.rst
new/keyrings.alt-2.2/CHANGES.rst
--- old/keyrings.alt-2.0/CHANGES.rst 2016-12-22 18:51:45.000000000 +0100
+++ new/keyrings.alt-2.2/CHANGES.rst 2017-03-25 21:20:01.000000000 +0100
@@ -1,3 +1,17 @@
+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
===
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/PKG-INFO
new/keyrings.alt-2.2/PKG-INFO
--- old/keyrings.alt-2.0/PKG-INFO 2016-12-22 18:53:14.000000000 +0100
+++ new/keyrings.alt-2.2/PKG-INFO 2017-03-25 21:22:20.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
Name: keyrings.alt
-Version: 2.0
+Version: 2.2
Summary: Alternate keyring implementations
Home-page: https://github.com/jaraco/keyrings.alt
Author: Jason R. Coombs
@@ -19,15 +19,12 @@
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: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
+Requires-Python: >=2.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/README.rst
new/keyrings.alt-2.2/README.rst
--- old/keyrings.alt-2.0/README.rst 2016-12-22 18:51:45.000000000 +0100
+++ new/keyrings.alt-2.2/README.rst 2017-03-25 21:20:01.000000000 +0100
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/docs/conf.py
new/keyrings.alt-2.2/docs/conf.py
--- old/keyrings.alt-2.0/docs/conf.py 2016-12-22 18:51:45.000000000 +0100
+++ new/keyrings.alt-2.2/docs/conf.py 2017-03-25 21:20:01.000000000 +0100
@@ -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 @@
]
# 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 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings/alt/Gnome.py
new/keyrings.alt-2.2/keyrings/alt/Gnome.py
--- old/keyrings.alt-2.0/keyrings/alt/Gnome.py 2016-12-22 18:51:45.000000000
+0100
+++ new/keyrings.alt-2.2/keyrings/alt/Gnome.py 2017-03-25 21:20:01.000000000
+0100
@@ -5,10 +5,11 @@
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 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings/alt/Google.py
new/keyrings.alt-2.2/keyrings/alt/Google.py
--- old/keyrings.alt-2.0/keyrings/alt/Google.py 2016-12-22 18:51:45.000000000
+0100
+++ new/keyrings.alt-2.2/keyrings/alt/Google.py 2017-03-25 21:20:01.000000000
+0100
@@ -6,6 +6,9 @@
import codecs
import base64
import io
+import pickle
+
+from six.moves import input
try:
import gdata.docs.service
@@ -15,7 +18,6 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings/alt/file.py
new/keyrings.alt-2.2/keyrings/alt/file.py
--- old/keyrings.alt-2.0/keyrings/alt/file.py 2016-12-22 18:51:45.000000000
+0100
+++ new/keyrings.alt-2.2/keyrings/alt/file.py 2017-03-25 21:20:01.000000000
+0100
@@ -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 @@
"""
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 @@
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 @@
__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 @@
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 @@
)
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 @@
"""
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 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings/alt/file_base.py
new/keyrings.alt-2.2/keyrings/alt/file_base.py
--- old/keyrings.alt-2.0/keyrings/alt/file_base.py 2016-12-22
18:51:45.000000000 +0100
+++ new/keyrings.alt-2.2/keyrings/alt/file_base.py 2017-03-25
21:20:01.000000000 +0100
@@ -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 @@
"""
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 @@
"""
@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 @@
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 @@
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 @@
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 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings/alt/pyfs.py
new/keyrings.alt-2.2/keyrings/alt/pyfs.py
--- old/keyrings.alt-2.0/keyrings/alt/pyfs.py 2016-12-22 18:51:45.000000000
+0100
+++ new/keyrings.alt-2.2/keyrings/alt/pyfs.py 2017-03-25 21:20:01.000000000
+0100
@@ -2,7 +2,7 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings.alt.egg-info/PKG-INFO
new/keyrings.alt-2.2/keyrings.alt.egg-info/PKG-INFO
--- old/keyrings.alt-2.0/keyrings.alt.egg-info/PKG-INFO 2016-12-22
18:53:14.000000000 +0100
+++ new/keyrings.alt-2.2/keyrings.alt.egg-info/PKG-INFO 2017-03-25
21:22:20.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.2
Name: keyrings.alt
-Version: 2.0
+Version: 2.2
Summary: Alternate keyring implementations
Home-page: https://github.com/jaraco/keyrings.alt
Author: Jason R. Coombs
@@ -19,15 +19,12 @@
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: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
+Requires-Python: >=2.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings.alt.egg-info/SOURCES.txt
new/keyrings.alt-2.2/keyrings.alt.egg-info/SOURCES.txt
--- old/keyrings.alt-2.0/keyrings.alt.egg-info/SOURCES.txt 2016-12-22
18:53:14.000000000 +0100
+++ new/keyrings.alt-2.2/keyrings.alt.egg-info/SOURCES.txt 2017-03-25
21:22:20.000000000 +0100
@@ -1,3 +1,4 @@
+.coveragerc
.travis.yml
CHANGES.rst
LICENSE
@@ -17,6 +18,7 @@
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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/keyrings.alt.egg-info/requires.txt
new/keyrings.alt-2.2/keyrings.alt.egg-info/requires.txt
--- old/keyrings.alt-2.0/keyrings.alt.egg-info/requires.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/keyrings.alt-2.2/keyrings.alt.egg-info/requires.txt 2017-03-25
21:22:20.000000000 +0100
@@ -0,0 +1 @@
+six
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/setup.cfg
new/keyrings.alt-2.2/setup.cfg
--- old/keyrings.alt-2.0/setup.cfg 2016-12-22 18:53:14.000000000 +0100
+++ new/keyrings.alt-2.2/setup.cfg 2017-03-25 21:22:20.000000000 +0100
@@ -5,9 +5,6 @@
[wheel]
universal = 1
-[upload]
-repository = https://upload.pypi.org/legacy/
-
[egg_info]
tag_build =
tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/setup.py
new/keyrings.alt-2.2/setup.py
--- old/keyrings.alt-2.0/setup.py 2016-12-22 18:51:45.000000000 +0100
+++ new/keyrings.alt-2.2/setup.py 2017-03-25 21:20:01.000000000 +0100
@@ -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 @@
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",
@@ -54,4 +52,4 @@
},
)
if __name__ == '__main__':
- setuptools.setup(**setup_params)
+ setuptools.setup(**params)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/tests/mocks.py
new/keyrings.alt-2.2/tests/mocks.py
--- old/keyrings.alt-2.0/tests/mocks.py 2016-12-22 18:51:45.000000000 +0100
+++ new/keyrings.alt-2.2/tests/mocks.py 2017-03-25 21:20:01.000000000 +0100
@@ -4,8 +4,9 @@
import base64
import io
+import pickle
-from keyring.py27compat import pickle, unicode_str
+import six
class MockAtom(object):
@@ -113,8 +114,7 @@
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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/tests/requirements.txt
new/keyrings.alt-2.2/tests/requirements.txt
--- old/keyrings.alt-2.0/tests/requirements.txt 2016-12-22 18:51:45.000000000
+0100
+++ new/keyrings.alt-2.2/tests/requirements.txt 2017-03-25 21:20:01.000000000
+0100
@@ -1,6 +1,7 @@
pytest >= 2.8
+subprocess32; python_version=="2.6"
backports.unittest_mock
-keyring[test]
+keyring[test] >= 10.3.1
fs>=0.5,<2
pycrypto
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/tests/test_Google.py
new/keyrings.alt-2.2/tests/test_Google.py
--- old/keyrings.alt-2.0/tests/test_Google.py 2016-12-22 18:51:45.000000000
+0100
+++ new/keyrings.alt-2.2/tests/test_Google.py 2017-03-25 21:20:01.000000000
+0100
@@ -1,13 +1,15 @@
import codecs
import base64
import unittest
+import pickle
+
+from six.moves import input
from keyring.tests.test_backend import BackendBasicTests
from keyrings.alt import Google
from keyring.credentials import SimpleCredential
from keyring.backend import NullCrypter
from keyring import errors
-from keyring.py27compat import input, pickle
from . import mocks
def is_gdata_supported():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/keyrings.alt-2.0/tests/test_file.py
new/keyrings.alt-2.2/tests/test_file.py
--- old/keyrings.alt-2.0/tests/test_file.py 2016-12-22 18:51:45.000000000
+0100
+++ new/keyrings.alt-2.2/tests/test_file.py 2017-03-25 21:20:01.000000000
+0100
@@ -4,16 +4,26 @@
import errno
import unittest
+from six.moves import configparser
+
+import pytest
+from unittest import mock
+
from keyring.tests.test_backend import BackendBasicTests
from keyring.tests.util import random_string
+from keyring.util.escape import escape as escape_for_ini
+
from keyrings.alt import file
+from keyrings.alt.file_base import encodebytes
+
+from keyring.errors import PasswordDeleteError
+
class FileKeyringTests(BackendBasicTests):
def setUp(self):
super(FileKeyringTests, self).setUp()
- self.keyring = self.init_keyring()
self.keyring.file_path = self.tmp_keyring_file = tempfile.mktemp()
def tearDown(self):
@@ -24,6 +34,16 @@
if e.errno != errno.ENOENT: # No such file or directory
raise
+ def get_config(self):
+ # setting a password triggers keyring file creation
+ config = configparser.RawConfigParser()
+ config.read(self.keyring.file_path)
+ return config
+
+ def save_config(self, config):
+ with open(self.keyring.file_path, 'w') as config_file:
+ config.write(config_file)
+
def test_encrypt_decrypt(self):
password = random_string(20)
# keyring.encrypt expects bytes
@@ -32,6 +52,147 @@
self.assertEqual(password, self.keyring.decrypt(encrypted))
+ def test_encrypt_decrypt_without_assoc(self):
+ # generate keyring
+ self.keyring.set_password('system', 'user', 'password')
+ config = self.get_config()
+ # generate and save password without assoc data
+ encrypted = self.keyring.encrypt('password'.encode('utf-8'))
+ password_base64 = '\n' + encodebytes(encrypted).decode()
+ config.set('system', 'user', password_base64)
+ self.save_config(config)
+ self.assertEqual(self.keyring.get_password('system', 'user'),
'password')
+
+ def test_delete_password(self):
+ self.keyring.set_password('system', 'user', 'password')
+ with pytest.raises(PasswordDeleteError):
+ self.keyring.delete_password('system', 'xxxx')
+ with pytest.raises(PasswordDeleteError):
+ self.keyring.delete_password('xxxxxx', 'xxxx')
+
+ def test_file(self):
+ if not hasattr(self.keyring, '_check_file'):
+ return
+ # keyring file doesn't exist yet
+ self.assertTrue(self.keyring._check_file() == False)
+ # generate keyring
+ self.keyring.set_password('system', 'user', 'password')
+ # valid keyring file exist now
+ self.assertTrue(self.keyring._check_file() == True)
+ # lock keyring
+ self.keyring._lock()
+ # fetch password from keyring
+ self.assertTrue(self.keyring.get_password('system', 'user') ==
'password')
+ # test missing password reference
+ config = self.get_config()
+ krsetting = escape_for_ini('keyring-setting')
+ pwref = escape_for_ini('password reference')
+ #pwrefval = config.get(krsetting, pwref)
+ config.remove_option(krsetting, pwref)
+ self.save_config(config)
+ self.assertTrue(self.keyring._check_file() == False)
+
+ def test_scheme(self):
+ # scheme exists
+ self.assertTrue(self.keyring.scheme is not None)
+ if not hasattr(self.keyring, '_check_file'):
+ return
+
+ # keyring file doesn't exist yet
+ self.assertTrue(self.keyring._check_file() == False)
+ # generate keyring
+ self.keyring.set_password('system', 'user', 'password')
+ config = self.get_config()
+ krsetting = escape_for_ini('keyring-setting')
+ scheme = escape_for_ini('scheme')
+ defscheme = '[PBKDF2] AES256.CFB'
+
+ # default scheme match
+ self.assertTrue(config.get(krsetting, scheme) == defscheme)
+
+ # invalid AES mode
+ config.set(krsetting, scheme, defscheme.replace('CFB', 'XXX'))
+ with pytest.raises(ValueError):
+ self.keyring._check_scheme(config)
+
+ # compatibility with former scheme format
+ config.set(krsetting, scheme, 'PyCrypto ' + defscheme)
+ self.assertTrue(self.keyring._check_scheme(config) == None)
+
+ # test with invalid KDF
+ config.set(krsetting, scheme, defscheme.replace('PBKDF2', 'scrypt'))
+ with pytest.raises(ValueError):
+ self.keyring._check_scheme(config)
+
+ # a missing scheme is valid
+ config.remove_option(krsetting, scheme)
+ self.save_config(config)
+ self.assertTrue(self.keyring._check_file() == True)
+
+ with pytest.raises(AttributeError):
+ self.keyring._check_scheme(config)
+
+ def test_version(self):
+ # version exists
+ self.assertTrue(self.keyring.version is not None)
+ if not hasattr(self.keyring, '_check_version'):
+ return
+
+ # generate keyring
+ self.keyring.set_password('system', 'user', 'password')
+ config = self.get_config()
+
+ # default version valid
+ self.assertTrue(self.keyring._check_version(config) == True)
+
+ krsetting = escape_for_ini('keyring-setting')
+ version = escape_for_ini('version')
+
+ # invalid, if version is missing
+ config.remove_option(krsetting, version)
+ self.save_config(config)
+ self.assertTrue(self.keyring._check_version(config) == False)
+
+
+class EncryptedFileKeyringTestCase(FileKeyringTests, unittest.TestCase):
+
+ def setUp(self):
+ super(EncryptedFileKeyringTestCase, self).setUp()
+ self.mock_getpass()
+
+ def mock_getpass(self, password = 'abcdef'):
+ fake_getpass = mock.Mock(return_value=password)
+ self.patcher = mock.patch('getpass.getpass', fake_getpass)
+ self.patcher.start()
+
+ def tearDown(self):
+ self.patcher.stop()
+
+ def init_keyring(self):
+ return file.EncryptedKeyring()
+
+ def test_wrong_password(self):
+ self.keyring.set_password('system', 'user', 'password')
+ self.patcher.stop()
+ self.mock_getpass('wrong')
+ with pytest.raises(ValueError):
+ self.keyring._unlock()
+ self.patcher.stop()
+ self.mock_getpass()
+
+ @unittest.skipIf(sys.platform == 'win32',
+ "Group/World permissions aren't meaningful on Windows")
+ def test_keyring_not_created_world_writable(self):
+ """
+ Ensure that when keyring creates the file that it's not overly-
+ permissive.
+ """
+ self.keyring.set_password('system', 'user', 'password')
+
+ self.assertTrue(os.path.exists(self.keyring.file_path))
+ group_other_perms = os.stat(self.keyring.file_path).st_mode & 0o077
+ self.assertEqual(group_other_perms, 0)
+
class UncryptedFileKeyringTestCase(FileKeyringTests, unittest.TestCase):