From fe0c2f926ab1cb73589d72136f8dae0516c9f884 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Wed, 21 May 2014 08:45:54 +1000 Subject: [PATCH] Session loading from conf Allow loading session objects from oslo.config. We want a generic way to do this for auth_token middleware and for servers creating session objects for inter-service communication. DocImpact: This is the first step in standardizing all the config options across projects. There are no changes to the config options that keystoneclient actually consumes in this review. Implements: blueprint standard-client-params Change-Id: I1e83280b2f76f16041ed8d5ed598db70210112bd --- keystoneclient/session.py | 134 +++++++++++++++++++++++++-- keystoneclient/tests/test_session.py | 62 +++++++++++++ 2 files changed, 186 insertions(+), 10 deletions(-) diff --git a/keystoneclient/session.py b/keystoneclient/session.py index 743c22672..81d70ce1d 100644 --- a/keystoneclient/session.py +++ b/keystoneclient/session.py @@ -12,6 +12,7 @@ import logging +from oslo.config import cfg import requests import six from six.moves import urllib @@ -355,12 +356,26 @@ class Session(object): functionswithout session arguments. """ - verify = kwargs.pop('verify', None) - cacert = kwargs.pop('cacert', None) - cert = kwargs.pop('cert', None) - key = kwargs.pop('key', None) - insecure = kwargs.pop('insecure', False) + params = {} + for attr in ('verify', 'cacert', 'cert', 'key', 'insecure', + 'timeout', 'session', 'original_ip', 'user_agent'): + try: + params[attr] = kwargs.pop(attr) + except KeyError: + pass + + return cls._make(**params) + + @classmethod + def _make(cls, insecure=False, verify=None, cacert=None, cert=None, + key=None, **kwargs): + """Create a session with individual certificate parameters. + + Some parameters used to create a session don't lend themselves to be + loaded from config/CLI etc. Create a session by converting those + parameters into session __init__ parameters. + """ if verify is None: if insecure: verify = False @@ -372,11 +387,7 @@ class Session(object): # requests lib form of having the cert and key as a tuple cert = (cert, key) - return cls(verify=verify, cert=cert, - timeout=kwargs.pop('timeout', None), - session=kwargs.pop('session', None), - original_ip=kwargs.pop('original_ip', None), - user_agent=kwargs.pop('user_agent', None)) + return cls(verify=verify, cert=cert, **kwargs) def get_token(self, auth=None): """Return a token as provided by the auth plugin. @@ -432,3 +443,106 @@ class Session(object): raise exceptions.MissingAuthPlugin(msg) return auth.invalidate() + + @utils.positional.classmethod() + def get_conf_options(cls, deprecated_opts=None): + """Get the oslo.config options that are needed for a session. + + These may be useful without being registered for config file generation + or to manipulate the options before registering them yourself. + + The options that are set are: + :cafile: The certificate authority filename. + :certfile: The client certificate file to present. + :keyfile: The key for the client certificate. + :insecure: Whether to ignore SSL verification. + :timeout: The max time to wait for HTTP connections. + + :param dict deprecated_opts: Deprecated options that should be included + in the definition of new options. This should be a dictionary from + the name of the new option to a list of oslo.DeprecatedOpts that + correspond to the new option. (optional) + + Example to support the 'ca_file' option pointing to the new + 'cafile' option name:: + + old_opt = oslo.cfg.DeprecatedOpt('ca_file', 'old_group') + deprecated_opts={'cafile': [old_opt]} + + :returns: A list of oslo.config options. + """ + if deprecated_opts is None: + deprecated_opts = {} + + return [cfg.StrOpt('cafile', + deprecated_opts=deprecated_opts.get('cafile'), + help='PEM encoded Certificate Authority to use ' + 'when verifying HTTPs connections.'), + cfg.StrOpt('certfile', + deprecated_opts=deprecated_opts.get('certfile'), + help='PEM encoded client certificate cert file'), + cfg.StrOpt('keyfile', + deprecated_opts=deprecated_opts.get('keyfile'), + help='PEM encoded client certificate key file'), + cfg.BoolOpt('insecure', + default=False, + deprecated_opts=deprecated_opts.get('insecure'), + help='Verify HTTPS connections.'), + cfg.IntOpt('timeout', + deprecated_opts=deprecated_opts.get('timeout'), + help='Timeout value for http requests'), + ] + + @utils.positional.classmethod() + def register_conf_options(cls, conf, group, deprecated_opts=None): + """Register the oslo.config options that are needed for a session. + + The options that are set are: + :cafile: The certificate authority filename. + :certfile: The client certificate file to present. + :keyfile: The key for the client certificate. + :insecure: Whether to ignore SSL verification. + :timeout: The max time to wait for HTTP connections. + + :param oslo.config.Cfg conf: config object to register with. + :param string group: The ini group to register options in. + :param dict deprecated_opts: Deprecated options that should be included + in the definition of new options. This should be a dictionary from + the name of the new option to a list of oslo.DeprecatedOpts that + correspond to the new option. (optional) + + Example to support the 'ca_file' option pointing to the new + 'cafile' option name:: + + old_opt = oslo.cfg.DeprecatedOpt('ca_file', 'old_group') + deprecated_opts={'cafile': [old_opt]} + + :returns: The list of options that was registered. + """ + opts = cls.get_conf_options(deprecated_opts=deprecated_opts) + conf.register_group(cfg.OptGroup(group)) + conf.register_opts(opts, group=group) + return opts + + @classmethod + def load_from_conf_options(cls, conf, group, **kwargs): + """Create a session object from an oslo.config object. + + The options must have been previously registered with + register_conf_options. + + :param oslo.config.Cfg conf: config object to register with. + :param string group: The ini group to register options in. + :param dict kwargs: Additional parameters to pass to session + construction. + :returns: A new session object. + """ + c = conf[group] + + kwargs['insecure'] = c.insecure + kwargs['cacert'] = c.cafile + kwargs['cert'] = c.certfile + kwargs['key'] = c.keyfile + kwargs['timeout'] = c.timeout + + return cls._make(**kwargs) diff --git a/keystoneclient/tests/test_session.py b/keystoneclient/tests/test_session.py index 98dd990d8..6c5637e83 100644 --- a/keystoneclient/tests/test_session.py +++ b/keystoneclient/tests/test_session.py @@ -14,12 +14,15 @@ import uuid import httpretty import mock +from oslo.config import cfg import requests import six +from testtools import matchers from keystoneclient import adapter from keystoneclient.auth import base from keystoneclient import exceptions +from keystoneclient.openstack.common.fixture import config from keystoneclient.openstack.common import jsonutils from keystoneclient import session as client_session from keystoneclient.tests import utils @@ -599,3 +602,62 @@ class AdapterTest(utils.TestCase): with mock.patch.object(adpt, 'request') as m: getattr(adpt, method)(url) m.assert_called_once_with(url, method.upper()) + + +class ConfLoadingTests(utils.TestCase): + + GROUP = 'sessiongroup' + + def setUp(self): + super(ConfLoadingTests, self).setUp() + + self.conf_fixture = self.useFixture(config.Config()) + client_session.Session.register_conf_options(self.conf_fixture.conf, + self.GROUP) + + def config(self, **kwargs): + kwargs['group'] = self.GROUP + self.conf_fixture.config(**kwargs) + + def get_session(self, **kwargs): + return client_session.Session.load_from_conf_options( + self.conf_fixture.conf, + self.GROUP, + **kwargs) + + def test_insecure_timeout(self): + self.config(insecure=True, timeout=5) + s = self.get_session() + + self.assertFalse(s.verify) + self.assertEqual(5, s.timeout) + + def test_client_certs(self): + cert = '/path/to/certfile' + key = '/path/to/keyfile' + + self.config(certfile=cert, keyfile=key) + s = self.get_session() + + self.assertTrue(s.verify) + self.assertEqual((cert, key), s.cert) + + def test_cacert(self): + cafile = '/path/to/cacert' + + self.config(cafile=cafile) + s = self.get_session() + + self.assertEqual(cafile, s.verify) + + def test_deprecated(self): + def new_deprecated(): + return cfg.DeprecatedOpt(uuid.uuid4().hex, group=uuid.uuid4().hex) + + opt_names = ['cafile', 'certfile', 'keyfile', 'insecure', 'timeout'] + depr = dict([(n, [new_deprecated()]) for n in opt_names]) + opts = client_session.Session.get_conf_options(deprecated_opts=depr) + + self.assertThat(opt_names, matchers.HasLength(len(opts))) + for opt in opts: + self.assertIn(depr[opt.name][0], opt.deprecated_opts)