Skip to content

Commit fe0c8e9

Browse files
dolphstevemar
authored andcommitted
Do not prompt for scope options with default scoped tokens
This changes the scope validation to occur after a token has already been created. Previous flow: 1. Validate authentication options. 2. Validate authorization options if the command requires a scope. 3. Create a token (using authentication + authorization options) 4. Run command. This means that scope was being checked, even if a default scope was applied in step 3 by Keystone. New flow: 1. Validate authentication options. 2. Create token (using authentication + authorization options) 3 Validate authorization options if the command requires a scope and the token is not scoped. 4. Run command. Change-Id: Idae368a11249f425b14b891fc68b4176e2b3e981 Closes-Bug: 1592062
1 parent 1464c8a commit fe0c8e9

File tree

5 files changed

+49
-33
lines changed

5 files changed

+49
-33
lines changed

openstackclient/api/auth.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,24 @@ def build_auth_params(auth_plugin_name, cmd_options):
128128
return (auth_plugin_loader, auth_params)
129129

130130

131-
def check_valid_auth_options(options, auth_plugin_name, required_scope=True):
132-
"""Perform basic option checking, provide helpful error messages.
133-
134-
:param required_scope: indicate whether a scoped token is required
135-
136-
"""
131+
def check_valid_authorization_options(options, auth_plugin_name):
132+
"""Validate authorization options, and provide helpful error messages."""
133+
if (options.auth.get('project_id') and not
134+
options.auth.get('domain_id') and not
135+
options.auth.get('domain_name') and not
136+
options.auth.get('project_name') and not
137+
options.auth.get('tenant_id') and not
138+
options.auth.get('tenant_name')):
139+
raise exc.CommandError(_(
140+
'Missing parameter(s): '
141+
'Set either a project or a domain scope, but not both. Set a '
142+
'project scope with --os-project-name, OS_PROJECT_NAME, or '
143+
'auth.project_name. Alternatively, set a domain scope with '
144+
'--os-domain-name, OS_DOMAIN_NAME or auth.domain_name.'))
145+
146+
147+
def check_valid_authentication_options(options, auth_plugin_name):
148+
"""Validate authentication options, and provide helpful error messages."""
137149

138150
msgs = []
139151
if auth_plugin_name.endswith('password'):
@@ -143,18 +155,6 @@ def check_valid_auth_options(options, auth_plugin_name, required_scope=True):
143155
if not options.auth.get('auth_url'):
144156
msgs.append(_('Set an authentication URL, with --os-auth-url,'
145157
' OS_AUTH_URL or auth.auth_url'))
146-
if (required_scope and not
147-
options.auth.get('project_id') and not
148-
options.auth.get('domain_id') and not
149-
options.auth.get('domain_name') and not
150-
options.auth.get('project_name') and not
151-
options.auth.get('tenant_id') and not
152-
options.auth.get('tenant_name')):
153-
msgs.append(_('Set a scope, such as a project or domain, set a '
154-
'project scope with --os-project-name, '
155-
'OS_PROJECT_NAME or auth.project_name, set a domain '
156-
'scope with --os-domain-name, OS_DOMAIN_NAME or '
157-
'auth.domain_name'))
158158
elif auth_plugin_name.endswith('token'):
159159
if not options.auth.get('token'):
160160
msgs.append(_('Set a token with --os-token, OS_TOKEN or '

openstackclient/common/clientmanager.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,8 @@ def __init__(
140140
# prior to dereferrencing auth_ref.
141141
self._auth_setup_completed = False
142142

143-
def setup_auth(self, required_scope=True):
144-
"""Set up authentication
145-
146-
:param required_scope: indicate whether a scoped token is required
143+
def setup_auth(self):
144+
"""Set up authentication.
147145
148146
This is deferred until authentication is actually attempted because
149147
it gets in the way of things that do not require auth.
@@ -157,9 +155,8 @@ def setup_auth(self, required_scope=True):
157155
self.auth_plugin_name = auth.select_auth_plugin(self._cli_options)
158156

159157
# Basic option checking to avoid unhelpful error messages
160-
auth.check_valid_auth_options(self._cli_options,
161-
self.auth_plugin_name,
162-
required_scope=required_scope)
158+
auth.check_valid_authentication_options(self._cli_options,
159+
self.auth_plugin_name)
163160

164161
# Horrible hack alert...must handle prompt for null password if
165162
# password auth is requested.
@@ -229,6 +226,20 @@ def setup_auth(self, required_scope=True):
229226

230227
self._auth_setup_completed = True
231228

229+
def validate_scope(self):
230+
if self._auth_ref.project_id is not None:
231+
# We already have a project scope.
232+
return
233+
if self._auth_ref.domain_id is not None:
234+
# We already have a domain scope.
235+
return
236+
237+
# We do not have a scoped token (and the user's default project scope
238+
# was not implied), so the client needs to be explicitly configured
239+
# with a scope.
240+
auth.check_valid_authorization_options(self._cli_options,
241+
self.auth_plugin_name)
242+
232243
@property
233244
def auth_ref(self):
234245
"""Dereference will trigger an auth if it hasn't already"""

openstackclient/shell.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,12 +443,12 @@ def prepare_to_run_command(self, cmd):
443443
cmd.__class__.__name__,
444444
)
445445
if cmd.auth_required:
446-
if hasattr(cmd, 'required_scope'):
446+
self.client_manager.setup_auth()
447+
if hasattr(cmd, 'required_scope') and cmd.required_scope:
447448
# let the command decide whether we need a scoped token
448-
self.client_manager.setup_auth(cmd.required_scope)
449+
self.client_manager.validate_scope()
449450
# Trigger the Identity client to initialize
450451
self.client_manager.auth_ref
451-
return
452452

453453
def clean_up(self, cmd, result, err):
454454
self.log.debug('clean_up %s: %s', cmd.__class__.__name__, err or '')

openstackclient/tests/common/test_clientmanager.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,8 @@ def test_client_manager_select_auth_plugin_failure(self):
356356
client_manager.setup_auth,
357357
)
358358

359-
@mock.patch('openstackclient.api.auth.check_valid_auth_options')
360-
def test_client_manager_auth_setup_once(self, check_auth_options_func):
359+
@mock.patch('openstackclient.api.auth.check_valid_authentication_options')
360+
def test_client_manager_auth_setup_once(self, check_authn_options_func):
361361
client_manager = clientmanager.ClientManager(
362362
cli_options=FakeOptions(
363363
auth=dict(
@@ -372,11 +372,11 @@ def test_client_manager_auth_setup_once(self, check_auth_options_func):
372372
)
373373
self.assertFalse(client_manager._auth_setup_completed)
374374
client_manager.setup_auth()
375-
self.assertTrue(check_auth_options_func.called)
375+
self.assertTrue(check_authn_options_func.called)
376376
self.assertTrue(client_manager._auth_setup_completed)
377377

378378
# now make sure we don't do auth setup the second time around
379379
# by checking whether check_valid_auth_options() gets called again
380-
check_auth_options_func.reset_mock()
380+
check_authn_options_func.reset_mock()
381381
client_manager.auth_ref
382-
check_auth_options_func.assert_not_called()
382+
check_authn_options_func.assert_not_called()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- Scope options are now validated after authentication occurs, and only if
4+
the user does not have a default project scope.
5+
[Bug `1592062 <https://bugs.launchpad.net/bugs/1592062>`_]

0 commit comments

Comments
 (0)