From 5cbecc130ef2aacd5d9bd265b814e6f8140374f9 Mon Sep 17 00:00:00 2001
From: Guang Yee <guang.yee@hpe.com>
Date: Thu, 7 Jan 2016 18:56:44 -0800
Subject: [PATCH] Support non-interactive user password update

Currently user password update require interactive prompting of user's
original password. This is problematic because we can't support
non-interactive applications and therefore hinders automation. This
patch make it possible by optionally accepting an
'--original-password' argument. If specified, we would use it instead
of prompting.

DocImpact

Change-Id: I2d994e8c2be949f7ae616ac1d1594fb94e1a27cd
Closes-Bug: 1531360
---
 openstackclient/identity/v3/user.py           | 31 +++++++++++++++++--
 .../tests/identity/v3/test_user.py            | 19 ++++++++++++
 .../notes/bug-1531360-0f5c62d18088e5b5.yaml   |  5 +++
 3 files changed, 53 insertions(+), 2 deletions(-)
 create mode 100644 releasenotes/notes/bug-1531360-0f5c62d18088e5b5.yaml

diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index eaef8f0523..43a116cb36 100644
--- a/openstackclient/identity/v3/user.py
+++ b/openstackclient/identity/v3/user.py
@@ -392,14 +392,41 @@ class SetPasswordUser(command.Command):
             metavar='<new-password>',
             help='New user password'
         )
+        parser.add_argument(
+            '--original-password',
+            metavar='<original-password>',
+            help='Original user password'
+        )
         return parser
 
     @utils.log_method(log)
     def take_action(self, parsed_args):
         identity_client = self.app.client_manager.identity
 
-        current_password = utils.get_password(
-            self.app.stdin, prompt="Current Password:", confirm=False)
+        # FIXME(gyee): there are two scenarios:
+        #
+        # 1. user update password for himself
+        # 2. admin update password on behalf of the user. This is an unlikely
+        #    scenario because that will require admin knowing the user's
+        #    original password which is forbidden under most security
+        #    policies.
+        #
+        # Of the two scenarios above, user either authenticate using its
+        # original password or an authentication token. For scenario #1,
+        # if user is authenticating with its original password (i.e. passing
+        # --os-password argument), we can just make use of it instead of using
+        # --original-password or prompting. For scenario #2, admin will need
+        # to specify --original-password option or this won't work because
+        # --os-password is the admin's own password. In the future if we stop
+        # supporting scenario #2 then we can just do this.
+        #
+        # current_password = (parsed_args.original_password or
+        #                     self.app.cloud.password)
+        #
+        current_password = parsed_args.original_password
+        if current_password is None:
+            current_password = utils.get_password(
+                self.app.stdin, prompt="Current Password:", confirm=False)
 
         password = parsed_args.password
         if password is None:
diff --git a/openstackclient/tests/identity/v3/test_user.py b/openstackclient/tests/identity/v3/test_user.py
index 76d5f83487..41fab60ee7 100644
--- a/openstackclient/tests/identity/v3/test_user.py
+++ b/openstackclient/tests/identity/v3/test_user.py
@@ -1095,6 +1095,25 @@ class TestUserSetPassword(TestUser):
             current_pass, new_pass
         )
 
+    def test_user_password_change_no_prompt(self):
+        current_pass = 'old_pass'
+        new_pass = 'new_pass'
+        arglist = [
+            '--password', new_pass,
+            '--original-password', current_pass,
+        ]
+        verifylist = [
+            ('password', new_pass),
+            ('original_password', current_pass),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        self.cmd.take_action(parsed_args)
+
+        self.users_mock.update_password.assert_called_with(
+            current_pass, new_pass
+        )
+
 
 class TestUserShow(TestUser):
 
diff --git a/releasenotes/notes/bug-1531360-0f5c62d18088e5b5.yaml b/releasenotes/notes/bug-1531360-0f5c62d18088e5b5.yaml
new file mode 100644
index 0000000000..992f2656e2
--- /dev/null
+++ b/releasenotes/notes/bug-1531360-0f5c62d18088e5b5.yaml
@@ -0,0 +1,5 @@
+---
+fixes:
+  - |
+    Support non-interactive user password update
+    [Bug `1531360 <https://bugs.launchpad.net/bugs/1531360>`_]