diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 0258f93122..54a06b049f 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -233,12 +233,15 @@ def get_effective_log_level(): return min_log_lvl -def get_password(stdin): +def get_password(stdin, prompt=None, confirm=True): + message = prompt or "User Password:" if hasattr(stdin, 'isatty') and stdin.isatty(): try: while True: - first_pass = getpass.getpass("User password: ") - second_pass = getpass.getpass("Repeat user password: ") + first_pass = getpass.getpass(message) + if not confirm: + return first_pass + second_pass = getpass.getpass("Repeat " + message) if first_pass == second_pass: return first_pass print("The passwords entered were not the same") diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 38c3497339..6ba54368a8 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -323,6 +323,35 @@ class SetUser(command.Command): return +class SetPasswordUser(command.Command): + """Change current user password""" + + log = logging.getLogger(__name__ + '.SetPasswordUser') + + def get_parser(self, prog_name): + parser = super(SetPasswordUser, self).get_parser(prog_name) + parser.add_argument( + '--password', + metavar='', + help='New user password' + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)', parsed_args) + identity_client = self.app.client_manager.identity + + current_password = utils.get_password( + self.app.stdin, prompt="Current Password:", confirm=False) + + password = parsed_args.password + if password is None: + password = utils.get_password( + self.app.stdin, prompt="New Password:") + + identity_client.users.update_password(current_password, password) + + class ShowUser(show.ShowOne): """Show user details""" diff --git a/openstackclient/tests/identity/v3/test_user.py b/openstackclient/tests/identity/v3/test_user.py index 569d91401c..42df57736e 100644 --- a/openstackclient/tests/identity/v3/test_user.py +++ b/openstackclient/tests/identity/v3/test_user.py @@ -13,7 +13,9 @@ # under the License. # +import contextlib import copy + import mock from openstackclient.identity.v3 import user @@ -944,6 +946,52 @@ class TestUserSet(TestUser): ) +class TestUserSetPassword(TestUser): + + def setUp(self): + super(TestUserSetPassword, self).setUp() + self.cmd = user.SetPasswordUser(self.app, None) + + @staticmethod + @contextlib.contextmanager + def _mock_get_password(*passwords): + mocker = mock.Mock(side_effect=passwords) + with mock.patch("openstackclient.common.utils.get_password", mocker): + yield + + def test_user_password_change(self): + current_pass = 'old_pass' + new_pass = 'new_pass' + arglist = [ + '--password', new_pass, + ] + verifylist = [ + ('password', new_pass), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # Mock getting user current password. + with self._mock_get_password(current_pass): + self.cmd.take_action(parsed_args) + + self.users_mock.update_password.assert_called_with( + current_pass, new_pass + ) + + def test_user_create_password_prompt(self): + current_pass = 'old_pass' + new_pass = 'new_pass' + parsed_args = self.check_parser(self.cmd, [], []) + + # Mock getting user current and new password. + with self._mock_get_password(current_pass, new_pass): + self.cmd.take_action(parsed_args) + + self.users_mock.update_password.assert_called_with( + current_pass, new_pass + ) + + class TestUserShow(TestUser): def setUp(self): diff --git a/setup.cfg b/setup.cfg index da07d3edc5..0106e7a02b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -242,6 +242,7 @@ openstack.identity.v3 = user_delete = openstackclient.identity.v3.user:DeleteUser user_list = openstackclient.identity.v3.user:ListUser user_set = openstackclient.identity.v3.user:SetUser + user_password_set = openstackclient.identity.v3.user:SetPasswordUser user_show = openstackclient.identity.v3.user:ShowUser openstack.image.v1 =