|
| 1 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 2 | +# not use this file except in compliance with the License. You may obtain |
| 3 | +# a copy of the License at |
| 4 | +# |
| 5 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 6 | +# |
| 7 | +# Unless required by applicable law or agreed to in writing, software |
| 8 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 9 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 10 | +# License for the specific language governing permissions and limitations |
| 11 | +# under the License. |
| 12 | +# |
| 13 | + |
| 14 | +"""Authentication Library""" |
| 15 | + |
| 16 | +import argparse |
| 17 | +import logging |
| 18 | + |
| 19 | +import stevedore |
| 20 | + |
| 21 | +from keystoneclient.auth import base |
| 22 | + |
| 23 | +from openstackclient.common import exceptions as exc |
| 24 | +from openstackclient.common import utils |
| 25 | + |
| 26 | + |
| 27 | +LOG = logging.getLogger(__name__) |
| 28 | + |
| 29 | + |
| 30 | +# Initialize the list of Authentication plugins early in order |
| 31 | +# to get the command-line options |
| 32 | +PLUGIN_LIST = stevedore.ExtensionManager( |
| 33 | + base.PLUGIN_NAMESPACE, |
| 34 | + invoke_on_load=False, |
| 35 | + propagate_map_exceptions=True, |
| 36 | +) |
| 37 | +# TODO(dtroyer): add some method to list the plugins for the |
| 38 | +# --os_auth_plugin option |
| 39 | + |
| 40 | +# Get the command line options so the help action has them available |
| 41 | +OPTIONS_LIST = {} |
| 42 | +for plugin in PLUGIN_LIST: |
| 43 | + for o in plugin.plugin.get_options(): |
| 44 | + os_name = o.dest.lower().replace('_', '-') |
| 45 | + os_env_name = 'OS_' + os_name.upper().replace('-', '_') |
| 46 | + OPTIONS_LIST.setdefault(os_name, {'env': os_env_name, 'help': ''}) |
| 47 | + # TODO(mhu) simplistic approach, would be better to only add |
| 48 | + # help texts if they vary from one auth plugin to another |
| 49 | + # also the text rendering is ugly in the CLI ... |
| 50 | + OPTIONS_LIST[os_name]['help'] += 'With %s: %s\n' % ( |
| 51 | + plugin.name, |
| 52 | + o.help, |
| 53 | + ) |
| 54 | + |
| 55 | + |
| 56 | +def _guess_authentication_method(options): |
| 57 | + """If no auth plugin was specified, pick one based on other options""" |
| 58 | + |
| 59 | + if options.os_url: |
| 60 | + # service token authentication, do nothing |
| 61 | + return |
| 62 | + auth_plugin = None |
| 63 | + if options.os_password: |
| 64 | + if options.os_identity_api_version == '3': |
| 65 | + auth_plugin = 'v3password' |
| 66 | + elif options.os_identity_api_version == '2.0': |
| 67 | + auth_plugin = 'v2password' |
| 68 | + else: |
| 69 | + # let keystoneclient figure it out itself |
| 70 | + auth_plugin = 'password' |
| 71 | + elif options.os_token: |
| 72 | + if options.os_identity_api_version == '3': |
| 73 | + auth_plugin = 'v3token' |
| 74 | + elif options.os_identity_api_version == '2.0': |
| 75 | + auth_plugin = 'v2token' |
| 76 | + else: |
| 77 | + # let keystoneclient figure it out itself |
| 78 | + auth_plugin = 'token' |
| 79 | + else: |
| 80 | + raise exc.CommandError( |
| 81 | + "Could not figure out which authentication method " |
| 82 | + "to use, please set --os-auth-plugin" |
| 83 | + ) |
| 84 | + LOG.debug("No auth plugin selected, picking %s from other " |
| 85 | + "options" % auth_plugin) |
| 86 | + options.os_auth_plugin = auth_plugin |
| 87 | + |
| 88 | + |
| 89 | +def build_auth_params(cmd_options): |
| 90 | + auth_params = {} |
| 91 | + if cmd_options.os_url: |
| 92 | + return {'token': cmd_options.os_token} |
| 93 | + if cmd_options.os_auth_plugin: |
| 94 | + auth_plugin = base.get_plugin_class(cmd_options.os_auth_plugin) |
| 95 | + plugin_options = auth_plugin.get_options() |
| 96 | + for option in plugin_options: |
| 97 | + option_name = 'os_' + option.dest |
| 98 | + LOG.debug('fetching option %s' % option_name) |
| 99 | + auth_params[option.dest] = getattr(cmd_options, option_name, None) |
| 100 | + # grab tenant from project for v2.0 API compatibility |
| 101 | + if cmd_options.os_auth_plugin.startswith("v2"): |
| 102 | + auth_params['tenant_id'] = getattr( |
| 103 | + cmd_options, |
| 104 | + 'os_project_id', |
| 105 | + None, |
| 106 | + ) |
| 107 | + auth_params['tenant_name'] = getattr( |
| 108 | + cmd_options, |
| 109 | + 'os_project_name', |
| 110 | + None, |
| 111 | + ) |
| 112 | + else: |
| 113 | + # delay the plugin choice, grab every option |
| 114 | + plugin_options = set([o.replace('-', '_') for o in OPTIONS_LIST]) |
| 115 | + for option in plugin_options: |
| 116 | + option_name = 'os_' + option |
| 117 | + LOG.debug('fetching option %s' % option_name) |
| 118 | + auth_params[option] = getattr(cmd_options, option_name, None) |
| 119 | + return auth_params |
| 120 | + |
| 121 | + |
| 122 | +def build_auth_plugins_option_parser(parser): |
| 123 | + """Auth plugins options builder |
| 124 | +
|
| 125 | + Builds dynamically the list of options expected by each available |
| 126 | + authentication plugin. |
| 127 | +
|
| 128 | + """ |
| 129 | + available_plugins = [plugin.name for plugin in PLUGIN_LIST] |
| 130 | + parser.add_argument( |
| 131 | + '--os-auth-plugin', |
| 132 | + metavar='<OS_AUTH_PLUGIN>', |
| 133 | + default=utils.env('OS_AUTH_PLUGIN'), |
| 134 | + help='The authentication method to use. If this option is not set, ' |
| 135 | + 'openstackclient will attempt to guess the authentication method ' |
| 136 | + 'to use based on the other options. If this option is set, ' |
| 137 | + 'the --os-identity-api-version argument must be consistent ' |
| 138 | + 'with the version of the method.\nAvailable methods are ' + |
| 139 | + ', '.join(available_plugins), |
| 140 | + choices=available_plugins |
| 141 | + ) |
| 142 | + # make sur we catch old v2.0 env values |
| 143 | + envs = { |
| 144 | + 'OS_PROJECT_NAME': utils.env( |
| 145 | + 'OS_PROJECT_NAME', |
| 146 | + default=utils.env('OS_TENANT_NAME') |
| 147 | + ), |
| 148 | + 'OS_PROJECT_ID': utils.env( |
| 149 | + 'OS_PROJECT_ID', |
| 150 | + default=utils.env('OS_TENANT_ID') |
| 151 | + ), |
| 152 | + } |
| 153 | + for o in OPTIONS_LIST: |
| 154 | + # remove allusion to tenants from v2.0 API |
| 155 | + if 'tenant' not in o: |
| 156 | + parser.add_argument( |
| 157 | + '--os-' + o, |
| 158 | + metavar='<auth-%s>' % o, |
| 159 | + default=envs.get(OPTIONS_LIST[o]['env'], |
| 160 | + utils.env(OPTIONS_LIST[o]['env'])), |
| 161 | + help='%s\n(Env: %s)' % (OPTIONS_LIST[o]['help'], |
| 162 | + OPTIONS_LIST[o]['env']), |
| 163 | + ) |
| 164 | + # add tenant-related options for compatibility |
| 165 | + # this is deprecated but still used in some tempest tests... |
| 166 | + parser.add_argument( |
| 167 | + '--os-tenant-name', |
| 168 | + metavar='<auth-tenant-name>', |
| 169 | + dest='os_project_name', |
| 170 | + default=utils.env('OS_TENANT_NAME'), |
| 171 | + help=argparse.SUPPRESS, |
| 172 | + ) |
| 173 | + parser.add_argument( |
| 174 | + '--os-tenant-id', |
| 175 | + metavar='<auth-tenant-id>', |
| 176 | + dest='os_project_id', |
| 177 | + default=utils.env('OS_TENANT_ID'), |
| 178 | + help=argparse.SUPPRESS, |
| 179 | + ) |
| 180 | + return parser |
0 commit comments