mirror of
https://github.com/ansible/awx.git
synced 2026-05-19 14:57:39 -02:30
Upgrade keyring to 4.0
This commit is contained in:
@@ -30,7 +30,7 @@ gevent-websocket==0.9.3 (geventwebsocket/*)
|
|||||||
httplib2==0.9 (httplib2/*)
|
httplib2==0.9 (httplib2/*)
|
||||||
importlib==1.0.3 (importlib/*, needed for Python 2.6 support)
|
importlib==1.0.3 (importlib/*, needed for Python 2.6 support)
|
||||||
iso8601==0.1.10 (iso8601/*)
|
iso8601==0.1.10 (iso8601/*)
|
||||||
keyring==3.7 (keyring/*, excluded bin/keyring)
|
keyring==4.0 (keyring/*, excluded bin/keyring)
|
||||||
kombu==3.0.14 (kombu/*)
|
kombu==3.0.14 (kombu/*)
|
||||||
Markdown==2.4 (markdown/*, excluded bin/markdown_py)
|
Markdown==2.4 (markdown/*, excluded bin/markdown_py)
|
||||||
mock==1.0.1 (mock.py)
|
mock==1.0.1 (mock.py)
|
||||||
|
|||||||
@@ -10,10 +10,18 @@ from ..errors import PasswordDeleteError, ExceptionRaisedContext
|
|||||||
from . import file
|
from . import file
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import pywintypes
|
# prefer pywin32-ctypes
|
||||||
import win32cred
|
from win32ctypes import pywintypes
|
||||||
|
from win32ctypes import win32cred
|
||||||
|
# force demand import to raise ImportError
|
||||||
|
win32cred.__name__
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
# fallback to pywin32
|
||||||
|
try:
|
||||||
|
import pywintypes
|
||||||
|
import win32cred
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import winreg
|
import winreg
|
||||||
@@ -229,9 +237,31 @@ class RegistryKeyring(KeyringBackend):
|
|||||||
hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0,
|
hkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_name, 0,
|
||||||
winreg.KEY_ALL_ACCESS)
|
winreg.KEY_ALL_ACCESS)
|
||||||
winreg.DeleteValue(hkey, username)
|
winreg.DeleteValue(hkey, username)
|
||||||
|
winreg.CloseKey(hkey)
|
||||||
except WindowsError:
|
except WindowsError:
|
||||||
e = sys.exc_info()[1]
|
e = sys.exc_info()[1]
|
||||||
raise PasswordDeleteError(e)
|
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):
|
class OldPywinError(object):
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class Keyring(KeyringBackend):
|
|||||||
KWallet.__name__
|
KWallet.__name__
|
||||||
if exc:
|
if exc:
|
||||||
raise RuntimeError("KDE libraries not available")
|
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.
|
# Infer if KDE environment is active based on environment vars.
|
||||||
# TODO: Does PyKDE provide a better indicator?
|
# TODO: Does PyKDE provide a better indicator?
|
||||||
kde_session_keys = (
|
kde_session_keys = (
|
||||||
|
|||||||
@@ -37,8 +37,9 @@ class CommandLineTool(object):
|
|||||||
|
|
||||||
if opts.keyring_backend is not None:
|
if opts.keyring_backend is not None:
|
||||||
try:
|
try:
|
||||||
backend = core.load_keyring(opts.keyring_path,
|
if opts.keyring_path:
|
||||||
opts.keyring_backend)
|
sys.path.insert(0, opts.keyring_path)
|
||||||
|
backend = core.load_keyring(opts.keyring_backend)
|
||||||
set_keyring(backend)
|
set_keyring(backend)
|
||||||
except (Exception,):
|
except (Exception,):
|
||||||
# Tons of things can go wrong here:
|
# Tons of things can go wrong here:
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ Created by Kang Zhang on 2009-07-09
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .py27compat import configparser
|
from .py27compat import configparser
|
||||||
@@ -71,19 +70,12 @@ def _get_best_keyring():
|
|||||||
return keyrings[0]
|
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
|
Load the specified keyring by name (a fully-qualified name to the
|
||||||
keyring, such as 'keyring.backends.file.PlaintextKeyring')
|
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('.')
|
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)
|
__import__(module_name)
|
||||||
module = sys.modules[module_name]
|
module = sys.modules[module_name]
|
||||||
class_ = getattr(module, class_name)
|
class_ = getattr(module, class_name)
|
||||||
@@ -93,46 +85,32 @@ def load_keyring(keyring_path, keyring_name):
|
|||||||
|
|
||||||
|
|
||||||
def load_config():
|
def load_config():
|
||||||
"""Load a keyring using the config file.
|
"""Load a keyring using the config file in the config root."""
|
||||||
|
|
||||||
The config file can be in the current working directory, or in the user's
|
|
||||||
home directory.
|
|
||||||
"""
|
|
||||||
keyring = None
|
|
||||||
|
|
||||||
filename = 'keyringrc.cfg'
|
filename = 'keyringrc.cfg'
|
||||||
|
|
||||||
local_path = os.path.join(os.getcwd(), filename)
|
keyring_cfg = os.path.join(platform.config_root(), filename)
|
||||||
config_path = os.path.join(platform.config_root(), filename)
|
|
||||||
|
|
||||||
# search from current working directory and the data root
|
if not os.path.exists(keyring_cfg):
|
||||||
keyring_cfg_candidates = [local_path, config_path]
|
return
|
||||||
|
|
||||||
# initialize the keyring_config with the first detected config file
|
config = configparser.RawConfigParser()
|
||||||
keyring_cfg = None
|
config.read(keyring_cfg)
|
||||||
for path in keyring_cfg_candidates:
|
_load_keyring_path(config)
|
||||||
keyring_cfg = path
|
|
||||||
if os.path.exists(path):
|
|
||||||
break
|
|
||||||
|
|
||||||
if os.path.exists(keyring_cfg):
|
# load the keyring class name, and then load this keyring
|
||||||
config = configparser.RawConfigParser()
|
try:
|
||||||
config.read(keyring_cfg)
|
if config.has_section("backend"):
|
||||||
_load_keyring_path(config)
|
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
|
except (configparser.NoOptionError, ImportError):
|
||||||
try:
|
logger.warning("Keyring config file contains incorrect values.\n" +
|
||||||
if config.has_section("backend"):
|
"Config file: %s" % keyring_cfg)
|
||||||
keyring_name = config.get("backend", "default-keyring").strip()
|
return
|
||||||
else:
|
|
||||||
raise configparser.NoOptionError('backend', 'default-keyring')
|
|
||||||
|
|
||||||
keyring = load_keyring(None, keyring_name)
|
return load_keyring(keyring_name)
|
||||||
except (configparser.NoOptionError, ImportError):
|
|
||||||
logger.warning("Keyring config file contains incorrect values.\n" +
|
|
||||||
"Config file: %s" % keyring_cfg)
|
|
||||||
|
|
||||||
return keyring
|
|
||||||
|
|
||||||
def _load_keyring_path(config):
|
def _load_keyring_path(config):
|
||||||
"load the keyring-path option (if present)"
|
"load the keyring-path option (if present)"
|
||||||
|
|||||||
@@ -10,17 +10,23 @@ import os
|
|||||||
import tempfile
|
import tempfile
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from keyring.tests.py30compat import unittest
|
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import pytest
|
||||||
|
|
||||||
import keyring.backend
|
import keyring.backend
|
||||||
import keyring.core
|
import keyring.core
|
||||||
|
import keyring.util.platform_
|
||||||
from keyring import errors
|
from keyring import errors
|
||||||
|
|
||||||
PASSWORD_TEXT = "This is password"
|
PASSWORD_TEXT = "This is password"
|
||||||
PASSWORD_TEXT_2 = "This is password2"
|
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):
|
class TestKeyring(keyring.backend.KeyringBackend):
|
||||||
@@ -53,7 +59,7 @@ class TestKeyring2(TestKeyring):
|
|||||||
return PASSWORD_TEXT_2
|
return PASSWORD_TEXT_2
|
||||||
|
|
||||||
|
|
||||||
class CoreTestCase(unittest.TestCase):
|
class TestCore:
|
||||||
mock_global_backend = mock.patch('keyring.core._keyring_backend')
|
mock_global_backend = mock.patch('keyring.core._keyring_backend')
|
||||||
|
|
||||||
@mock_global_backend
|
@mock_global_backend
|
||||||
@@ -72,7 +78,7 @@ class CoreTestCase(unittest.TestCase):
|
|||||||
"""
|
"""
|
||||||
result = keyring.core.get_password("test", "user")
|
result = keyring.core.get_password("test", "user")
|
||||||
backend.get_password.assert_called_once_with('test', 'user')
|
backend.get_password.assert_called_once_with('test', 'user')
|
||||||
self.assertIsNotNone(result)
|
assert result is not None
|
||||||
|
|
||||||
@mock_global_backend
|
@mock_global_backend
|
||||||
def test_delete_password(self, backend):
|
def test_delete_password(self, backend):
|
||||||
@@ -85,71 +91,40 @@ class CoreTestCase(unittest.TestCase):
|
|||||||
keyring.core.set_keyring(TestKeyring())
|
keyring.core.set_keyring(TestKeyring())
|
||||||
|
|
||||||
keyring.core.set_password("test", "user", "password")
|
keyring.core.set_password("test", "user", "password")
|
||||||
self.assertEqual(keyring.core.get_password("test", "user"),
|
assert keyring.core.get_password("test", "user") == PASSWORD_TEXT
|
||||||
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.
|
"""Test setting the keyring by config file.
|
||||||
"""
|
"""
|
||||||
# create the config file
|
# create the config file
|
||||||
config_file = open(KEYRINGRC, 'w')
|
with open(config_filename, 'w') as config_file:
|
||||||
config_file.writelines([
|
config_file.writelines([
|
||||||
"[backend]\n",
|
"[backend]\n",
|
||||||
# the path for the user created keyring
|
# the path for the user created keyring
|
||||||
"keyring-path= %s\n" % os.path.dirname(os.path.abspath(__file__)),
|
"keyring-path= %s\n" % os.path.dirname(os.path.abspath(__file__)),
|
||||||
# the name of the keyring class
|
# the name of the keyring class
|
||||||
"default-keyring=test_core.TestKeyring2\n",
|
"default-keyring=test_core.TestKeyring2\n",
|
||||||
])
|
])
|
||||||
config_file.close()
|
|
||||||
|
|
||||||
# init the keyring lib, the lib will automaticlly load the
|
# init the keyring lib, the lib will automaticlly load the
|
||||||
# config file and load the user defined module
|
# config file and load the user defined module
|
||||||
keyring.core.init_backend()
|
keyring.core.init_backend()
|
||||||
|
|
||||||
keyring.core.set_password("test", "user", "password")
|
keyring.core.set_password("test", "user", "password")
|
||||||
self.assertEqual(keyring.core.get_password("test", "user"),
|
assert keyring.core.get_password("test", "user") == PASSWORD_TEXT_2
|
||||||
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):
|
def test_load_config_degenerate(self, config_filename):
|
||||||
tempdir = tempfile.mkdtemp()
|
"load_config should succeed in the absence of a backend section"
|
||||||
old_location = os.getcwd()
|
with open(config_filename, 'w') as config_file:
|
||||||
os.chdir(tempdir)
|
config_file.write('[keyring]')
|
||||||
personal_cfg = os.path.join(os.path.expanduser("~"), "keyringrc.cfg")
|
assert keyring.core.load_config() is None
|
||||||
if os.path.exists(personal_cfg):
|
|
||||||
os.rename(personal_cfg, personal_cfg + '.old')
|
|
||||||
personal_renamed = True
|
|
||||||
else:
|
|
||||||
personal_renamed = False
|
|
||||||
|
|
||||||
# loading with an empty environment
|
def test_load_config_blank_backend(self, config_filename):
|
||||||
keyring.core.load_config()
|
"load_config should succeed with an empty [backend] section"
|
||||||
|
with open(config_filename, 'w') as config_file:
|
||||||
# loading with a file that doesn't have a backend section
|
config_file.write('[backend]')
|
||||||
cfg = os.path.join(tempdir, "keyringrc.cfg")
|
assert keyring.core.load_config() is None
|
||||||
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")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user