summaryrefslogtreecommitdiff
path: root/openstackclient/shell.py
diff options
context:
space:
mode:
authorDean Troyer <dtroyer@gmail.com>2014-10-20 18:53:10 -0500
committerDean Troyer <dtroyer@gmail.com>2014-10-21 23:16:16 -0500
commite063246b97a7f31a47aca0a5eb36d571f5df7236 (patch)
tree1272ad2fe096430e7b4311cddac0242212fad7fe /openstackclient/shell.py
parentf600c0eafbf9aff23b6efa1c7b94797a25873b99 (diff)
downloadpython-openstackclient-e063246b97a7f31a47aca0a5eb36d571f5df7236.tar.gz
Clean up shell authentication
* Remove the auth option checks as the auth plugins will validate their own options * Move the initialization of client_manager to the end of initialize_app() so it is always called. Note that no attempts to actually authenticate occur until the first use of one of the client attributes in client_manager. This leaves initialize_clientmanager() (formerly uathenticate_user()) empty so remove it. * Remove interact() as the client_manager has already been created And there is nothing left. * prepare_to_run_command() is reduced to trigger an authentication attempt for the best_effort auth commands, currently the only one is 'complete'. * Add prompt_for_password() to ask the user to enter a password when necessary. Passed to ClientManager in a new kward pw_func. Bug: 1355838 Change-Id: I9fdec9144c4c84f65aed1cf91ce41fe1895089b2
Diffstat (limited to 'openstackclient/shell.py')
-rw-r--r--openstackclient/shell.py152
1 files changed, 34 insertions, 118 deletions
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 668e48b5..e671ecc3 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -36,6 +36,30 @@ from openstackclient.common import utils
DEFAULT_DOMAIN = 'default'
+def prompt_for_password(prompt=None):
+ """Prompt user for a password
+
+ Propmpt for a password if stdin is a tty.
+ """
+
+ if not prompt:
+ prompt = 'Password: '
+ pw = None
+ # If stdin is a tty, try prompting for the password
+ if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty():
+ # Check for Ctl-D
+ try:
+ pw = getpass.getpass(prompt)
+ except EOFError:
+ pass
+ # No password because we did't have a tty or nothing was entered
+ if not pw:
+ raise exc.CommandError(
+ "No password entered, or found via --os-password or OS_PASSWORD",
+ )
+ return pw
+
+
class OpenStackShell(app.App):
CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'
@@ -206,112 +230,6 @@ class OpenStackShell(app.App):
return clientmanager.build_plugin_option_parser(parser)
- def initialize_clientmanager(self):
- """Validating authentication options and generate a clientmanager"""
-
- if self.client_manager:
- self.log.debug('The clientmanager has been initialized already')
- return
-
- self.log.debug("validating authentication options")
-
- # Assuming all auth plugins will be named in the same fashion,
- # ie vXpluginName
- if (not self.options.os_url and
- self.options.os_auth_plugin.startswith('v') and
- self.options.os_auth_plugin[1] !=
- self.options.os_identity_api_version[0]):
- raise exc.CommandError(
- "Auth plugin %s not compatible"
- " with requested API version" % self.options.os_auth_plugin
- )
- # TODO(mhu) All these checks should be exposed at the plugin level
- # or just dropped altogether, as the client instantiation will fail
- # anyway
- if self.options.os_url and not self.options.os_token:
- # service token needed
- raise exc.CommandError(
- "You must provide a service token via"
- " either --os-token or env[OS_TOKEN]")
-
- if (self.options.os_auth_plugin.endswith('token') and
- (self.options.os_token or self.options.os_auth_url)):
- # Token flow auth takes priority
- if not self.options.os_token:
- raise exc.CommandError(
- "You must provide a token via"
- " either --os-token or env[OS_TOKEN]")
-
- if not self.options.os_auth_url:
- raise exc.CommandError(
- "You must provide a service URL via"
- " either --os-auth-url or env[OS_AUTH_URL]")
-
- if (not self.options.os_url and
- not self.options.os_auth_plugin.endswith('token')):
- # Validate password flow auth
- if not self.options.os_username:
- raise exc.CommandError(
- "You must provide a username via"
- " either --os-username or env[OS_USERNAME]")
-
- if not self.options.os_password:
- # No password, if we've got a tty, try prompting for it
- if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty():
- # Check for Ctl-D
- try:
- self.options.os_password = getpass.getpass()
- except EOFError:
- pass
- # No password because we did't have a tty or the
- # user Ctl-D when prompted?
- if not self.options.os_password:
- raise exc.CommandError(
- "You must provide a password via"
- " either --os-password, or env[OS_PASSWORD], "
- " or prompted response")
-
- if not ((self.options.os_project_id
- or self.options.os_project_name) or
- (self.options.os_domain_id
- or self.options.os_domain_name) or
- self.options.os_trust_id):
- if self.options.os_auth_plugin.endswith('password'):
- raise exc.CommandError(
- "You must provide authentication scope as a project "
- "or a domain via --os-project-id "
- "or env[OS_PROJECT_ID], "
- "--os-project-name or env[OS_PROJECT_NAME], "
- "--os-domain-id or env[OS_DOMAIN_ID], or"
- "--os-domain-name or env[OS_DOMAIN_NAME], or "
- "--os-trust-id or env[OS_TRUST_ID].")
-
- if not self.options.os_auth_url:
- raise exc.CommandError(
- "You must provide an auth url via"
- " either --os-auth-url or via env[OS_AUTH_URL]")
-
- if (self.options.os_trust_id and
- self.options.os_identity_api_version != '3'):
- raise exc.CommandError(
- "Trusts can only be used with Identity API v3")
-
- if (self.options.os_trust_id and
- ((self.options.os_project_id
- or self.options.os_project_name) or
- (self.options.os_domain_id
- or self.options.os_domain_name))):
- raise exc.CommandError(
- "Authentication cannot be scoped to multiple targets. "
- "Pick one of project, domain or trust.")
-
- self.client_manager = clientmanager.ClientManager(
- auth_options=self.options,
- verify=self.verify,
- api_version=self.api_version,
- )
- return
-
def initialize_app(self, argv):
"""Global app init bits:
@@ -368,19 +286,23 @@ class OpenStackShell(app.App):
else:
self.verify = not self.options.insecure
+ self.client_manager = clientmanager.ClientManager(
+ auth_options=self.options,
+ verify=self.verify,
+ api_version=self.api_version,
+ pw_func=prompt_for_password,
+ )
+
def prepare_to_run_command(self, cmd):
"""Set up auth and API versions"""
self.log.debug('prepare_to_run_command %s', cmd.__class__.__name__)
- if not cmd.auth_required:
- return
- if cmd.best_effort:
+ if cmd.auth_required and cmd.best_effort:
try:
- self.initialize_clientmanager()
+ # Trigger the Identity client to initialize
+ self.client_manager.auth_ref
except Exception:
pass
- else:
- self.initialize_clientmanager()
return
def clean_up(self, cmd, result, err):
@@ -412,12 +334,6 @@ class OpenStackShell(app.App):
targs = tparser.parse_args(['-f', format])
tcmd.run(targs)
- def interact(self):
- # NOTE(dtroyer): Maintain the old behaviour for interactive use as
- # this path does not call prepare_to_run_command()
- self.initialize_clientmanager()
- super(OpenStackShell, self).interact()
-
def main(argv=sys.argv[1:]):
return OpenStackShell().run(argv)