blob: d4e7612dfb7ebcc63279aaed7d565a935e98dc7d [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):
31
32 @classmethod
33 def resource_setup(cls):
34 super(IdentityV3UsersTest, cls).resource_setup()
Jordan Pittier8160d312017-04-18 11:52:23 +020035 cls.creds = cls.os_primary.credentials
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030036 cls.user_id = cls.creds.user_id
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030037
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030038 def _update_password(self, original_password, password):
Daniel Mellado7aea5342016-02-09 09:10:12 +000039 self.non_admin_users_client.update_user_password(
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030040 self.user_id,
41 password=password,
42 original_password=original_password)
Lance Bragstad144c2f42015-11-19 16:42:37 +000043
Morgan Fainberg5b2c7452016-02-02 20:15:47 -080044 # NOTE(morganfainberg): Fernet tokens are not subsecond aware and
45 # Keystone should only be precise to the second. Sleep to ensure
Yaroslav Lobankovcbcb6112016-03-08 12:30:01 -060046 # we are passing the second boundary.
Lance Bragstada2c4ebc2015-10-05 20:34:39 +000047 time.sleep(1)
48
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030049 # check authorization with new password
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030050 self.non_admin_token.auth(user_id=self.user_id, password=password)
51
52 # Reset auth to get a new token with the new password
53 self.non_admin_users_client.auth_provider.clear_auth()
54 self.non_admin_users_client.auth_provider.credentials.password = (
55 password)
56
57 def _restore_password(self, old_pass, new_pass):
58 if CONF.identity_feature_enabled.security_compliance:
59 # First we need to clear the password history
60 unique_count = CONF.identity.user_unique_last_password_count
zhufl8e9a0732017-01-26 16:15:21 +080061 for _ in range(unique_count):
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030062 random_pass = data_utils.rand_password()
63 self._update_password(
64 original_password=new_pass, password=random_pass)
65 new_pass = random_pass
66
67 self._update_password(original_password=new_pass, password=old_pass)
68 # Reset auth again to verify the password restore does work.
69 # Clear auth restores the original credentials and deletes
70 # cached auth data
71 self.non_admin_users_client.auth_provider.clear_auth()
72 # NOTE(lbragstad): Fernet tokens are not subsecond aware and
73 # Keystone should only be precise to the second. Sleep to ensure we
74 # are passing the second boundary before attempting to
75 # authenticate.
76 time.sleep(1)
77 self.non_admin_users_client.auth_provider.set_auth()
78
Ken'ichi Ohmichi44f01272017-01-27 18:44:14 -080079 @decorators.idempotent_id('ad71bd23-12ad-426b-bb8b-195d2b635f27')
nicolasc98a3252018-12-17 13:06:02 -080080 @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
81 'Skipped because environment has an '
82 'immutable user source and solely '
83 'provides read-only access to users.')
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030084 def test_user_update_own_password(self):
85 old_pass = self.creds.password
86 old_token = self.non_admin_client.token
87 new_pass = data_utils.rand_password()
88
Ken'ichi Ohmichi553d7cb2018-07-13 22:53:03 +000089 # to change password back. important for use_dynamic_credentials=false
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030090 self.addCleanup(self._restore_password, old_pass, new_pass)
91
92 # user updates own password
93 self._update_password(original_password=old_pass, password=new_pass)
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030094
95 # authorize with old token should lead to IdentityError (404 code)
96 self.assertRaises(exceptions.IdentityError,
97 self.non_admin_token.auth,
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -030098 token=old_token)
Jane Zadorozhna9c938c62015-07-01 17:06:16 +030099
100 # authorize with old password should lead to Unauthorized
101 self.assertRaises(exceptions.Unauthorized,
102 self.non_admin_token.auth,
103 user_id=self.user_id,
104 password=old_pass)
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300105
106 @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
107 'Security compliance not available.')
Ken'ichi Ohmichi44f01272017-01-27 18:44:14 -0800108 @decorators.idempotent_id('941784ee-5342-4571-959b-b80dd2cea516')
nicolasc98a3252018-12-17 13:06:02 -0800109 @testtools.skipIf(CONF.identity_feature_enabled.immutable_user_source,
110 'Skipped because environment has an '
111 'immutable user source and solely '
112 'provides read-only access to users.')
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300113 def test_password_history_check_self_service_api(self):
114 old_pass = self.creds.password
115 new_pass1 = data_utils.rand_password()
116 new_pass2 = data_utils.rand_password()
117
118 self.addCleanup(self._restore_password, old_pass, new_pass2)
119
120 # Update password
121 self._update_password(original_password=old_pass, password=new_pass1)
122
123 if CONF.identity.user_unique_last_password_count > 1:
124 # Can not reuse a previously set password
125 self.assertRaises(exceptions.BadRequest,
126 self.non_admin_users_client.update_user_password,
127 self.user_id,
128 password=new_pass1,
129 original_password=new_pass1)
130
131 self.assertRaises(exceptions.BadRequest,
132 self.non_admin_users_client.update_user_password,
133 self.user_id,
134 password=old_pass,
135 original_password=new_pass1)
136
137 # A different password can be set
138 self._update_password(original_password=new_pass1, password=new_pass2)
139
140 @testtools.skipUnless(CONF.identity_feature_enabled.security_compliance,
141 'Security compliance not available.')
Ken'ichi Ohmichi44f01272017-01-27 18:44:14 -0800142 @decorators.idempotent_id('a7ad8bbf-2cff-4520-8c1d-96332e151658')
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300143 def test_user_account_lockout(self):
zhufl270c25a2018-10-18 14:56:16 +0800144 if (CONF.identity.user_lockout_failure_attempts <= 0 or
145 CONF.identity.user_lockout_duration <= 0):
146 raise self.skipException(
147 "Both CONF.identity.user_lockout_failure_attempts and "
148 "CONF.identity.user_lockout_duration should be greater than "
149 "zero to test this feature")
150
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300151 password = self.creds.password
152
153 # First, we login using the correct credentials
154 self.non_admin_token.auth(user_id=self.user_id, password=password)
155
156 # Lock user account by using the wrong password to login
157 bad_password = data_utils.rand_password()
zhufl8e9a0732017-01-26 16:15:21 +0800158 for _ in range(CONF.identity.user_lockout_failure_attempts):
Rodrigo Duarte Sousa2d78e8e2016-09-28 10:38:08 -0300159 self.assertRaises(exceptions.Unauthorized,
160 self.non_admin_token.auth,
161 user_id=self.user_id,
162 password=bad_password)
163
164 # The user account must be locked, so now it is not possible to login
165 # even using the correct password
166 self.assertRaises(exceptions.Unauthorized,
167 self.non_admin_token.auth,
168 user_id=self.user_id,
169 password=password)
170
171 # If we wait the required time, the user account will be unlocked
172 time.sleep(CONF.identity.user_lockout_duration + 1)
Rodrigo Duarte Sousa36f68822017-01-05 16:05:00 -0300173 self.non_admin_token.auth(user_id=self.user_id, password=password)