Merge "Prompt for password on CLI if not provided"

This commit is contained in:
Jenkins
2015-05-20 20:08:05 +00:00
committed by Gerrit Code Review
8 changed files with 136 additions and 8 deletions

View File

@@ -82,3 +82,11 @@ class Password(base.BaseGenericPlugin):
options = super(Password, cls).get_options() options = super(Password, cls).get_options()
options.extend(get_options()) options.extend(get_options())
return options return options
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
if not (kwargs.get('password') or namespace.os_password):
kwargs['password'] = utils.prompt_user_password()
return super(Password, cls).load_from_argparse_arguments(namespace,
**kwargs)

View File

@@ -144,6 +144,14 @@ class Password(Auth):
return {'passwordCredentials': auth} return {'passwordCredentials': auth}
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
if not (kwargs.get('password') or namespace.os_password):
kwargs['password'] = utils.prompt_user_password()
return super(Password, cls).load_from_argparse_arguments(namespace,
**kwargs)
@classmethod @classmethod
def get_options(cls): def get_options(cls):
options = super(Password, cls).get_options() options = super(Password, cls).get_options()

View File

@@ -13,6 +13,7 @@
from oslo_config import cfg from oslo_config import cfg
from keystoneclient.auth.identity.v3 import base from keystoneclient.auth.identity.v3 import base
from keystoneclient import utils
__all__ = ['PasswordMethod', 'Password'] __all__ = ['PasswordMethod', 'Password']
@@ -86,3 +87,11 @@ class Password(base.AuthConstructor):
]) ])
return options return options
@classmethod
def load_from_argparse_arguments(cls, namespace, **kwargs):
if not (kwargs.get('password') or namespace.os_password):
kwargs['password'] = utils.prompt_user_password()
return super(Password, cls).load_from_argparse_arguments(namespace,
**kwargs)

View File

@@ -19,7 +19,6 @@
from __future__ import print_function from __future__ import print_function
import argparse import argparse
import getpass
import logging import logging
import os import os
import sys import sys
@@ -296,13 +295,8 @@ class OpenStackIdentityShell(object):
'--os-username or env[OS_USERNAME]') '--os-username or env[OS_USERNAME]')
if not args.os_password: if not args.os_password:
# No password, If we've got a tty, try prompting for it args.os_password = utils.prompt_user_password()
if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty():
# Check for Ctl-D
try:
args.os_password = getpass.getpass('OS Password: ')
except EOFError:
pass
# No password because we didn't have a tty or the # No password because we didn't have a tty or the
# user Ctl-D when prompted? # user Ctl-D when prompted?
if not args.os_password: if not args.os_password:

View File

@@ -10,9 +10,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import argparse
import copy import copy
import uuid import uuid
import mock
from keystoneclient.auth.identity import v2 from keystoneclient.auth.identity import v2
from keystoneclient import exceptions from keystoneclient import exceptions
from keystoneclient import session from keystoneclient import session
@@ -294,3 +297,28 @@ class V2IdentityPlugin(utils.TestCase):
def test_password_with_no_user_id_or_name(self): def test_password_with_no_user_id_or_name(self):
self.assertRaises(TypeError, self.assertRaises(TypeError,
v2.Password, self.TEST_URL, password=self.TEST_PASS) v2.Password, self.TEST_URL, password=self.TEST_PASS)
@mock.patch('sys.stdin', autospec=True)
def test_prompt_password(self, mock_stdin):
parser = argparse.ArgumentParser()
v2.Password.register_argparse_arguments(parser)
username = uuid.uuid4().hex
auth_url = uuid.uuid4().hex
tenant_id = uuid.uuid4().hex
password = uuid.uuid4().hex
opts = parser.parse_args(['--os-username', username,
'--os-auth-url', auth_url,
'--os-tenant-id', tenant_id])
with mock.patch('getpass.getpass') as mock_getpass:
mock_getpass.return_value = password
mock_stdin.isatty = lambda: True
plugin = v2.Password.load_from_argparse_arguments(opts)
self.assertEqual(auth_url, plugin.auth_url)
self.assertEqual(username, plugin.username)
self.assertEqual(tenant_id, plugin.tenant_id)
self.assertEqual(password, plugin.password)

