Upgrade keyring to 4.0

This commit is contained in:
Matthew Jones
2014-08-06 15:31:20 -04:00
parent 6a7cb5a4e7
commit cbe26c4619
6 changed files with 93 additions and 107 deletions

View File

@@ -30,7 +30,7 @@ gevent-websocket==0.9.3 (geventwebsocket/*)
httplib2==0.9 (httplib2/*)
importlib==1.0.3 (importlib/*, needed for Python 2.6 support)
iso8601==0.1.10 (iso8601/*)
keyring==3.7 (keyring/*, excluded bin/keyring)
keyring==4.0 (keyring/*, excluded bin/keyring)
kombu==3.0.14 (kombu/*)
Markdown==2.4 (markdown/*, excluded bin/markdown_py)
mock==1.0.1 (mock.py)

View File

@@ -10,10 +10,18 @@ from ..errors import PasswordDeleteError, ExceptionRaisedContext
from . import file
try:
import pywintypes
import win32cred
# prefer pywin32-ctypes
from win32ctypes import pywintypes
from win32ctypes import win32cred
# force demand import to raise ImportError
win32cred.__name__
except ImportError:
pass
# fallback to pywin32
try:
import pywintypes
import win32cred
except ImportError:
pass
try:
import winreg
@@ -229,9 +237,31 @@ class RegistryKeyring(KeyringBackend):
hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0,
winreg.KEY_ALL_ACCESS)
winreg.DeleteValue(hkey, username)
winreg.CloseKey(hkey)
except WindowsError:
e = sys.exc_info()[1]
raise PasswordDeleteError(e)
self._delete_key_if_empty(service)
def _delete_key_if_empty(self, service):
key_name = r'Software\%s\Keyring' % service
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0,
winreg.KEY_ALL_ACCESS)
try:
winreg.EnumValue(key, 0)
return
except WindowsError:
pass
winreg.CloseKey(key)
# it's empty; delete everything
while key_name != 'Software':
parent, sep, base = key_name.rpartition('\\')
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, parent, 0,
winreg.KEY_ALL_ACCESS)
winreg.DeleteKey(key, base)
winreg.CloseKey(key)
key_name = parent
class OldPywinError(object):

View File

@@ -62,6 +62,8 @@ class Keyring(KeyringBackend):
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 = (

View File

@@ -37,8 +37,9 @@ class CommandLineTool(object):
if opts.keyring_backend is not None:
try:
backend = core.load_keyring(opts.keyring_path,
opts.keyring_backend)
if opts.keyring_path:
sys.path.insert(0, opts.keyring_path)
backend = core.load_keyring(opts.keyring_backend)
set_keyring(backend)
except (Exception,):
# Tons of things can go wrong here:

View File

@@ -5,7 +5,6 @@ Created by Kang Zhang on 2009-07-09
"""
import os
import sys
import warnings
import logging
from .py27compat import configparser
@@ -71,19 +70,12 @@ def _get_best_keyring():
return keyrings[0]
def load_keyring(keyring_path, keyring_name):
def load_keyring(keyring_name):
"""
Load the specified keyring by name (a fully-qualified name to the
keyring, such as 'keyring.backends.file.PlaintextKeyring')
`keyring_path` is an additional, optional search path and may be None.
**deprecated** In the future, keyring_path must be None.
"""
module_name, sep, class_name = keyring_name.rpartition('.')
if keyring_path is not None and keyring_path not in sys.path:
warnings.warn("keyring_path is deprecated and should always be None",
DeprecationWarning)
sys.path.insert(0, keyring_path)
__import__(module_name)
module = sys.modules[module_name]
class_ = getattr(module, class_name)
@@ -93,46 +85,32 @@ def load_keyring(keyring_path, keyring_name):
def load_config():
"""Load a keyring using the config file.
The config file can be in the current working directory, or in the user's
home directory.
"""
keyring = None
"""Load a keyring using the config file in the config root."""
filename = 'keyringrc.cfg'
local_path = os.path.join(os.getcwd(), filename)
config_path = os.path.join(platform.config_root(), filename)
keyring_cfg = os.path.join(platform.config_root(), filename)
# search from current working directory and the data root
keyring_cfg_candidates = [local_path, config_path]
if not os.path.exists(keyring_cfg):
return
# initialize the keyring_config with the first detected config file
keyring_cfg = None
for path in keyring_cfg_candidates:
keyring_cfg = path
if os.path.exists(path):
break
config = configparser.RawConfigParser()
config.read(keyring_cfg)
_load_keyring_path(config)
if os.path.exists(keyring_cfg):
config = configparser.RawConfigParser()
config.read(keyring_cfg)
_load_keyring_path(config)
# load the keyring class name, and then load this keyring
try:
if config.has_section("backend"):
keyring_name = config.get("backend", "default-keyring").strip()
else:
raise configparser.NoOptionError('backend', 'default-keyring')
# load the keyring class name, and then load this keyring
try:
if config.has_section("backend"):
keyring_name = config.get("backend", "default-keyring").strip()
else:
raise configparser.NoOptionError('backend', 'default-keyring')
except (configparser.NoOptionError, ImportError):
logger.warning("Keyring config file contains incorrect values.\n" +
"Config file: %s" % keyring_cfg)
return
keyring = load_keyring(None, keyring_name)
except (configparser.NoOptionError, ImportError):
logger.warning("Keyring config file contains incorrect values.\n" +
"Config file: %s" % keyring_cfg)
return keyring
return load_keyring(keyring_name)
def _load_keyring_path(config):
"load the keyring-path option (if present)"

View File

@@ -10,17 +10,23 @@ import os
import tempfile
import shutil
from keyring.tests.py30compat import unittest
import mock
import pytest
import keyring.backend
import keyring.core
import keyring.util.platform_
from keyring import errors
PASSWORD_TEXT = "This is password"
PASSWORD_TEXT_2 = "This is password2"
KEYRINGRC = "keyringrc.cfg"
@pytest.yield_fixture()
def config_filename(tmpdir):
filename = tmpdir / 'keyringrc.cfg'
with mock.patch('keyring.util.platform_.config_root', lambda: str(tmpdir)):
yield str(filename)
class TestKeyring(keyring.backend.KeyringBackend):
@@ -53,7 +59,7 @@ class TestKeyring2(TestKeyring):
return PASSWORD_TEXT_2
class CoreTestCase(unittest.TestCase):
class TestCore:
mock_global_backend = mock.patch('keyring.core._keyring_backend')
@mock_global_backend
@@ -72,7 +78,7 @@ class CoreTestCase(unittest.TestCase):
"""
result = keyring.core.get_password("test", "user")
backend.get_password.assert_called_once_with('test', 'user')
self.assertIsNotNone(result)
assert result is not None
@mock_global_backend
def test_delete_password(self, backend):
@@ -85,71 +91,40 @@ class CoreTestCase(unittest.TestCase):
keyring.core.set_keyring(TestKeyring())
keyring.core.set_password("test", "user", "password")
self.assertEqual(keyring.core.get_password("test", "user"),
PASSWORD_TEXT)
assert keyring.core.get_password("test", "user") == PASSWORD_TEXT
def test_set_keyring_in_config(self):
def test_set_keyring_in_config(self, config_filename):
"""Test setting the keyring by config file.
"""
# create the config file
config_file = open(KEYRINGRC, 'w')
config_file.writelines([
"[backend]\n",
# the path for the user created keyring
"keyring-path= %s\n" % os.path.dirname(os.path.abspath(__file__)),
# the name of the keyring class
"default-keyring=test_core.TestKeyring2\n",
])
config_file.close()
with open(config_filename, 'w') as config_file:
config_file.writelines([
"[backend]\n",
# the path for the user created keyring
"keyring-path= %s\n" % os.path.dirname(os.path.abspath(__file__)),
# the name of the keyring class
"default-keyring=test_core.TestKeyring2\n",
])
# init the keyring lib, the lib will automaticlly load the
# config file and load the user defined module
keyring.core.init_backend()
keyring.core.set_password("test", "user", "password")
self.assertEqual(keyring.core.get_password("test", "user"),
PASSWORD_TEXT_2)
assert keyring.core.get_password("test", "user") == PASSWORD_TEXT_2
os.remove(KEYRINGRC)
def test_load_config_empty(self, config_filename):
"A non-existent or empty config should load"
assert keyring.core.load_config() is None
def test_load_config(self):
tempdir = tempfile.mkdtemp()
old_location = os.getcwd()
os.chdir(tempdir)
personal_cfg = os.path.join(os.path.expanduser("~"), "keyringrc.cfg")
if os.path.exists(personal_cfg):
os.rename(personal_cfg, personal_cfg + '.old')
personal_renamed = True
else:
personal_renamed = False
def test_load_config_degenerate(self, config_filename):
"load_config should succeed in the absence of a backend section"
with open(config_filename, 'w') as config_file:
config_file.write('[keyring]')
assert keyring.core.load_config() is None
# loading with an empty environment
keyring.core.load_config()
# loading with a file that doesn't have a backend section
cfg = os.path.join(tempdir, "keyringrc.cfg")
f = open(cfg, 'w')
f.write('[keyring]')
f.close()
keyring.core.load_config()
# loading with a file that doesn't have a default-keyring value
cfg = os.path.join(tempdir, "keyringrc.cfg")
f = open(cfg, 'w')
f.write('[backend]')
f.close()
keyring.core.load_config()
os.chdir(old_location)
shutil.rmtree(tempdir)
if personal_renamed:
os.rename(personal_cfg + '.old', personal_cfg)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(CoreTestCase))
return suite
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")
def test_load_config_blank_backend(self, config_filename):
"load_config should succeed with an empty [backend] section"
with open(config_filename, 'w') as config_file:
config_file.write('[backend]')
assert keyring.core.load_config() is None