From d324530532d5361e85e784c3df2f0d40a128b149 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 23 Jun 2016 15:39:48 -0500 Subject: [PATCH] osc-lib: api.auth Move auth plugin checking to osc-lib. Change-Id: I673d9c2d6e8bbf724c3000459a729e831d747814 --- examples/common.py | 3 +- examples/object_api.py | 4 +- examples/osc-lib.py | 3 +- openstackclient/api/auth.py | 230 +----------------- openstackclient/common/clientmanager.py | 2 +- openstackclient/identity/client.py | 2 +- .../tests/common/test_clientmanager.py | 4 +- 7 files changed, 15 insertions(+), 233 deletions(-) diff --git a/examples/common.py b/examples/common.py index 6d48a8cb2..d472fe6b9 100755 --- a/examples/common.py +++ b/examples/common.py @@ -38,8 +38,7 @@ import sys import traceback from keystoneauth1 import session as ks_session - -from openstackclient.api import auth +from osc_lib.api import auth CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' diff --git a/examples/object_api.py b/examples/object_api.py index 11b62da77..577fc052f 100755 --- a/examples/object_api.py +++ b/examples/object_api.py @@ -25,13 +25,11 @@ import logging import sys import common - +from os_client_config import config as cloud_config from openstackclient.api import object_store_v1 as object_store from openstackclient.identity import client as identity_client -from os_client_config import config as cloud_config - LOG = logging.getLogger('') diff --git a/examples/osc-lib.py b/examples/osc-lib.py index abaa18711..cc04bc70f 100755 --- a/examples/osc-lib.py +++ b/examples/osc-lib.py @@ -26,11 +26,10 @@ import logging import sys import common +from os_client_config import config as cloud_config from openstackclient.common import clientmanager -from os_client_config import config as cloud_config - LOG = logging.getLogger('') diff --git a/openstackclient/api/auth.py b/openstackclient/api/auth.py index 0c82fe9ba..d62a82dc4 100644 --- a/openstackclient/api/auth.py +++ b/openstackclient/api/auth.py @@ -11,229 +11,15 @@ # under the License. # -"""Authentication Library""" +# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release +# or Jun 2017. -import argparse -import logging +import sys -from keystoneauth1.loading import base -from osc_lib import exceptions as exc -from osc_lib import utils - -from openstackclient.i18n import _ - -LOG = logging.getLogger(__name__) - -# Initialize the list of Authentication plugins early in order -# to get the command-line options -PLUGIN_LIST = None - -# List of plugin command line options -OPTIONS_LIST = {} +from osc_lib.api.auth import * # noqa -def get_plugin_list(): - """Gather plugin list and cache it""" - global PLUGIN_LIST - - if PLUGIN_LIST is None: - PLUGIN_LIST = base.get_available_plugin_names() - return PLUGIN_LIST - - -def get_options_list(): - """Gather plugin options so the help action has them available""" - - global OPTIONS_LIST - - if not OPTIONS_LIST: - for plugin_name in get_plugin_list(): - plugin_options = base.get_plugin_options(plugin_name) - for o in plugin_options: - os_name = o.dest.lower().replace('_', '-') - os_env_name = 'OS_' + os_name.upper().replace('-', '_') - OPTIONS_LIST.setdefault( - os_name, {'env': os_env_name, 'help': ''}, - ) - # TODO(mhu) simplistic approach, would be better to only add - # help texts if they vary from one auth plugin to another - # also the text rendering is ugly in the CLI ... - OPTIONS_LIST[os_name]['help'] += 'With %s: %s\n' % ( - plugin_name, - o.help, - ) - return OPTIONS_LIST - - -def select_auth_plugin(options): - """Pick an auth plugin based on --os-auth-type or other options""" - - auth_plugin_name = None - - # Do the token/url check first as this must override the default - # 'password' set by os-client-config - # Also, url and token are not copied into o-c-c's auth dict (yet?) - if options.auth.get('url') and options.auth.get('token'): - # service token authentication - auth_plugin_name = 'token_endpoint' - elif options.auth_type in PLUGIN_LIST: - # A direct plugin name was given, use it - auth_plugin_name = options.auth_type - elif options.auth.get('username'): - if options.identity_api_version == '3': - auth_plugin_name = 'v3password' - elif options.identity_api_version.startswith('2'): - auth_plugin_name = 'v2password' - else: - # let keystoneclient figure it out itself - auth_plugin_name = 'password' - elif options.auth.get('token'): - if options.identity_api_version == '3': - auth_plugin_name = 'v3token' - elif options.identity_api_version.startswith('2'): - auth_plugin_name = 'v2token' - else: - # let keystoneclient figure it out itself - auth_plugin_name = 'token' - else: - # The ultimate default is similar to the original behaviour, - # but this time with version discovery - auth_plugin_name = 'password' - LOG.debug("Auth plugin %s selected", auth_plugin_name) - return auth_plugin_name - - -def build_auth_params(auth_plugin_name, cmd_options): - - if auth_plugin_name: - LOG.debug('auth_type: %s', auth_plugin_name) - auth_plugin_loader = base.get_plugin_loader(auth_plugin_name) - auth_params = {opt.dest: opt.default - for opt in base.get_plugin_options(auth_plugin_name)} - auth_params.update(dict(cmd_options.auth)) - # grab tenant from project for v2.0 API compatibility - if auth_plugin_name.startswith("v2"): - if 'project_id' in auth_params: - auth_params['tenant_id'] = auth_params['project_id'] - del auth_params['project_id'] - if 'project_name' in auth_params: - auth_params['tenant_name'] = auth_params['project_name'] - del auth_params['project_name'] - else: - LOG.debug('no auth_type') - # delay the plugin choice, grab every option - auth_plugin_loader = None - auth_params = dict(cmd_options.auth) - plugin_options = set([o.replace('-', '_') for o in get_options_list()]) - for option in plugin_options: - LOG.debug('fetching option %s', option) - auth_params[option] = getattr(cmd_options.auth, option, None) - return (auth_plugin_loader, auth_params) - - -def check_valid_authorization_options(options, auth_plugin_name): - """Validate authorization options, and provide helpful error messages.""" - if (options.auth.get('project_id') and not - options.auth.get('domain_id') and not - options.auth.get('domain_name') and not - options.auth.get('project_name') and not - options.auth.get('tenant_id') and not - options.auth.get('tenant_name')): - raise exc.CommandError(_( - 'Missing parameter(s): ' - 'Set either a project or a domain scope, but not both. Set a ' - 'project scope with --os-project-name, OS_PROJECT_NAME, or ' - 'auth.project_name. Alternatively, set a domain scope with ' - '--os-domain-name, OS_DOMAIN_NAME or auth.domain_name.')) - - -def check_valid_authentication_options(options, auth_plugin_name): - """Validate authentication options, and provide helpful error messages.""" - - # Get all the options defined within the plugin. - plugin_opts = base.get_plugin_options(auth_plugin_name) - plugin_opts = {opt.dest: opt for opt in plugin_opts} - - # NOTE(aloga): this is an horrible hack. We need a way to specify the - # required options in the plugins. Using the "required" argument for - # the oslo_config.cfg.Opt does not work, as it is not possible to load the - # plugin if the option is not defined, so the error will simply be: - # "NoMatchingPlugin: The plugin foobar could not be found" - msgs = [] - if 'password' in plugin_opts and not options.auth.get('username'): - msgs.append(_('Set a username with --os-username, OS_USERNAME,' - ' or auth.username')) - if 'auth_url' in plugin_opts and not options.auth.get('auth_url'): - msgs.append(_('Set a service AUTH_URL, with --os-auth-url, ' - 'OS_AUTH_URL or auth.auth_url')) - if 'url' in plugin_opts and not options.auth.get('url'): - msgs.append(_('Set a service URL, with --os-url, ' - 'OS_URL or auth.url')) - if 'token' in plugin_opts and not options.auth.get('token'): - msgs.append(_('Set a token with --os-token, ' - 'OS_TOKEN or auth.token')) - if msgs: - raise exc.CommandError( - _('Missing parameter(s): \n%s') % '\n'.join(msgs)) - - -def build_auth_plugins_option_parser(parser): - """Auth plugins options builder - - Builds dynamically the list of options expected by each available - authentication plugin. - - """ - available_plugins = list(get_plugin_list()) - parser.add_argument( - '--os-auth-type', - metavar='', - dest='auth_type', - default=utils.env('OS_AUTH_TYPE'), - help=_('Select an authentication type. Available types: %s.' - ' Default: selected based on --os-username/--os-token' - ' (Env: OS_AUTH_TYPE)') % ', '.join(available_plugins), - choices=available_plugins - ) - # Maintain compatibility with old tenant env vars - envs = { - 'OS_PROJECT_NAME': utils.env( - 'OS_PROJECT_NAME', - default=utils.env('OS_TENANT_NAME') - ), - 'OS_PROJECT_ID': utils.env( - 'OS_PROJECT_ID', - default=utils.env('OS_TENANT_ID') - ), - } - for o in get_options_list(): - # Remove tenant options from KSC plugins and replace them below - if 'tenant' not in o: - parser.add_argument( - '--os-' + o, - metavar='' % o, - dest=o.replace('-', '_'), - default=envs.get( - OPTIONS_LIST[o]['env'], - utils.env(OPTIONS_LIST[o]['env']), - ), - help=_('%(help)s\n(Env: %(env)s)') % { - 'help': OPTIONS_LIST[o]['help'], - 'env': OPTIONS_LIST[o]['env'], - }, - ) - # add tenant-related options for compatibility - # this is deprecated but still used in some tempest tests... - parser.add_argument( - '--os-tenant-name', - metavar='', - dest='os_project_name', - help=argparse.SUPPRESS, - ) - parser.add_argument( - '--os-tenant-id', - metavar='', - dest='os_project_id', - help=argparse.SUPPRESS, - ) - return parser +sys.stderr.write( + "WARNING: %s is deprecated and will be removed after Jun 2017. " + "Please use osc_lib.api.auth\n" % __name__ +) diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 3c35b5293..13c6b92cf 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -20,12 +20,12 @@ import logging import pkg_resources import sys +from osc_lib.api import auth from osc_lib import exceptions from oslo_utils import strutils import requests import six -from openstackclient.api import auth from openstackclient.common import session as osc_session from openstackclient.identity import client as identity_client diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index be7b643f5..d932b9706 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -16,9 +16,9 @@ import logging from keystoneclient.v2_0 import client as identity_client_v2 +from osc_lib.api import auth from osc_lib import utils -from openstackclient.api import auth from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py index 0a9965e0f..117c71842 100644 --- a/openstackclient/tests/common/test_clientmanager.py +++ b/openstackclient/tests/common/test_clientmanager.py @@ -19,10 +19,10 @@ import mock from keystoneauth1.access import service_catalog from keystoneauth1.identity import v2 as auth_v2 from keystoneauth1 import token_endpoint +from osc_lib.api import auth from osc_lib import exceptions as exc from requests_mock.contrib import fixture -from openstackclient.api import auth from openstackclient.common import clientmanager from openstackclient.tests import fakes from openstackclient.tests import utils @@ -356,7 +356,7 @@ class TestClientManager(utils.TestCase): client_manager.setup_auth, ) - @mock.patch('openstackclient.api.auth.check_valid_authentication_options') + @mock.patch('osc_lib.api.auth.check_valid_authentication_options') def test_client_manager_auth_setup_once(self, check_authn_options_func): client_manager = clientmanager.ClientManager( cli_options=FakeOptions(