View File

@@ -10,9 +10,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import argparse
import copy import copy
import uuid import uuid
import mock
from keystoneclient import access from keystoneclient import access
from keystoneclient.auth.identity import v3 from keystoneclient.auth.identity import v3
from keystoneclient.auth.identity.v3 import base as v3_base from keystoneclient.auth.identity.v3 import base as v3_base
@@ -531,3 +534,32 @@ class V3IdentityPlugin(utils.TestCase):
s = session.Session() s = session.Session()
self.assertRaises(exceptions.AuthorizationFailure, a.get_auth_ref, s) self.assertRaises(exceptions.AuthorizationFailure, a.get_auth_ref, s)
@mock.patch('sys.stdin', autospec=True)
def test_prompt_password(self, mock_stdin):
parser = argparse.ArgumentParser()
v3.Password.register_argparse_arguments(parser)
username = uuid.uuid4().hex
user_domain_id = uuid.uuid4().hex
auth_url = uuid.uuid4().hex
project_id = uuid.uuid4().hex
password = uuid.uuid4().hex
opts = parser.parse_args(['--os-username', username,
'--os-auth-url', auth_url,
'--os-user-domain-id', user_domain_id,
'--os-project-id', project_id])
with mock.patch('getpass.getpass') as mock_getpass:
mock_getpass.return_value = password
mock_stdin.isatty = lambda: True
plugin = v3.Password.load_from_argparse_arguments(opts)
self.assertEqual(auth_url, plugin.auth_url)
self.assertEqual(username, plugin.auth_methods[0].username)
self.assertEqual(project_id, plugin.project_id)
self.assertEqual(user_domain_id,
plugin.auth_methods[0].user_domain_id)
self.assertEqual(password, plugin.auth_methods[0].password)

View File

@@ -10,8 +10,11 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import argparse
import uuid import uuid
import mock
from keystoneclient.auth.identity.generic import password from keystoneclient.auth.identity.generic import password
from keystoneclient.auth.identity import v2 from keystoneclient.auth.identity import v2
from keystoneclient.auth.identity import v3 from keystoneclient.auth.identity import v3
@@ -66,3 +69,31 @@ class PasswordTests(utils.GenericPluginTestCase):
def test_symbols(self): def test_symbols(self):
self.assertIs(v3.Password, v3_password.Password) self.assertIs(v3.Password, v3_password.Password)
self.assertIs(v3.PasswordMethod, v3_password.PasswordMethod) self.assertIs(v3.PasswordMethod, v3_password.PasswordMethod)
@mock.patch('sys.stdin', autospec=True)
def test_prompt_password(self, mock_stdin):
parser = argparse.ArgumentParser()
self.PLUGIN_CLASS.register_argparse_arguments(parser)
username = uuid.uuid4().hex
user_domain_id = uuid.uuid4().hex
auth_url = uuid.uuid4().hex
project_id = uuid.uuid4().hex
password = uuid.uuid4().hex
opts = parser.parse_args(['--os-username', username,
'--os-auth-url', auth_url,
'--os-user-domain-id', user_domain_id,
'--os-project-id', project_id])
with mock.patch('getpass.getpass') as mock_getpass:
mock_getpass.return_value = password
mock_stdin.isatty = lambda: True
plugin = self.PLUGIN_CLASS.load_from_argparse_arguments(opts)
self.assertEqual(auth_url, plugin.auth_url)
self.assertEqual(username, plugin._username)
self.assertEqual(project_id, plugin._project_id)
self.assertEqual(user_domain_id, plugin._user_domain_id)
self.assertEqual(password, plugin._password)

View File

@@ -147,6 +147,24 @@ def hash_signed_token(signed_text, mode='md5'):
return hash_.hexdigest() return hash_.hexdigest()
def prompt_user_password():
"""Prompt user for a password
Prompt for a password if stdin is a tty.
"""
password = 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:
password = getpass.getpass('Password: ')
except EOFError:
pass
return password
def prompt_for_password(): def prompt_for_password():
"""Prompt user for password if not provided so the password """Prompt user for password if not provided so the password
doesn't show up in the bash history. doesn't show up in the bash history.