Prompt for password on CLI if not provided
load_from_argparse_arguments is very specifically for use with argparse. We can therefore safely prompt for a password from the user if none is provided and it won't affect config options or other loading mechanisms. Change-Id: Ib76743b768c5f0eef756184f1da49613423298f0
This commit is contained in:
		| @@ -82,3 +82,11 @@ class Password(base.BaseGenericPlugin): | ||||
|         options = super(Password, cls).get_options() | ||||
|         options.extend(get_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) | ||||
|   | ||||
| @@ -144,6 +144,14 @@ class Password(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 | ||||
|     def get_options(cls): | ||||
|         options = super(Password, cls).get_options() | ||||
|   | ||||
| @@ -13,6 +13,7 @@ | ||||
| from oslo_config import cfg | ||||
|  | ||||
| from keystoneclient.auth.identity.v3 import base | ||||
| from keystoneclient import utils | ||||
|  | ||||
|  | ||||
| __all__ = ['PasswordMethod', 'Password'] | ||||
| @@ -86,3 +87,11 @@ class Password(base.AuthConstructor): | ||||
|         ]) | ||||
|  | ||||
|         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) | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| from __future__ import print_function | ||||
|  | ||||
| import argparse | ||||
| import getpass | ||||
| import logging | ||||
| import os | ||||
| import sys | ||||
| @@ -296,13 +295,8 @@ class OpenStackIdentityShell(object): | ||||
|                         '--os-username or env[OS_USERNAME]') | ||||
|  | ||||
|                 if not args.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: | ||||
|                             args.os_password = getpass.getpass('OS Password: ') | ||||
|                         except EOFError: | ||||
|                             pass | ||||
|                     args.os_password = utils.prompt_user_password() | ||||
|  | ||||
|                     # No password because we didn't have a tty or the | ||||
|                     # user Ctl-D when prompted? | ||||
|                     if not args.os_password: | ||||
|   | ||||
| @@ -10,9 +10,12 @@ | ||||
| # License for the specific language governing permissions and limitations | ||||
| # under the License. | ||||
|  | ||||
| import argparse | ||||
| import copy | ||||
| import uuid | ||||
|  | ||||
| import mock | ||||
|  | ||||
| from keystoneclient.auth.identity import v2 | ||||
| from keystoneclient import exceptions | ||||
| from keystoneclient import session | ||||
| @@ -294,3 +297,28 @@ class V2IdentityPlugin(utils.TestCase): | ||||
|     def test_password_with_no_user_id_or_name(self): | ||||
|         self.assertRaises(TypeError, | ||||
|                           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) | ||||
|   | ||||
| @@ -10,9 +10,12 @@ | ||||
| # License for the specific language governing permissions and limitations | ||||
| # under the License. | ||||
|  | ||||
| import argparse | ||||
| import copy | ||||
| import uuid | ||||
|  | ||||
| import mock | ||||
|  | ||||
| from keystoneclient import access | ||||
| from keystoneclient.auth.identity import v3 | ||||
| from keystoneclient.auth.identity.v3 import base as v3_base | ||||
| @@ -531,3 +534,32 @@ class V3IdentityPlugin(utils.TestCase): | ||||
|         s = session.Session() | ||||
|  | ||||
|         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) | ||||
|   | ||||
| @@ -10,8 +10,11 @@ | ||||
| # License for the specific language governing permissions and limitations | ||||
| # under the License. | ||||
|  | ||||
| import argparse | ||||
| import uuid | ||||
|  | ||||
| import mock | ||||
|  | ||||
| from keystoneclient.auth.identity.generic import password | ||||
| from keystoneclient.auth.identity import v2 | ||||
| from keystoneclient.auth.identity import v3 | ||||
| @@ -66,3 +69,31 @@ class PasswordTests(utils.GenericPluginTestCase): | ||||
|     def test_symbols(self): | ||||
|         self.assertIs(v3.Password, v3_password.Password) | ||||
|         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) | ||||
|   | ||||
| @@ -147,6 +147,24 @@ def hash_signed_token(signed_text, mode='md5'): | ||||
|     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(): | ||||
|     """Prompt user for password if not provided so the password | ||||
|     doesn't show up in the bash history. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jamie Lennox
					Jamie Lennox