summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
authorDean Troyer <dtroyer@gmail.com>2014-10-09 15:16:07 -0500
committerDean Troyer <dtroyer@gmail.com>2014-10-12 16:48:43 -0500
commitc3c6edbe8a083aef0fb6aea3cb461ff8e715fc59 (patch)
tree250d81e1b649d39bbabb2d84cae3ba1c27f575a0 /openstackclient
parent0c77a9fe8baa4df9ea2d0055db9c700af3cae310 (diff)
downloadpython-openstackclient-c3c6edbe8a083aef0fb6aea3cb461ff8e715fc59.tar.gz
Add plugin to support token-endpoint auth
The ksc auth plugins do not have support for the original token-endpoint (aka token flow) auth where the user supplies a token (possibly the Keystone admin_token) and an API endpoint. This is used for bootstrapping Keystone but also has other uses when a scoped user token is provided. The api.auth:TokenEndpoint class is required to provide the same interface methods so all of the special-case code branches to support token-endpoint can be removed. Some additional cleanups related to ClientManager and creating the Compute client also were done to streamline using sessions. Change-Id: I1a6059afa845a591eff92567ca346c09010a93af
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/auth.py69
-rw-r--r--openstackclient/common/clientmanager.py41
-rw-r--r--openstackclient/compute/client.py21
-rw-r--r--openstackclient/tests/common/test_clientmanager.py48
4 files changed, 121 insertions, 58 deletions
diff --git a/openstackclient/api/auth.py b/openstackclient/api/auth.py
index 2bd5271f..e33b72d5 100644
--- a/openstackclient/api/auth.py
+++ b/openstackclient/api/auth.py
@@ -18,6 +18,8 @@ import logging
import stevedore
+from oslo.config import cfg
+
from keystoneclient.auth import base
from openstackclient.common import exceptions as exc
@@ -53,14 +55,14 @@ for plugin in PLUGIN_LIST:
)
-def _guess_authentication_method(options):
+def select_auth_plugin(options):
"""If no auth plugin was specified, pick one based on other options"""
- if options.os_url:
- # service token authentication, do nothing
- return
auth_plugin = None
- if options.os_password:
+ if options.os_url and options.os_token:
+ # service token authentication
+ auth_plugin = 'token_endpoint'
+ elif options.os_password:
if options.os_identity_api_version == '3':
auth_plugin = 'v3password'
elif options.os_identity_api_version == '2.0':
@@ -83,14 +85,13 @@ def _guess_authentication_method(options):
)
LOG.debug("No auth plugin selected, picking %s from other "
"options" % auth_plugin)
- options.os_auth_plugin = auth_plugin
+ return auth_plugin
def build_auth_params(cmd_options):
auth_params = {}
- if cmd_options.os_url:
- return {'token': cmd_options.os_token}
if cmd_options.os_auth_plugin:
+ LOG.debug('auth_plugin: %s', cmd_options.os_auth_plugin)
auth_plugin = base.get_plugin_class(cmd_options.os_auth_plugin)
plugin_options = auth_plugin.get_options()
for option in plugin_options:
@@ -110,6 +111,7 @@ def build_auth_params(cmd_options):
None,
)
else:
+ LOG.debug('no auth_plugin')
# delay the plugin choice, grab every option
plugin_options = set([o.replace('-', '_') for o in OPTIONS_LIST])
for option in plugin_options:
@@ -178,3 +180,54 @@ def build_auth_plugins_option_parser(parser):
help=argparse.SUPPRESS,
)
return parser
+
+
+class TokenEndpoint(base.BaseAuthPlugin):
+ """Auth plugin to handle traditional token/endpoint usage
+
+ Implements the methods required to handle token authentication
+ with a user-specified token and service endpoint; no Identity calls
+ are made for re-scoping, service catalog lookups or the like.
+
+ The purpose of this plugin is to get rid of the special-case paths
+ in the code to handle this authentication format. Its primary use
+ is for bootstrapping the Keystone database.
+ """
+
+ def __init__(self, url, token, **kwargs):
+ """A plugin for static authentication with an existing token
+
+ :param string url: Service endpoint
+ :param string token: Existing token
+ """
+ super(TokenEndpoint, self).__init__()
+ self.endpoint = url
+ self.token = token
+
+ def get_endpoint(self, session, **kwargs):
+ """Return the supplied endpoint"""
+ return self.endpoint
+
+ def get_token(self, session):
+ """Return the supplied token"""
+ return self.token
+
+ def get_auth_ref(self, session, **kwargs):
+ """Stub this method for compatibility"""
+ return None
+
+ # Override this because it needs to be a class method...
+ @classmethod
+ def get_options(self):
+ options = super(TokenEndpoint, self).get_options()
+
+ options.extend([
+ # Maintain name 'url' for compatibility
+ cfg.StrOpt('url',
+ help='Specific service endpoint to use'),
+ cfg.StrOpt('token',
+ secret=True,
+ help='Authentication token to use'),
+ ])
+
+ return options
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 0542b473..bcb81990 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -54,9 +54,10 @@ class ClientManager(object):
return self._auth_params[name[1:]]
def __init__(self, auth_options, api_version=None, verify=True):
-
+ # If no plugin is named by the user, select one based on
+ # the supplied options
if not auth_options.os_auth_plugin:
- auth._guess_authentication_method(auth_options)
+ auth_options.os_auth_plugin = auth.select_auth_plugin(auth_options)
self._auth_plugin = auth_options.os_auth_plugin
self._url = auth_options.os_url
@@ -66,7 +67,7 @@ class ClientManager(object):
self._service_catalog = None
self.timing = auth_options.timing
- # For compatability until all clients can be updated
+ # For compatibility until all clients can be updated
if 'project_name' in self._auth_params:
self._project_name = self._auth_params['project_name']
elif 'tenant_name' in self._auth_params:
@@ -86,27 +87,25 @@ class ClientManager(object):
root_logger = logging.getLogger('')
LOG.setLevel(root_logger.getEffectiveLevel())
- self.session = None
- if not self._url:
- LOG.debug('Using auth plugin: %s' % self._auth_plugin)
- auth_plugin = base.get_plugin_class(self._auth_plugin)
- self.auth = auth_plugin.load_from_options(**self._auth_params)
- # needed by SAML authentication
- request_session = requests.session()
- self.session = session.Session(
- auth=self.auth,
- session=request_session,
- verify=verify,
- )
+ LOG.debug('Using auth plugin: %s' % self._auth_plugin)
+ auth_plugin = base.get_plugin_class(self._auth_plugin)
+ self.auth = auth_plugin.load_from_options(**self._auth_params)
+ # needed by SAML authentication
+ request_session = requests.session()
+ self.session = session.Session(
+ auth=self.auth,
+ session=request_session,
+ verify=verify,
+ )
self.auth_ref = None
- if not self._auth_plugin.endswith("token") and not self._url:
- LOG.debug("Populate other password flow attributes")
- self.auth_ref = self.session.auth.get_auth_ref(self.session)
- self._token = self.session.auth.get_token(self.session)
+ if 'token' not in self._auth_params:
+ LOG.debug("Get service catalog")
+ self.auth_ref = self.auth.get_auth_ref(self.session)
self._service_catalog = self.auth_ref.service_catalog
- else:
- self._token = self._auth_params.get('token')
+
+ # This begone when clients no longer need it...
+ self._token = self.auth.get_token(self.session)
return
diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py
index dc50507e..6c03d24e 100644
--- a/openstackclient/compute/client.py
+++ b/openstackclient/compute/client.py
@@ -44,33 +44,20 @@ def make_client(instance):
extensions = [extension.Extension('list_extensions', list_extensions)]
client = compute_client(
- username=instance._username,
- api_key=instance._password,
- project_id=instance._project_name,
- auth_url=instance._auth_url,
- cacert=instance._cacert,
- insecure=instance._insecure,
- region_name=instance._region_name,
- # FIXME(dhellmann): get endpoint_type from option?
- endpoint_type='publicURL',
+ session=instance.session,
extensions=extensions,
- service_type=API_NAME,
- # FIXME(dhellmann): what is service_name?
- service_name='',
http_log_debug=http_log_debug,
timings=instance.timing,
)
# Populate the Nova client to skip another auth query to Identity
- if instance._url:
- # token flow
- client.client.management_url = instance._url
- else:
+ if 'token' not in instance._auth_params:
# password flow
client.client.management_url = instance.get_endpoint_for_service_type(
API_NAME)
client.client.service_catalog = instance._service_catalog
- client.client.auth_token = instance._token
+ client.client.auth_token = instance.auth.get_token(instance.session)
+
return client
diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py
index 18461fb7..5ec86d59 100644
--- a/openstackclient/tests/common/test_clientmanager.py
+++ b/openstackclient/tests/common/test_clientmanager.py
@@ -76,6 +76,31 @@ class TestClientManager(utils.TestCase):
url=fakes.AUTH_URL,
verb='GET')
+ def test_client_manager_token_endpoint(self):
+
+ client_manager = clientmanager.ClientManager(
+ auth_options=FakeOptions(os_token=fakes.AUTH_TOKEN,
+ os_url=fakes.AUTH_URL,
+ os_auth_plugin='token_endpoint'),
+ api_version=API_VERSION,
+ verify=True
+ )
+ self.assertEqual(
+ fakes.AUTH_URL,
+ client_manager._url,
+ )
+
+ self.assertEqual(
+ fakes.AUTH_TOKEN,
+ client_manager._token,
+ )
+ self.assertIsInstance(
+ client_manager.auth,
+ auth.TokenEndpoint,
+ )
+ self.assertFalse(client_manager._insecure)
+ self.assertTrue(client_manager._verify)
+
def test_client_manager_token(self):
client_manager = clientmanager.ClientManager(
@@ -176,8 +201,7 @@ class TestClientManager(utils.TestCase):
self.assertTrue(client_manager._verify)
self.assertEqual('cafile', client_manager._cacert)
- def _client_manager_guess_auth_plugin(self, auth_params,
- api_version, auth_plugin):
+ def _select_auth_plugin(self, auth_params, api_version, auth_plugin):
auth_params['os_auth_plugin'] = auth_plugin
auth_params['os_identity_api_version'] = api_version
client_manager = clientmanager.ClientManager(
@@ -190,25 +214,25 @@ class TestClientManager(utils.TestCase):
client_manager._auth_plugin,
)
- def test_client_manager_guess_auth_plugin(self):
+ def test_client_manager_select_auth_plugin(self):
# test token auth
params = dict(os_token=fakes.AUTH_TOKEN,
os_auth_url=fakes.AUTH_URL)
- self._client_manager_guess_auth_plugin(params, '2.0', 'v2token')
- self._client_manager_guess_auth_plugin(params, '3', 'v3token')
- self._client_manager_guess_auth_plugin(params, 'XXX', 'token')
- # test service auth
+ self._select_auth_plugin(params, '2.0', 'v2token')
+ self._select_auth_plugin(params, '3', 'v3token')
+ self._select_auth_plugin(params, 'XXX', 'token')
+ # test token/endpoint auth
params = dict(os_token=fakes.AUTH_TOKEN, os_url='test')
- self._client_manager_guess_auth_plugin(params, 'XXX', '')
+ self._select_auth_plugin(params, 'XXX', 'token_endpoint')
# test password auth
params = dict(os_auth_url=fakes.AUTH_URL,
os_username=fakes.USERNAME,
os_password=fakes.PASSWORD)
- self._client_manager_guess_auth_plugin(params, '2.0', 'v2password')
- self._client_manager_guess_auth_plugin(params, '3', 'v3password')
- self._client_manager_guess_auth_plugin(params, 'XXX', 'password')
+ self._select_auth_plugin(params, '2.0', 'v2password')
+ self._select_auth_plugin(params, '3', 'v3password')
+ self._select_auth_plugin(params, 'XXX', 'password')
- def test_client_manager_guess_auth_plugin_failure(self):
+ def test_client_manager_select_auth_plugin_failure(self):
self.assertRaises(exc.CommandError,
clientmanager.ClientManager,
auth_options=FakeOptions(os_auth_plugin=''),