From d324530532d5361e85e784c3df2f0d40a128b149 Mon Sep 17 00:00:00 2001
From: Dean Troyer <dtroyer@gmail.com>
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 6d48a8cb27..d472fe6b91 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 11b62da773..577fc052fa 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 abaa18711f..cc04bc70fe 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 0c82fe9ba8..d62a82dc43 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='<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
+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 3c35b52933..13c6b92cfa 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 be7b643f5c..d932b97068 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 0a9965e0f3..117c718429 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(