Skip to content

Commit 0c77a9f

Browse files
mhuinFlorent Flament
andcommitted
Support for keystone auth plugins
This patch allows the user to choose which authentication plugin to use with the CLI. The arguments needed by the auth plugins are automatically added to the argument parser. Some examples with the currently available authentication plugins:: OS_USERNAME=admin OS_PROJECT_NAME=admin OS_AUTH_URL=http://keystone:5000/v2.0 \ OS_PASSWORD=admin openstack user list OS_USERNAME=admin OS_PROJECT_DOMAIN_NAME=default OS_USER_DOMAIN_NAME=default \ OS_PROJECT_NAME=admin OS_AUTH_URL=http://keystone:5000/v3 OS_PASSWORD=admin \ OS_IDENTITY_API_VERSION=3 OS_AUTH_PLUGIN=v3password openstack project list OS_TOKEN=1234 OS_URL=http://service_url:35357/v2.0 \ OS_IDENTITY_API_VERSION=2.0 openstack user list The --os-auth-plugin option can be omitted; if so the CLI will attempt to guess which plugin to use from the other options. Change-Id: I330c20ddb8d96b3a4287c68b57c36c4a0f869669 Co-Authored-By: Florent Flament <florent.flament-ext@cloudwatt.com>
1 parent 866965f commit 0c77a9f

9 files changed

Lines changed: 614 additions & 305 deletions

File tree

‎doc/source/man/openstack.rst‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ DESCRIPTION
2121
equivalent to the CLIs provided by the OpenStack project client libraries, but with
2222
a distinct and consistent command structure.
2323

24+
25+
AUTHENTICATION METHODS
26+
======================
27+
2428
:program:`openstack` uses a similar authentication scheme as the OpenStack project CLIs, with
2529
the credential information supplied either as environment variables or as options on the
2630
command line. The primary difference is the use of 'project' in the name of the options
@@ -33,6 +37,15 @@ command line. The primary difference is the use of 'project' in the name of the
3337
export OS_USERNAME=<user-name>
3438
export OS_PASSWORD=<password> # (optional)
3539

40+
:program:`openstack` can use different types of authentication plugins provided by the keystoneclient library. The following default plugins are available:
41+
42+
* ``token``: Authentication with a token
43+
* ``password``: Authentication with a username and a password
44+
45+
Refer to the keystoneclient library documentation for more details about these plugins and their options, and for a complete list of available plugins.
46+
Please bear in mind that some plugins might not support all of the functionalities of :program:`openstack`; for example the v3unscopedsaml plugin can deliver only unscoped tokens, some commands might not be available through this authentication method.
47+
48+
Additionally, it is possible to use Keystone's service token to authenticate, by setting the options :option:`--os-token` and :option:`--os-url` (or the environment variables :envvar:`OS_TOKEN` and :envvar:`OS_URL` respectively). This method takes precedence over authentication plugins.
3649

3750
OPTIONS
3851
=======
@@ -41,9 +54,16 @@ OPTIONS
4154

4255
:program:`openstack` recognizes the following global topions:
4356

57+
:option:`--os-auth-plugin` <auth-plugin>
58+
The authentication plugin to use when connecting to the Identity service. If this option is not set, :program:`openstack` will attempt to guess the authentication method to use based on the other options.
59+
If this option is set, its version must match :option:`--os-identity-api-version`
60+
4461
:option:`--os-auth-url` <auth-url>
4562
Authentication URL
4663

64+
:option:`--os-url` <service-url>
65+
Service URL, when using a service token for authentication
66+
4767
:option:`--os-domain-name` <auth-domain-name> | :option:`--os-domain-id` <auth-domain-id>
4868
Domain-level authorization scope (name or ID)
4969

@@ -59,6 +79,9 @@ OPTIONS
5979
:option:`--os-password` <auth-password>
6080
Authentication password
6181

82+
:option:`--os-token` <token>
83+
Authenticated token or service token
84+
6285
:option:`--os-user-domain-name` <auth-user-domain-name> | :option:`--os-user-domain-id` <auth-user-domain-id>
6386
Domain name or id containing user
6487

@@ -86,6 +109,7 @@ OPTIONS
86109
:option:`--os-XXXX-api-version` <XXXX-api-version>
87110
Additional API version options will be available depending on the installed API libraries.
88111

112+
89113
COMMANDS
90114
========
91115

@@ -174,9 +198,15 @@ ENVIRONMENT VARIABLES
174198

175199
The following environment variables can be set to alter the behaviour of :program:`openstack`. Most of them have corresponding command-line options that take precedence if set.
176200

201+
:envvar:`OS_AUTH_PLUGIN`
202+
The authentication plugin to use when connecting to the Identity service, its version must match the Identity API version
203+
177204
:envvar:`OS_AUTH_URL`
178205
Authentication URL
179206

207+
:envvar:`OS_URL`
208+
Service URL (when using the service token)
209+
180210
:envvar:`OS_DOMAIN_NAME`
181211
Domain-level authorization scope (name or ID)
182212

@@ -189,6 +219,9 @@ The following environment variables can be set to alter the behaviour of :progra
189219
:envvar:`OS_USERNAME`
190220
Authentication username
191221

222+
:envvar:`OS_TOKEN`
223+
Authenticated or service token
224+
192225
:envvar:`OS_PASSWORD`
193226
Authentication password
194227

@@ -213,6 +246,7 @@ The following environment variables can be set to alter the behaviour of :progra
213246
:envvar:`OS_XXXX_API_VERSION`
214247
Additional API version options will be available depending on the installed API libraries.
215248

249+
216250
BUGS
217251
====
218252

‎openstackclient/api/auth.py‎

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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

Comments
 (0)