Merge "Add option for user to enter password"
This commit is contained in:
commit
c2c5af603f
@ -139,6 +139,10 @@ swift optional arguments
|
|||||||
compression should be disabled by default by the
|
compression should be disabled by default by the
|
||||||
system SSL library.
|
system SSL library.
|
||||||
|
|
||||||
|
``--prompt``
|
||||||
|
Prompt user to enter a password which overrides any password supplied via
|
||||||
|
``--key``, ``--os-password`` or environment variables.
|
||||||
|
|
||||||
Authentication
|
Authentication
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -17,11 +17,13 @@
|
|||||||
from __future__ import print_function, unicode_literals
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import getpass
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
|
import warnings
|
||||||
|
|
||||||
from os import environ, walk, _exit as os_exit
|
from os import environ, walk, _exit as os_exit
|
||||||
from os.path import isfile, isdir, join
|
from os.path import isfile, isdir, join
|
||||||
@ -1410,6 +1412,30 @@ class HelpFormatter(argparse.HelpFormatter):
|
|||||||
return action.dest
|
return action.dest
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_for_password():
|
||||||
|
"""
|
||||||
|
Prompt the user for a password.
|
||||||
|
|
||||||
|
:raise SystemExit: if a password cannot be entered without it being echoed
|
||||||
|
to the terminal.
|
||||||
|
:return: the entered password.
|
||||||
|
"""
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('error', category=getpass.GetPassWarning,
|
||||||
|
append=True)
|
||||||
|
try:
|
||||||
|
# temporarily set signal handling back to default to avoid user
|
||||||
|
# Ctrl-c leaving terminal in weird state
|
||||||
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
return getpass.getpass()
|
||||||
|
except EOFError:
|
||||||
|
return None
|
||||||
|
except getpass.GetPassWarning:
|
||||||
|
exit('Input stream incompatible with --prompt option')
|
||||||
|
finally:
|
||||||
|
signal.signal(signal.SIGINT, immediate_exit)
|
||||||
|
|
||||||
|
|
||||||
def parse_args(parser, args, enforce_requires=True):
|
def parse_args(parser, args, enforce_requires=True):
|
||||||
options, args = parser.parse_known_args(args or ['-h'])
|
options, args = parser.parse_known_args(args or ['-h'])
|
||||||
options = vars(options)
|
options = vars(options)
|
||||||
@ -1435,6 +1461,10 @@ def parse_args(parser, args, enforce_requires=True):
|
|||||||
if args and args[0] == 'tempurl':
|
if args and args[0] == 'tempurl':
|
||||||
return options, args
|
return options, args
|
||||||
|
|
||||||
|
# do this before process_options sets default auth version
|
||||||
|
if enforce_requires and options['prompt']:
|
||||||
|
options['key'] = options['os_password'] = prompt_for_password()
|
||||||
|
|
||||||
# Massage auth version; build out os_options subdict
|
# Massage auth version; build out os_options subdict
|
||||||
process_options(options)
|
process_options(options)
|
||||||
|
|
||||||
@ -1506,6 +1536,7 @@ def main(arguments=None):
|
|||||||
[--os-key <client-certificate-key-file>]
|
[--os-key <client-certificate-key-file>]
|
||||||
[--no-ssl-compression]
|
[--no-ssl-compression]
|
||||||
[--force-auth-retry]
|
[--force-auth-retry]
|
||||||
|
[--prompt]
|
||||||
<subcommand> [--help] [<subcommand options>]
|
<subcommand> [--help] [<subcommand options>]
|
||||||
|
|
||||||
Command-line interface to the OpenStack Swift API.
|
Command-line interface to the OpenStack Swift API.
|
||||||
@ -1620,6 +1651,12 @@ Examples:
|
|||||||
default=False,
|
default=False,
|
||||||
help='Force a re-auth attempt on '
|
help='Force a re-auth attempt on '
|
||||||
'any error other than 401 unauthorized')
|
'any error other than 401 unauthorized')
|
||||||
|
parser.add_argument('--prompt',
|
||||||
|
action='store_true', dest='prompt',
|
||||||
|
default=False,
|
||||||
|
help='Prompt user to enter a password which overrides '
|
||||||
|
'any password supplied via --key, --os-password '
|
||||||
|
'or environment variables.')
|
||||||
|
|
||||||
os_grp = parser.add_argument_group("OpenStack authentication options")
|
os_grp = parser.add_argument_group("OpenStack authentication options")
|
||||||
os_grp.add_argument('--os-username',
|
os_grp.add_argument('--os-username',
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from genericpath import getmtime
|
from genericpath import getmtime
|
||||||
|
|
||||||
|
import getpass
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -2283,17 +2285,66 @@ class TestParsing(TestBase):
|
|||||||
os_opts = {"password": "secret",
|
os_opts = {"password": "secret",
|
||||||
"auth_url": "http://example.com:5000/v3"}
|
"auth_url": "http://example.com:5000/v3"}
|
||||||
args = _make_args("stat", opts, os_opts)
|
args = _make_args("stat", opts, os_opts)
|
||||||
self.assertRaises(SystemExit, swiftclient.shell.main, args)
|
with self.assertRaises(SystemExit) as cm:
|
||||||
|
swiftclient.shell.main(args)
|
||||||
|
self.assertIn(
|
||||||
|
'Auth version 3 requires either OS_USERNAME or OS_USER_ID',
|
||||||
|
str(cm.exception))
|
||||||
|
|
||||||
os_opts = {"username": "user",
|
os_opts = {"username": "user",
|
||||||
"auth_url": "http://example.com:5000/v3"}
|
"auth_url": "http://example.com:5000/v3"}
|
||||||
args = _make_args("stat", opts, os_opts)
|
args = _make_args("stat", opts, os_opts)
|
||||||
self.assertRaises(SystemExit, swiftclient.shell.main, args)
|
with self.assertRaises(SystemExit) as cm:
|
||||||
|
swiftclient.shell.main(args)
|
||||||
|
self.assertIn('Auth version 3 requires OS_PASSWORD', str(cm.exception))
|
||||||
|
|
||||||
os_opts = {"username": "user",
|
os_opts = {"username": "user",
|
||||||
"password": "secret"}
|
"password": "secret"}
|
||||||
args = _make_args("stat", opts, os_opts)
|
args = _make_args("stat", opts, os_opts)
|
||||||
self.assertRaises(SystemExit, swiftclient.shell.main, args)
|
with self.assertRaises(SystemExit) as cm:
|
||||||
|
swiftclient.shell.main(args)
|
||||||
|
self.assertIn('Auth version 3 requires OS_AUTH_URL', str(cm.exception))
|
||||||
|
|
||||||
|
def test_password_prompt(self):
|
||||||
|
def do_test(opts, os_opts, auth_version):
|
||||||
|
args = _make_args("stat", opts, os_opts)
|
||||||
|
result = [None, None]
|
||||||
|
fake_command = self._make_fake_command(result)
|
||||||
|
with mock.patch('swiftclient.shell.st_stat', fake_command):
|
||||||
|
with mock.patch('getpass.getpass',
|
||||||
|
return_value='input_pwd') as mock_getpass:
|
||||||
|
swiftclient.shell.main(args)
|
||||||
|
mock_getpass.assert_called_once_with()
|
||||||
|
self.assertEqual('input_pwd', result[0]['key'])
|
||||||
|
self.assertEqual('input_pwd', result[0]['os_password'])
|
||||||
|
|
||||||
|
# ctrl-D
|
||||||
|
with self.assertRaises(SystemExit) as cm:
|
||||||
|
with mock.patch('swiftclient.shell.st_stat', fake_command):
|
||||||
|
with mock.patch('getpass.getpass',
|
||||||
|
side_effect=EOFError) as mock_getpass:
|
||||||
|
swiftclient.shell.main(args)
|
||||||
|
mock_getpass.assert_called_once_with()
|
||||||
|
self.assertIn(
|
||||||
|
'Auth version %s requires' % auth_version, str(cm.exception))
|
||||||
|
|
||||||
|
# force getpass to think it needs to use raw input
|
||||||
|
with self.assertRaises(SystemExit) as cm:
|
||||||
|
with mock.patch('getpass.getpass', getpass.fallback_getpass):
|
||||||
|
swiftclient.shell.main(args)
|
||||||
|
self.assertIn(
|
||||||
|
'Input stream incompatible', str(cm.exception))
|
||||||
|
|
||||||
|
opts = {"prompt": None, "user": "bob", "key": "secret",
|
||||||
|
"auth": "http://example.com:8080/auth/v1.0"}
|
||||||
|
do_test(opts, {}, '1.0')
|
||||||
|
os_opts = {"username": "user",
|
||||||
|
"password": "secret",
|
||||||
|
"auth_url": "http://example.com:5000/v3"}
|
||||||
|
opts = {"auth_version": "2.0", "prompt": None}
|
||||||
|
do_test(opts, os_opts, '2.0')
|
||||||
|
opts = {"auth_version": "3", "prompt": None}
|
||||||
|
do_test(opts, os_opts, '3')
|
||||||
|
|
||||||
def test_no_tenant_name_or_id_v2(self):
|
def test_no_tenant_name_or_id_v2(self):
|
||||||
os_opts = {"password": "secret",
|
os_opts = {"password": "secret",
|
||||||
|
Loading…
Reference in New Issue
Block a user