Added test_user_update_own_password tests for Identity v2, v3

Added test_user_update_own_password tests for Identity v2, v3
that test non admin user capability to change own password via
public identity client.
Tests check that after updating password token is revoked and
new token is valid; old password is invalid and new one is valid.

Change-Id: I4108312e01bbcacaa9ac6d6b9960ca0e75533f33
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
new file mode 100644
index 0000000..4e5b41d
--- /dev/null
+++ b/tempest/api/identity/v2/test_users.py
@@ -0,0 +1,78 @@
+# Copyright 2015 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest_lib.common.utils import data_utils
+from tempest_lib import exceptions
+
+from tempest.api.identity import base
+from tempest import manager
+from tempest import test
+
+
+class IdentityUsersTest(base.BaseIdentityV2Test):
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityUsersTest, cls).resource_setup()
+        cls.creds = cls.os.credentials
+        cls.username = cls.creds.username
+        cls.password = cls.creds.password
+        cls.tenant_name = cls.creds.tenant_name
+
+    @test.idempotent_id('165859c9-277f-4124-9479-a7d1627b0ca7')
+    def test_user_update_own_password(self):
+        self.new_creds = copy.copy(self.creds.credentials)
+        self.new_creds.password = data_utils.rand_password()
+        # we need new non-admin Identity Client with new credentials, since
+        # current non_admin_client token will be revoked after updating
+        # password
+        self.non_admin_client_for_cleanup = copy.copy(self.non_admin_client)
+        self.non_admin_client_for_cleanup.auth_provider = (
+            manager.get_auth_provider(self.new_creds))
+        user_id = self.creds.credentials.user_id
+        old_pass = self.creds.credentials.password
+        new_pass = self.new_creds.password
+
+        # to change password back. important for allow_tenant_isolation = false
+        self.addCleanup(
+            self.non_admin_client_for_cleanup.update_user_own_password,
+            user_id=user_id,
+            new_pass=old_pass,
+            old_pass=new_pass)
+
+        # user updates own password
+        resp = self.non_admin_client.update_user_own_password(
+            user_id=user_id, new_pass=new_pass, old_pass=old_pass)
+
+        # check authorization with new token
+        self.non_admin_token_client.auth_token(resp['token']['id'])
+        # check authorization with new password
+        self.non_admin_token_client.auth(self.username,
+                                         new_pass,
+                                         self.tenant_name)
+
+        # authorize with old token should lead to Unauthorized
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_token_client.auth_token,
+                          self.non_admin_client.token)
+
+        # authorize with old password should lead to Unauthorized
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_token_client.auth,
+                          self.username,
+                          old_pass,
+                          self.tenant_name)
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
new file mode 100644
index 0000000..a1f664f
--- /dev/null
+++ b/tempest/api/identity/v3/test_users.py
@@ -0,0 +1,72 @@
+# Copyright 2015 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest_lib.common.utils import data_utils
+from tempest_lib import exceptions
+
+from tempest.api.identity import base
+from tempest import manager
+from tempest import test
+
+
+class IdentityV3UsersTest(base.BaseIdentityV3Test):
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityV3UsersTest, cls).resource_setup()
+        cls.creds = cls.os.credentials
+        cls.user_id = cls.creds.user_id
+        cls.username = cls.creds.username
+        cls.password = cls.creds.password
+
+    @test.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
+    def test_user_update_own_password(self):
+        self.new_creds = copy.copy(self.creds.credentials)
+        self.new_creds.password = data_utils.rand_password()
+        # we need new non-admin Identity V3 Client with new credentials, since
+        # current non_admin_client token will be revoked after updating
+        # password
+        self.non_admin_client_for_cleanup = copy.copy(self.non_admin_client)
+        self.non_admin_client_for_cleanup.auth_provider = (
+            manager.get_auth_provider(self.new_creds))
+        user_id = self.creds.credentials.user_id
+        old_pass = self.creds.credentials.password
+        new_pass = self.new_creds.password
+        # to change password back. important for allow_tenant_isolation = false
+        self.addCleanup(
+            self.non_admin_client_for_cleanup.update_user_password,
+            user_id=user_id,
+            password=old_pass,
+            original_password=new_pass)
+
+        # user updates own password
+        self.non_admin_client.update_user_password(
+            user_id=user_id, password=new_pass, original_password=old_pass)
+
+        # check authorization with new password
+        self.non_admin_token.auth(user_id=self.user_id, password=new_pass)
+
+        # authorize with old token should lead to IdentityError (404 code)
+        self.assertRaises(exceptions.IdentityError,
+                          self.non_admin_token.auth,
+                          token=self.non_admin_client.token)
+
+        # authorize with old password should lead to Unauthorized
+        self.assertRaises(exceptions.Unauthorized,
+                          self.non_admin_token.auth,
+                          user_id=self.user_id,
+                          password=old_pass)
diff --git a/tempest/services/identity/v2/json/identity_client.py b/tempest/services/identity/v2/json/identity_client.py
index e6416d6..8eeefe7 100644
--- a/tempest/services/identity/v2/json/identity_client.py
+++ b/tempest/services/identity/v2/json/identity_client.py
@@ -297,6 +297,17 @@
         self.expected_success(200, resp.status)
         return service_client.ResponseBody(resp, self._parse_resp(body))
 
+    def update_user_own_password(self, user_id, new_pass, old_pass):
+        """User updates own password"""
+        patch_body = {
+            "password": new_pass,
+            "original_password": old_pass
+        }
+        patch_body = json.dumps({'user': patch_body})
+        resp, body = self.patch('OS-KSCRUD/users/%s' % user_id, patch_body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, self._parse_resp(body))
+
     def list_extensions(self):
         """List all the extensions."""
         resp, body = self.get('/extensions')