Plugin loading from config objects
Provide a pattern for auth plugins to load themselves from a config object. The first user of this will be auth_token middleware however it is not likely to be the only user. By doing this in an exportable way we are defining a single config file format for specifying how to load a plugin for all services. We also provide a standard way of retrieving a plugins options for loading via other mechanisms. Blueprint: standard-client-params Change-Id: I353b26a1ffc04a20666e76f5bd2f1e6d7c19a22d
This commit is contained in:
@@ -13,6 +13,31 @@
|
|||||||
import abc
|
import abc
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
import stevedore
|
||||||
|
|
||||||
|
from keystoneclient import exceptions
|
||||||
|
|
||||||
|
PLUGIN_NAMESPACE = 'keystoneclient.auth.plugin'
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin_class(name):
|
||||||
|
"""Retrieve a plugin class by its entrypoint name.
|
||||||
|
|
||||||
|
:param str name: The name of the object to get.
|
||||||
|
|
||||||
|
:returns: An auth plugin class.
|
||||||
|
|
||||||
|
:raises exceptions.NoMatchingPlugin: if a plugin cannot be created.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
mgr = stevedore.DriverManager(namespace=PLUGIN_NAMESPACE,
|
||||||
|
name=name,
|
||||||
|
invoke_on_load=False)
|
||||||
|
except RuntimeError:
|
||||||
|
msg = 'The plugin %s could not be found' % name
|
||||||
|
raise exceptions.NoMatchingPlugin(msg)
|
||||||
|
|
||||||
|
return mgr.driver
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
@@ -70,3 +95,24 @@ class BaseAuthPlugin(object):
|
|||||||
If nothing happens returns False to indicate give up.
|
If nothing happens returns False to indicate give up.
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
"""Return the list of parameters associated with the auth plugin.
|
||||||
|
|
||||||
|
This list may be used to generate CLI or config arguments.
|
||||||
|
|
||||||
|
:returns list: A list of Param objects describing available plugin
|
||||||
|
parameters.
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_from_options(cls, **kwargs):
|
||||||
|
"""Create a plugin from the arguments retrieved from get_options.
|
||||||
|
|
||||||
|
A client can override this function to do argument validation or to
|
||||||
|
handle differences between the registered options and what is required
|
||||||
|
to create the plugin.
|
||||||
|
"""
|
||||||
|
return cls(**kwargs)
|
||||||
|
116
keystoneclient/auth/conf.py
Normal file
116
keystoneclient/auth/conf.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from keystoneclient.auth import base
|
||||||
|
from keystoneclient import exceptions
|
||||||
|
|
||||||
|
_AUTH_PLUGIN_OPT = cfg.StrOpt('auth_plugin', help='Name of the plugin to load')
|
||||||
|
|
||||||
|
_section_help = 'Config Section from which to load plugin specific options'
|
||||||
|
_AUTH_SECTION_OPT = cfg.StrOpt('auth_section', help=_section_help)
|
||||||
|
|
||||||
|
|
||||||
|
def get_common_conf_options():
|
||||||
|
"""Get the oslo.config options common for all auth plugins.
|
||||||
|
|
||||||
|
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:
|
||||||
|
:auth_plugin: The name of the pluign to load.
|
||||||
|
:auth_section: The config file section to load options from.
|
||||||
|
|
||||||
|
:returns: A list of oslo.config options.
|
||||||
|
"""
|
||||||
|
return [_AUTH_PLUGIN_OPT, _AUTH_SECTION_OPT]
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugin_options(name):
|
||||||
|
"""Get the oslo.config options for a specific plugin.
|
||||||
|
|
||||||
|
This will be the list of config options that is registered and loaded by
|
||||||
|
the specified plugin.
|
||||||
|
|
||||||
|
:returns: A list of oslo.config options.
|
||||||
|
"""
|
||||||
|
return base.get_plugin_class(name).get_options()
|
||||||
|
|
||||||
|
|
||||||
|
def register_conf_options(conf, group):
|
||||||
|
"""Register the oslo.config options that are needed for a plugin.
|
||||||
|
|
||||||
|
This only registers the basic options shared by all plugins. Options that
|
||||||
|
are specific to a plugin are loaded just before they are read.
|
||||||
|
|
||||||
|
The defined options are:
|
||||||
|
|
||||||
|
- auth_plugin: the name of the auth plugin that will be used for
|
||||||
|
authentication.
|
||||||
|
- auth_section: the group from which further auth plugin options should be
|
||||||
|
taken. If section is not provided then the auth plugin options will be
|
||||||
|
taken from the same group as provided in the parameters.
|
||||||
|
|
||||||
|
:param oslo.config.Cfg conf: config object to register with.
|
||||||
|
:param string group: The ini group to register options in.
|
||||||
|
"""
|
||||||
|
conf.register_opt(_AUTH_SECTION_OPT, group=group)
|
||||||
|
|
||||||
|
# NOTE(jamielennox): plugins are allowed to specify a 'section' which is
|
||||||
|
# the group that auth options should be taken from. If not present they
|
||||||
|
# come from the same as the base options were registered in. If present
|
||||||
|
# then the auth_plugin option may be read from that section so add that
|
||||||
|
# option.
|
||||||
|
if conf[group].auth_section:
|
||||||
|
group = conf[group].auth_section
|
||||||
|
|
||||||
|
conf.register_opt(_AUTH_PLUGIN_OPT, group=group)
|
||||||
|
|
||||||
|
|
||||||
|
def load_from_conf_options(conf, group, **kwargs):
|
||||||
|
"""Load a plugin from an oslo.config CONF object.
|
||||||
|
|
||||||
|
Each plugin will register there own required options and so there is no
|
||||||
|
standard list and the plugin should be consulted.
|
||||||
|
|
||||||
|
The base options should have been registered with register_conf_options
|
||||||
|
before this function is called.
|
||||||
|
|
||||||
|
:param conf: An oslo.config conf object.
|
||||||
|
:param string group: The group name that options should be read from.
|
||||||
|
|
||||||
|
:returns plugin: An authentication Plugin.
|
||||||
|
|
||||||
|
:raises exceptions.NoMatchingPlugin: if a plugin cannot be created.
|
||||||
|
"""
|
||||||
|
# NOTE(jamielennox): plugins are allowed to specify a 'section' which is
|
||||||
|
# the group that auth options should be taken from. If not present they
|
||||||
|
# come from the same as the base options were registered in.
|
||||||
|
if conf[group].auth_section:
|
||||||
|
group = conf[group].auth_section
|
||||||
|
|
||||||
|
name = conf[group].auth_plugin
|
||||||
|
if not name:
|
||||||
|
raise exceptions.NoMatchingPlugin('No plugin name provided for config')
|
||||||
|
|
||||||
|
plugin_class = base.get_plugin_class(name)
|
||||||
|
plugin_opts = plugin_class.get_options()
|
||||||
|
conf.register_opts(plugin_opts, group=group)
|
||||||
|
|
||||||
|
for opt in plugin_opts:
|
||||||
|
val = conf[group][opt.dest]
|
||||||
|
if val is not None:
|
||||||
|
val = opt.type(val)
|
||||||
|
kwargs.setdefault(opt.dest, val)
|
||||||
|
|
||||||
|
return plugin_class.load_from_options(**kwargs)
|
@@ -13,6 +13,7 @@
|
|||||||
import abc
|
import abc
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from keystoneclient import _discover
|
from keystoneclient import _discover
|
||||||
@@ -178,3 +179,13 @@ class BaseIdentityPlugin(base.BaseAuthPlugin):
|
|||||||
session_endpoint_cache[sc_url] = disc
|
session_endpoint_cache[sc_url] = disc
|
||||||
|
|
||||||
return disc.url_for(version)
|
return disc.url_for(version)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
options = super(BaseIdentityPlugin, cls).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('auth-url', help='Authentication URL'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
import abc
|
import abc
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from keystoneclient import access
|
from keystoneclient import access
|
||||||
@@ -23,6 +24,18 @@ from keystoneclient import utils
|
|||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class Auth(base.BaseIdentityPlugin):
|
class Auth(base.BaseIdentityPlugin):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
options = super(Auth, cls).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('tenant-id', help='Tenant ID'),
|
||||||
|
cfg.StrOpt('tenant-name', help='Tenant Name'),
|
||||||
|
cfg.StrOpt('trust-id', help='Trust ID'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
@utils.positional()
|
@utils.positional()
|
||||||
def __init__(self, auth_url,
|
def __init__(self, auth_url,
|
||||||
trust_id=None,
|
trust_id=None,
|
||||||
@@ -90,6 +103,20 @@ class Password(Auth):
|
|||||||
return {'passwordCredentials': {'username': self.username,
|
return {'passwordCredentials': {'username': self.username,
|
||||||
'password': self.password}}
|
'password': self.password}}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
options = super(Password, cls).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('user-name',
|
||||||
|
dest='username',
|
||||||
|
deprecated_name='username',
|
||||||
|
help='Username to login with'),
|
||||||
|
cfg.StrOpt('password', help='Password to use'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
class Token(Auth):
|
class Token(Auth):
|
||||||
|
|
||||||
@@ -106,3 +133,13 @@ class Token(Auth):
|
|||||||
if headers is not None:
|
if headers is not None:
|
||||||
headers['X-Auth-Token'] = self.token
|
headers['X-Auth-Token'] = self.token
|
||||||
return {'token': {'id': self.token}}
|
return {'token': {'id': self.token}}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
options = super(Token, cls).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('token', help='Token'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
@@ -13,6 +13,7 @@
|
|||||||
import abc
|
import abc
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from keystoneclient import access
|
from keystoneclient import access
|
||||||
@@ -115,6 +116,24 @@ class Auth(base.BaseIdentityPlugin):
|
|||||||
return access.AccessInfoV3(resp.headers['X-Subject-Token'],
|
return access.AccessInfoV3(resp.headers['X-Subject-Token'],
|
||||||
**resp_data)
|
**resp_data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
options = super(Auth, cls).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('domain-id', help='Domain ID to scope to'),
|
||||||
|
cfg.StrOpt('domain-name', help='Domain name to scope to'),
|
||||||
|
cfg.StrOpt('project-id', help='Project ID to scope to'),
|
||||||
|
cfg.StrOpt('project-name', help='Project name to scope to'),
|
||||||
|
cfg.StrOpt('project-domain-id',
|
||||||
|
help='Domain ID containing project'),
|
||||||
|
cfg.StrOpt('project-domain-name',
|
||||||
|
help='Domain name containing project'),
|
||||||
|
cfg.StrOpt('trust-id', help='Trust ID'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class AuthMethod(object):
|
class AuthMethod(object):
|
||||||
@@ -214,6 +233,21 @@ class PasswordMethod(AuthMethod):
|
|||||||
class Password(AuthConstructor):
|
class Password(AuthConstructor):
|
||||||
_auth_method_class = PasswordMethod
|
_auth_method_class = PasswordMethod
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
options = super(Password, cls).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('user-id', help='User ID'),
|
||||||
|
cfg.StrOpt('user-name', dest='username', help='Username',
|
||||||
|
deprecated_name='username'),
|
||||||
|
cfg.StrOpt('user-domain-id', help="User's domain id"),
|
||||||
|
cfg.StrOpt('user-domain-name', help="User's domain name"),
|
||||||
|
cfg.StrOpt('password', help="User's password"),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
class TokenMethod(AuthMethod):
|
class TokenMethod(AuthMethod):
|
||||||
|
|
||||||
@@ -236,3 +270,13 @@ class Token(AuthConstructor):
|
|||||||
|
|
||||||
def __init__(self, auth_url, token, **kwargs):
|
def __init__(self, auth_url, token, **kwargs):
|
||||||
super(Token, self).__init__(auth_url, token=token, **kwargs)
|
super(Token, self).__init__(auth_url, token=token, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
options = super(Token, cls).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('token', help='Token to authenticate with'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
@@ -37,3 +37,14 @@ class Token(base.BaseAuthPlugin):
|
|||||||
parameters passed to the plugin.
|
parameters passed to the plugin.
|
||||||
"""
|
"""
|
||||||
return self.endpoint
|
return self.endpoint
|
||||||
|
|
||||||
|
def get_options(self):
|
||||||
|
options = super(Token, self).get_options()
|
||||||
|
|
||||||
|
options.extend([
|
||||||
|
cfg.StrOpt('endpoint',
|
||||||
|
help='The endpoint that will always be used'),
|
||||||
|
cfg.StrOpt('token', help='The token that will always be used'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return options
|
||||||
|
176
keystoneclient/tests/auth/test_conf.py
Normal file
176
keystoneclient/tests/auth/test_conf.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslo.config import cfg
|
||||||
|
import stevedore
|
||||||
|
|
||||||
|
from keystoneclient.auth import base
|
||||||
|
from keystoneclient.auth import conf
|
||||||
|
from keystoneclient.auth.identity import v2 as v2_auth
|
||||||
|
from keystoneclient.auth.identity import v3 as v3_auth
|
||||||
|
from keystoneclient import exceptions
|
||||||
|
from keystoneclient.openstack.common.fixture import config
|
||||||
|
from keystoneclient.tests.auth import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ConfTests(utils.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ConfTests, self).setUp()
|
||||||
|
self.conf_fixture = self.useFixture(config.Config())
|
||||||
|
|
||||||
|
# NOTE(jamielennox): we register the basic config options first because
|
||||||
|
# we need them in place before we can stub them. We will need to run
|
||||||
|
# the register again after we stub the auth section and auth plugin so
|
||||||
|
# it can load the plugin specific options.
|
||||||
|
conf.register_conf_options(self.conf_fixture.conf, group=self.GROUP)
|
||||||
|
|
||||||
|
def test_loading_v2(self):
|
||||||
|
section = uuid.uuid4().hex
|
||||||
|
username = uuid.uuid4().hex
|
||||||
|
password = uuid.uuid4().hex
|
||||||
|
trust_id = uuid.uuid4().hex
|
||||||
|
tenant_id = uuid.uuid4().hex
|
||||||
|
|
||||||
|
self.conf_fixture.config(auth_section=section, group=self.GROUP)
|
||||||
|
conf.register_conf_options(self.conf_fixture.conf, group=self.GROUP)
|
||||||
|
|
||||||
|
self.conf_fixture.register_opts(v2_auth.Password.get_options(),
|
||||||
|
group=section)
|
||||||
|
|
||||||
|
self.conf_fixture.config(auth_plugin=self.V2PASS,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
trust_id=trust_id,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
group=section)
|
||||||
|
|
||||||
|
a = conf.load_from_conf_options(self.conf_fixture.conf, self.GROUP)
|
||||||
|
|
||||||
|
self.assertEqual(username, a.username)
|
||||||
|
self.assertEqual(password, a.password)
|
||||||
|
self.assertEqual(trust_id, a.trust_id)
|
||||||
|
self.assertEqual(tenant_id, a.tenant_id)
|
||||||
|
|
||||||
|
def test_loading_v3(self):
|
||||||
|
section = uuid.uuid4().hex
|
||||||
|
token = uuid.uuid4().hex
|
||||||
|
trust_id = uuid.uuid4().hex
|
||||||
|
project_id = uuid.uuid4().hex
|
||||||
|
project_domain_name = uuid.uuid4().hex
|
||||||
|
|
||||||
|
self.conf_fixture.config(auth_section=section, group=self.GROUP)
|
||||||
|
conf.register_conf_options(self.conf_fixture.conf, group=self.GROUP)
|
||||||
|
|
||||||
|
self.conf_fixture.register_opts(v3_auth.Token.get_options(),
|
||||||
|
group=section)
|
||||||
|
|
||||||
|
self.conf_fixture.config(auth_plugin=self.V3TOKEN,
|
||||||
|
token=token,
|
||||||
|
trust_id=trust_id,
|
||||||
|
project_id=project_id,
|
||||||
|
project_domain_name=project_domain_name,
|
||||||
|
group=section)
|
||||||
|
|
||||||
|
a = conf.load_from_conf_options(self.conf_fixture.conf, self.GROUP)
|
||||||
|
|
||||||
|
self.assertEqual(token, a.auth_methods[0].token)
|
||||||
|
self.assertEqual(trust_id, a.trust_id)
|
||||||
|
self.assertEqual(project_id, a.project_id)
|
||||||
|
self.assertEqual(project_domain_name, a.project_domain_name)
|
||||||
|
|
||||||
|
def test_loading_invalid_plugin(self):
|
||||||
|
self.conf_fixture.config(auth_plugin=uuid.uuid4().hex,
|
||||||
|
group=self.GROUP)
|
||||||
|
|
||||||
|
self.assertRaises(exceptions.NoMatchingPlugin,
|
||||||
|
conf.load_from_conf_options,
|
||||||
|
self.conf_fixture.conf,
|
||||||
|
self.GROUP)
|
||||||
|
|
||||||
|
def test_loading_with_no_data(self):
|
||||||
|
self.assertRaises(exceptions.NoMatchingPlugin,
|
||||||
|
conf.load_from_conf_options,
|
||||||
|
self.conf_fixture.conf,
|
||||||
|
self.GROUP)
|
||||||
|
|
||||||
|
@mock.patch('stevedore.DriverManager')
|
||||||
|
def test_other_params(self, m):
|
||||||
|
m.return_value = utils.MockManager(utils.MockPlugin)
|
||||||
|
driver_name = uuid.uuid4().hex
|
||||||
|
|
||||||
|
self.conf_fixture.register_opts(utils.MockPlugin.get_options(),
|
||||||
|
group=self.GROUP)
|
||||||
|
self.conf_fixture.config(auth_plugin=driver_name,
|
||||||
|
group=self.GROUP,
|
||||||
|
**self.TEST_VALS)
|
||||||
|
|
||||||
|
a = conf.load_from_conf_options(self.conf_fixture.conf, self.GROUP)
|
||||||
|
self.assertTestVals(a)
|
||||||
|
|
||||||
|
m.assert_called_once_with(namespace=base.PLUGIN_NAMESPACE,
|
||||||
|
name=driver_name,
|
||||||
|
invoke_on_load=False)
|
||||||
|
|
||||||
|
@utils.mock_plugin
|
||||||
|
def test_same_section(self, m):
|
||||||
|
self.conf_fixture.register_opts(utils.MockPlugin.get_options(),
|
||||||
|
group=self.GROUP)
|
||||||
|
conf.register_conf_options(self.conf_fixture.conf, group=self.GROUP)
|
||||||
|
self.conf_fixture.config(auth_plugin=uuid.uuid4().hex,
|
||||||
|
group=self.GROUP,
|
||||||
|
**self.TEST_VALS)
|
||||||
|
|
||||||
|
a = conf.load_from_conf_options(self.conf_fixture.conf, self.GROUP)
|
||||||
|
self.assertTestVals(a)
|
||||||
|
|
||||||
|
@utils.mock_plugin
|
||||||
|
def test_diff_section(self, m):
|
||||||
|
section = uuid.uuid4().hex
|
||||||
|
|
||||||
|
self.conf_fixture.config(auth_section=section, group=self.GROUP)
|
||||||
|
conf.register_conf_options(self.conf_fixture.conf, group=self.GROUP)
|
||||||
|
|
||||||
|
self.conf_fixture.register_opts(utils.MockPlugin.get_options(),
|
||||||
|
group=section)
|
||||||
|
self.conf_fixture.config(group=section,
|
||||||
|
auth_plugin=uuid.uuid4().hex,
|
||||||
|
**self.TEST_VALS)
|
||||||
|
|
||||||
|
a = conf.load_from_conf_options(self.conf_fixture.conf, self.GROUP)
|
||||||
|
self.assertTestVals(a)
|
||||||
|
|
||||||
|
def test_plugins_are_all_opts(self):
|
||||||
|
manager = stevedore.ExtensionManager(base.PLUGIN_NAMESPACE,
|
||||||
|
invoke_on_load=False,
|
||||||
|
propagate_map_exceptions=True)
|
||||||
|
|
||||||
|
def inner(driver):
|
||||||
|
for p in driver.plugin.get_options():
|
||||||
|
self.assertIsInstance(p, cfg.Opt)
|
||||||
|
|
||||||
|
manager.map(inner)
|
||||||
|
|
||||||
|
def test_get_common(self):
|
||||||
|
opts = conf.get_common_conf_options()
|
||||||
|
for opt in opts:
|
||||||
|
self.assertIsInstance(opt, cfg.Opt)
|
||||||
|
self.assertEqual(2, len(opts))
|
||||||
|
|
||||||
|
def test_get_named(self):
|
||||||
|
loaded_opts = conf.get_plugin_options('v2password')
|
||||||
|
plugin_opts = v2_auth.Password.get_options()
|
||||||
|
|
||||||
|
self.assertEqual(plugin_opts, loaded_opts)
|
83
keystoneclient/tests/auth/utils.py
Normal file
83
keystoneclient/tests/auth/utils.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import functools
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslo.config import cfg
|
||||||
|
import six
|
||||||
|
|
||||||
|
from keystoneclient.auth import base
|
||||||
|
from keystoneclient.tests import utils
|
||||||
|
|
||||||
|
|
||||||
|
class MockPlugin(base.BaseAuthPlugin):
|
||||||
|
|
||||||
|
INT_DESC = 'test int'
|
||||||
|
FLOAT_DESC = 'test float'
|
||||||
|
BOOL_DESC = 'test bool'
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self._data = kwargs
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self._data[key]
|
||||||
|
|
||||||
|
def get_token(self, *args, **kwargs):
|
||||||
|
return 'aToken'
|
||||||
|
|
||||||
|
def get_endpoint(self, *args, **kwargs):
|
||||||
|
return 'http://test'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_options(cls):
|
||||||
|
return [
|
||||||
|
cfg.IntOpt('a-int', default='3', help=cls.INT_DESC),
|
||||||
|
cfg.BoolOpt('a-bool', help=cls.BOOL_DESC),
|
||||||
|
cfg.FloatOpt('a-float', help=cls.FLOAT_DESC),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MockManager(object):
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
self.driver = driver
|
||||||
|
|
||||||
|
|
||||||
|
def mock_plugin(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
with mock.patch.object(base, 'get_plugin_class') as m:
|
||||||
|
m.return_value = MockPlugin
|
||||||
|
args = list(args) + [m]
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
class TestCase(utils.TestCase):
|
||||||
|
|
||||||
|
GROUP = 'auth'
|
||||||
|
V2PASS = 'v2password'
|
||||||
|
V3TOKEN = 'v3token'
|
||||||
|
|
||||||
|
a_int = 88
|
||||||
|
a_float = 88.8
|
||||||
|
a_bool = False
|
||||||
|
|
||||||
|
TEST_VALS = {'a_int': a_int,
|
||||||
|
'a_float': a_float,
|
||||||
|
'a_bool': a_bool}
|
||||||
|
|
||||||
|
def assertTestVals(self, plugin, vals=TEST_VALS):
|
||||||
|
for k, v in six.iteritems(vals):
|
||||||
|
self.assertEqual(v, plugin[k])
|
@@ -7,3 +7,4 @@ pbr>=0.6,!=0.7,<1.0
|
|||||||
PrettyTable>=0.7,<0.8
|
PrettyTable>=0.7,<0.8
|
||||||
requests>=1.1
|
requests>=1.1
|
||||||
six>=1.7.0
|
six>=1.7.0
|
||||||
|
stevedore>=0.14
|
||||||
|
@@ -31,6 +31,12 @@ setup-hooks =
|
|||||||
console_scripts =
|
console_scripts =
|
||||||
keystone = keystoneclient.shell:main
|
keystone = keystoneclient.shell:main
|
||||||
|
|
||||||
|
keystoneclient.auth.plugin =
|
||||||
|
v2password = keystoneclient.auth.identity.v2:Password
|
||||||
|
v2token = keystoneclient.auth.identity.v2:Token
|
||||||
|
v3password = keystoneclient.auth.identity.v3:Password
|
||||||
|
v3token = keystoneclient.auth.identity.v3:Token
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
source-dir = doc/source
|
source-dir = doc/source
|
||||||
build-dir = doc/build
|
build-dir = doc/build
|
||||||
|
@@ -9,7 +9,6 @@ mox3>=0.7.0
|
|||||||
oauthlib>=0.6
|
oauthlib>=0.6
|
||||||
pycrypto>=2.6
|
pycrypto>=2.6
|
||||||
sphinx>=1.1.2,!=1.2.0,<1.3
|
sphinx>=1.1.2,!=1.2.0,<1.3
|
||||||
stevedore>=0.14
|
|
||||||
testrepository>=0.0.18
|
testrepository>=0.0.18
|
||||||
testresources>=0.2.4
|
testresources>=0.2.4
|
||||||
testtools>=0.9.34
|
testtools>=0.9.34
|
||||||
|
Reference in New Issue
Block a user