osc-lib: api.auth

Move auth plugin checking to osc-lib.

Change-Id: I673d9c2d6e8bbf724c3000459a729e831d747814
This commit is contained in:
Dean Troyer 2016-06-23 15:39:48 -05:00
parent b7909252a5
commit d324530532
7 changed files with 15 additions and 233 deletions

View File

@ -38,8 +38,7 @@ import sys
import traceback import traceback
from keystoneauth1 import session as ks_session from keystoneauth1 import session as ks_session
from osc_lib.api import auth
from openstackclient.api import auth
CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'

View File

@ -25,13 +25,11 @@ import logging
import sys import sys
import common import common
from os_client_config import config as cloud_config
from openstackclient.api import object_store_v1 as object_store from openstackclient.api import object_store_v1 as object_store
from openstackclient.identity import client as identity_client from openstackclient.identity import client as identity_client
from os_client_config import config as cloud_config
LOG = logging.getLogger('') LOG = logging.getLogger('')

View File

@ -26,11 +26,10 @@ import logging
import sys import sys
import common import common
from os_client_config import config as cloud_config
from openstackclient.common import clientmanager from openstackclient.common import clientmanager
from os_client_config import config as cloud_config
LOG = logging.getLogger('') LOG = logging.getLogger('')

View File

@ -11,229 +11,15 @@
# under the License. # 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 sys
import logging
from keystoneauth1.loading import base from osc_lib.api.auth import * # noqa
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 = {}
def get_plugin_list(): sys.stderr.write(
"""Gather plugin list and cache it""" "WARNING: %s is deprecated and will be removed after Jun 2017. "
global PLUGIN_LIST "Please use osc_lib.api.auth\n" % __name__
)
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='<auth-type>',
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='<auth-%s>' % 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='<auth-tenant-name>',
dest='os_project_name',
help=argparse.SUPPRESS,
)
parser.add_argument(
'--os-tenant-id',
metavar='<auth-tenant-id>',
dest='os_project_id',
help=argparse.SUPPRESS,
)
return parser

View File

@ -20,12 +20,12 @@ import logging
import pkg_resources import pkg_resources
import sys import sys
from osc_lib.api import auth
from osc_lib import exceptions from osc_lib import exceptions
from oslo_utils import strutils from oslo_utils import strutils
import requests import requests
import six import six
from openstackclient.api import auth
from openstackclient.common import session as osc_session from openstackclient.common import session as osc_session
from openstackclient.identity import client as identity_client from openstackclient.identity import client as identity_client

View File

@ -16,9 +16,9 @@
import logging import logging
from keystoneclient.v2_0 import client as identity_client_v2 from keystoneclient.v2_0 import client as identity_client_v2
from osc_lib.api import auth
from osc_lib import utils from osc_lib import utils
from openstackclient.api import auth
from openstackclient.i18n import _ from openstackclient.i18n import _
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)

View File

@ -19,10 +19,10 @@ import mock
from keystoneauth1.access import service_catalog from keystoneauth1.access import service_catalog
from keystoneauth1.identity import v2 as auth_v2 from keystoneauth1.identity import v2 as auth_v2
from keystoneauth1 import token_endpoint from keystoneauth1 import token_endpoint
from osc_lib.api import auth
from osc_lib import exceptions as exc from osc_lib import exceptions as exc
from requests_mock.contrib import fixture from requests_mock.contrib import fixture
from openstackclient.api import auth
from openstackclient.common import clientmanager from openstackclient.common import clientmanager
from openstackclient.tests import fakes from openstackclient.tests import fakes
from openstackclient.tests import utils from openstackclient.tests import utils
@ -356,7 +356,7 @@ class TestClientManager(utils.TestCase):
client_manager.setup_auth, 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): def test_client_manager_auth_setup_once(self, check_authn_options_func):
client_manager = clientmanager.ClientManager( client_manager = clientmanager.ClientManager(
cli_options=FakeOptions( cli_options=FakeOptions(