 c1b376dc33
			
		
	
	c1b376dc33
	
	
	
		
			
			Help for option --os-auth-type doesn't specify what environment variable configures it. This patch fixes that. Change-Id: Id2e29e477d5ca56339bd777fb73b5af13788615b
		
			
				
	
	
		
			233 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #   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.
 | |
| #
 | |
| 
 | |
| """Authentication Library"""
 | |
| 
 | |
| import argparse
 | |
| import logging
 | |
| 
 | |
| import stevedore
 | |
| 
 | |
| from oslo.config import cfg
 | |
| 
 | |
| from keystoneclient.auth import base
 | |
| 
 | |
| from openstackclient.common import exceptions as exc
 | |
| from openstackclient.common import utils
 | |
| 
 | |
| 
 | |
| LOG = logging.getLogger(__name__)
 | |
| 
 | |
| 
 | |
| # Initialize the list of Authentication plugins early in order
 | |
| # to get the command-line options
 | |
| PLUGIN_LIST = stevedore.ExtensionManager(
 | |
|     base.PLUGIN_NAMESPACE,
 | |
|     invoke_on_load=False,
 | |
|     propagate_map_exceptions=True,
 | |
| )
 | |
| 
 | |
| # Get the command line options so the help action has them available
 | |
| OPTIONS_LIST = {}
 | |
| for plugin in PLUGIN_LIST:
 | |
|     for o in plugin.plugin.get_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,
 | |
|         )
 | |
| 
 | |
| 
 | |
| def select_auth_plugin(options):
 | |
|     """Pick an auth plugin based on --os-auth-type or other options"""
 | |
| 
 | |
|     auth_plugin_name = None
 | |
| 
 | |
|     if options.os_auth_type in [plugin.name for plugin in PLUGIN_LIST]:
 | |
|         # A direct plugin name was given, use it
 | |
|         return options.os_auth_type
 | |
| 
 | |
|     if options.os_url and options.os_token:
 | |
|         # service token authentication
 | |
|         auth_plugin_name = 'token_endpoint'
 | |
|     elif options.os_username:
 | |
|         if options.os_identity_api_version == '3':
 | |
|             auth_plugin_name = 'v3password'
 | |
|         elif options.os_identity_api_version == '2.0':
 | |
|             auth_plugin_name = 'v2password'
 | |
|         else:
 | |
|             # let keystoneclient figure it out itself
 | |
|             auth_plugin_name = 'password'
 | |
|     elif options.os_token:
 | |
|         if options.os_identity_api_version == '3':
 | |
|             auth_plugin_name = 'v3token'
 | |
|         elif options.os_identity_api_version == '2.0':
 | |
|             auth_plugin_name = 'v2token'
 | |
|         else:
 | |
|             # let keystoneclient figure it out itself
 | |
|             auth_plugin_name = 'token'
 | |
|     else:
 | |
|         raise exc.CommandError(
 | |
|             "Authentication type must be selected with --os-auth-type"
 | |
|         )
 | |
|     LOG.debug("Auth plugin %s selected" % auth_plugin_name)
 | |
|     return auth_plugin_name
 | |
| 
 | |
| 
 | |
| def build_auth_params(auth_plugin_name, cmd_options):
 | |
|     auth_params = {}
 | |
|     if auth_plugin_name:
 | |
|         LOG.debug('auth_type: %s', auth_plugin_name)
 | |
|         auth_plugin_class = base.get_plugin_class(auth_plugin_name)
 | |
|         plugin_options = auth_plugin_class.get_options()
 | |
|         for option in plugin_options:
 | |
|             option_name = 'os_' + option.dest
 | |
|             LOG.debug('fetching option %s' % option_name)
 | |
|             auth_params[option.dest] = getattr(cmd_options, option_name, None)
 | |
|         # grab tenant from project for v2.0 API compatibility
 | |
|         if auth_plugin_name.startswith("v2"):
 | |
|             auth_params['tenant_id'] = getattr(
 | |
|                 cmd_options,
 | |
|                 'os_project_id',
 | |
|                 None,
 | |
|             )
 | |
|             auth_params['tenant_name'] = getattr(
 | |
|                 cmd_options,
 | |
|                 'os_project_name',
 | |
|                 None,
 | |
|             )
 | |
|     else:
 | |
|         LOG.debug('no auth_type')
 | |
|         # delay the plugin choice, grab every option
 | |
|         plugin_options = set([o.replace('-', '_') for o in OPTIONS_LIST])
 | |
|         for option in plugin_options:
 | |
|             option_name = 'os_' + option
 | |
|             LOG.debug('fetching option %s' % option_name)
 | |
|             auth_params[option] = getattr(cmd_options, option_name, None)
 | |
|     return (auth_plugin_class, auth_params)
 | |
| 
 | |
| 
 | |
| 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 = [plugin.name for plugin in PLUGIN_LIST]
 | |
|     parser.add_argument(
 | |
|         '--os-auth-type',
 | |
|         metavar='<auth-type>',
 | |
|         default=utils.env('OS_AUTH_TYPE'),
 | |
|         help='Select an auhentication type. Available types: ' +
 | |
|              ', '.join(available_plugins) +
 | |
|              '. Default: selected based on --os-username/--os-token' +
 | |
|              ' (Env: OS_AUTH_TYPE)',
 | |
|         choices=available_plugins
 | |
|     )
 | |
|     # make sur we catch old v2.0 env values
 | |
|     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 OPTIONS_LIST:
 | |
|         # remove allusion to tenants from v2.0 API
 | |
|         if 'tenant' not in o:
 | |
|             parser.add_argument(
 | |
|                 '--os-' + o,
 | |
|                 metavar='<auth-%s>' % o,
 | |
|                 default=envs.get(OPTIONS_LIST[o]['env'],
 | |
|                                  utils.env(OPTIONS_LIST[o]['env'])),
 | |
|                 help='%s\n(Env: %s)' % (OPTIONS_LIST[o]['help'],
 | |
|                                         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',
 | |
|         default=utils.env('OS_TENANT_NAME'),
 | |
|         help=argparse.SUPPRESS,
 | |
|     )
 | |
|     parser.add_argument(
 | |
|         '--os-tenant-id',
 | |
|         metavar='<auth-tenant-id>',
 | |
|         dest='os_project_id',
 | |
|         default=utils.env('OS_TENANT_ID'),
 | |
|         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
 |