blob: 6425ea9cf31a9991d3e4aa8748ef2ab2210dd6c6 [file] [log] [blame]
Jane Zadorozhna9c938c62015-07-01 17:06:16 +03001# Copyright 2015 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Lance Bragstada2c4ebc2015-10-05 20:34:39 +000016import time
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030017
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030018import testtools
19
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030020from tempest.api.identity import base
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030021from tempest import config
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050022from tempest.lib.common.utils import data_utils
Ken'ichi Ohmichi44f01272017-01-27 18:44:14 -080023from tempest.lib import decorators
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050024from tempest.lib import exceptions
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030025
26
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030027CONF = config.CONF
28
29
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030030class IdentityV3UsersTest(base.BaseIdentityV3Test):
zhufl8e3aacd2020-04-27 14:46:46 +080031 """Test identity user password"""
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030032
33 @classmethod
34 def resource_setup(cls):
35 super(IdentityV3UsersTest, cls).resource_setup()
Jordan Pittier8160d312017-04-18 11:52:23 +020036 cls.creds = cls.os_primary.credentials
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030037 cls.user_id = cls.creds.user_id
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030038
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030039 def _update_password(self, original_password, password):
Daniel Mellado7aea5342016-02-09 09:10:12 +000040 self.non_admin_users_client.update_user_password(
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030041 self.user_id,
42 password=password,
43 original_password=original_password)
Lance Bragstad144c2f42015-11-19 16:42:37 +000044
Morgan Fainberg5b2c7452016-02-02 20:15:47 -080045 # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
46 # Keystone should only be precise to the second. Sleep to ensure
Yaroslav Lobankovcbcb6112016-03-08 12:30:01 -060047 # we are passing the second boundary.
Lance Bragstada2c4ebc2015-10-05 20:34:39 +000048 time.sleep(1)
49
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030050 # check authorization with new password
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030051 self.non_admin_token.auth(user_id=self.user_id, password=password)
52
53 # Reset auth to get a new token with the new password
54 self.non_admin_users_client.auth_provider.clear_auth()
55 self.non_admin_users_client.auth_provider.credentials.password = (
56 password)
57
58 def _restore_password(self, old_pass, new_pass):
59 if CONF.identity_feature_enabled.security_compliance:
60 # First we need to clear the password history
61 unique_count = CONF.identity.user_unique_last_password_count
zhufl8e9a0732017-01-26 16:15:21 +080062 for _ in range(unique_count):
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030063 random_pass = data_utils.rand_password()
64 self._update_password(
65 original_password=new_pass, password=random_pass)
66 new_pass = random_pass
67
68 self._update_password(original_password=new_pass, password=old_pass)
69 # Reset auth again to verify the password restore does work.
70 # Clear auth restores the original credentials and deletes
71 # cached auth data
72 self.non_admin_users_client.auth_provider.clear_auth()
73 # NOTE(lbragstad): Fernet tokens are not subsecond aware and
74 # Keystone should only be precise to the second. Sleep to ensure we
75 # are passing the second boundary before attempting to
76 # authenticate.
77 time.sleep(1)
78 self.non_admin_users_client.auth_provider.set_auth()
79
Ken'ichi Ohmichi44f01272017-01-27 18:44:14 -080080 @decorators.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
nicolasc98a3252018-12-17 13:06:02 -080081 @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
82 'Skipped because environment has an '
83 'immutable user source and solely '
84 'provides read-only access to users.')
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030085 def test_user_update_own_password(self):
zhufl8e3aacd2020-04-27 14:46:46 +080086 """Test updating user's own password"""
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030087 old_pass = self.creds.password
88 old_token = self.non_admin_client.token
89 new_pass = data_utils.rand_password()
90
Ken'ichi Ohmichi553d7cb2018-07-13 22:53:03 +000091 # to change password back. important for use_dynamic_credentials=false
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030092 self.addCleanup(self._restore_password, old_pass, new_pass)
93
94 # user updates own password
95 self._update_password(original_password=old_pass, password=new_pass)
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030096
97 # authorize with old token should lead to IdentityError (404 code)
98 self.assertRaises(exceptions.IdentityError,
99 self.non_admin_token.auth,
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300100 token=old_token)
Jane Zadorozhna9c938c62015-07-01 17:06:16 +0300101
102 # authorize with old password should lead to Unauthorized
103 self.assertRaises(exceptions.Unauthorized,
104 self.non_admin_token.auth,
105 user_id=self.user_id,
106 password=old_pass)
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300107
108 @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
109 'Security compliance not available.')
Ken'ichi Ohmichi44f01272017-01-27 18:44:14 -0800110 @decorators.idempotent_id('941784ee-5342-4571-959b-b80dd2cea516')
nicolasc98a3252018-12-17 13:06:02 -0800111 @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
112 'Skipped because environment has an '
113 'immutable user source and solely '
114 'provides read-only access to users.')
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300115 def test_password_history_check_self_service_api(self):
zhufl8e3aacd2020-04-27 14:46:46 +0800116 """Test checking password changing history"""
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300117 old_pass = self.creds.password
118 new_pass1 = data_utils.rand_password()
119 new_pass2 = data_utils.rand_password()
120
121 self.addCleanup(self._restore_password, old_pass, new_pass2)
122
123 # Update password
124 self._update_password(original_password=old_pass, password=new_pass1)
125
126 if CONF.identity.user_unique_last_password_count > 1:
127 # Can not reuse a previously set password
128 self.assertRaises(exceptions.BadRequest,
129 self.non_admin_users_client.update_user_password,
130 self.user_id,
131 password=new_pass1,
132 original_password=new_pass1)
133
134 self.assertRaises(exceptions.BadRequest,
135 self.non_admin_users_client.update_user_password,
136 self.user_id,
137 password=old_pass,
138 original_password=new_pass1)
139
140 # A different password can be set
141 self._update_password(original_password=new_pass1, password=new_pass2)
142
143 @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
144 'Security compliance not available.')
Ken'ichi Ohmichi44f01272017-01-27 18:44:14 -0800145 @decorators.idempotent_id('a7ad8bbf-2cff-4520-8c1d-96332e151658')
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300146 def test_user_account_lockout(self):
zhufl8e3aacd2020-04-27 14:46:46 +0800147 """Test locking out user account after failure attempts"""
zhufl270c25a2018-10-18 14:56:16 +0800148 if (CONF.identity.user_lockout_failure_attempts <= 0 or
149 CONF.identity.user_lockout_duration <= 0):
150 raise self.skipException(
151 "Both CONF.identity.user_lockout_failure_attempts and "
152 "CONF.identity.user_lockout_duration should be greater than "
153 "zero to test this feature")
154
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300155 password = self.creds.password
156
157 # First, we login using the correct credentials
158 self.non_admin_token.auth(user_id=self.user_id, password=password)
159
160 # Lock user account by using the wrong password to login
161 bad_password = data_utils.rand_password()
zhufl8e9a0732017-01-26 16:15:21 +0800162 for _ in range(CONF.identity.user_lockout_failure_attempts):
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300163 self.assertRaises(exceptions.Unauthorized,
164 self.non_admin_token.auth,
165 user_id=self.user_id,
166 password=bad_password)
167
168 # The user account must be locked, so now it is not possible to login
169 # even using the correct password
170 self.assertRaises(exceptions.Unauthorized,
171 self.non_admin_token.auth,
172 user_id=self.user_id,
173 password=password)
174
175 # If we wait the required time, the user account will be unlocked
176 time.sleep(CONF.identity.user_lockout_duration + 1)
Rodrigo Duarte Sousa36f68822017-01-05 16:05:00 -0300177 self.non_admin_token.auth(user_id=self.user_id, password=password)