Support unscoped token request

Make scope check optional for the "token issue" command as unscoped token is
a valid Keystone V2/V3 API.

Change-Id: Ie1cded4dbfdafd3a78c0ebdf89e3f66762509930
Closes-Bug: #1543214
This commit is contained in:
guang-yee
2016-02-08 11:16:24 -08:00
parent fae6c4c7bd
commit e7ac0db0a3
4 changed files with 56 additions and 5 deletions

View File

@@ -135,8 +135,12 @@ def build_auth_params(auth_plugin_name, cmd_options):
return (auth_plugin_class, auth_params) return (auth_plugin_class, auth_params)
def check_valid_auth_options(options, auth_plugin_name): def check_valid_auth_options(options, auth_plugin_name, required_scope=True):
"""Perform basic option checking, provide helpful error messages""" """Perform basic option checking, provide helpful error messages.
:param required_scope: indicate whether a scoped token is required
"""
msg = '' msg = ''
if auth_plugin_name.endswith('password'): if auth_plugin_name.endswith('password'):
@@ -146,7 +150,8 @@ def check_valid_auth_options(options, auth_plugin_name):
if not options.auth.get('auth_url', None): if not options.auth.get('auth_url', None):
msg += _('Set an authentication URL, with --os-auth-url,' msg += _('Set an authentication URL, with --os-auth-url,'
' OS_AUTH_URL or auth.auth_url\n') ' OS_AUTH_URL or auth.auth_url\n')
if (not options.auth.get('project_id', None) and not if (required_scope and not
options.auth.get('project_id', None) and not
options.auth.get('domain_id', None) and not options.auth.get('domain_id', None) and not
options.auth.get('domain_name', None) and not options.auth.get('domain_name', None) and not
options.auth.get('project_name', None) and not options.auth.get('project_name', None) and not

View File

@@ -113,19 +113,35 @@ class ClientManager(object):
root_logger = logging.getLogger('') root_logger = logging.getLogger('')
LOG.setLevel(root_logger.getEffectiveLevel()) LOG.setLevel(root_logger.getEffectiveLevel())
def setup_auth(self): # NOTE(gyee): use this flag to indicate whether auth setup has already
# been completed. If so, do not perform auth setup again. The reason
# we need this flag is that we want to be able to perform auth setup
# outside of auth_ref as auth_ref itself is a property. We can not
# retrofit auth_ref to optionally skip scope check. Some operations
# do not require a scoped token. In those cases, we call setup_auth
# prior to dereferrencing auth_ref.
self._auth_setup_completed = False
def setup_auth(self, required_scope=True):
"""Set up authentication """Set up authentication
:param required_scope: indicate whether a scoped token is required
This is deferred until authentication is actually attempted because This is deferred until authentication is actually attempted because
it gets in the way of things that do not require auth. it gets in the way of things that do not require auth.
""" """
if self._auth_setup_completed:
return
# If no auth type is named by the user, select one based on # If no auth type is named by the user, select one based on
# the supplied options # the supplied options
self.auth_plugin_name = auth.select_auth_plugin(self._cli_options) self.auth_plugin_name = auth.select_auth_plugin(self._cli_options)
# Basic option checking to avoid unhelpful error messages # Basic option checking to avoid unhelpful error messages
auth.check_valid_auth_options(self._cli_options, self.auth_plugin_name) auth.check_valid_auth_options(self._cli_options,
self.auth_plugin_name,
required_scope=required_scope)
# Horrible hack alert...must handle prompt for null password if # Horrible hack alert...must handle prompt for null password if
# password auth is requested. # password auth is requested.
@@ -180,6 +196,8 @@ class ClientManager(object):
user_agent=USER_AGENT, user_agent=USER_AGENT,
) )
self._auth_setup_completed = True
return return
@property @property

View File

@@ -353,6 +353,9 @@ class OpenStackShell(app.App):
cmd.__class__.__name__, cmd.__class__.__name__,
) )
if cmd.auth_required: if cmd.auth_required:
if hasattr(cmd, 'required_scope'):
# let the command decide whether we need a scoped token
self.client_manager.setup_auth(cmd.required_scope)
# Trigger the Identity client to initialize # Trigger the Identity client to initialize
self.client_manager.auth_ref self.client_manager.auth_ref
return return

View File

@@ -325,3 +325,28 @@ class TestClientManager(utils.TestCase):
exc.CommandError, exc.CommandError,
client_manager.setup_auth, client_manager.setup_auth,
) )
@mock.patch('openstackclient.api.auth.check_valid_auth_options')
def test_client_manager_auth_setup_once(self, check_auth_options_func):
client_manager = clientmanager.ClientManager(
cli_options=FakeOptions(
auth=dict(
auth_url=fakes.AUTH_URL,
username=fakes.USERNAME,
password=fakes.PASSWORD,
project_name=fakes.PROJECT_NAME,
),
),
api_version=API_VERSION,
verify=False,
)
self.assertFalse(client_manager._auth_setup_completed)
client_manager.setup_auth()
self.assertTrue(check_auth_options_func.called)
self.assertTrue(client_manager._auth_setup_completed)
# now make sure we don't do auth setup the second time around
# by checking whether check_valid_auth_options() gets called again
check_auth_options_func.reset_mock()
client_manager.auth_ref
check_auth_options_func.assert_not_called()