diff options
author | Ronald De Rose <ronald.de.rose@intel.com> | 2016-09-28 23:06:02 +0000 |
---|---|---|
committer | ayoung <ayoung@redhat.com> | 2016-10-11 19:55:38 +0000 |
commit | 18d6eb76ce7bf46e9ced352b7af366112df62b11 (patch) | |
tree | 85cdff28f1c2ded2b1c792094329d1db1108c40c | |
parent | 339e7cc798aed24b7697980eb7cf8e20498d436d (diff) | |
download | keystone-18d6eb76ce7bf46e9ced352b7af366112df62b11.tar.gz |
Remove password history validation from admin password resets
This patch removes password history validation from the update_user
(admin password reset) backend method.
backport: newton
Closes-Bug: #1630092
Change-Id: Ic567841703c1da495131cbb052636bbe90d54819
(cherry-picked from 432fa4acd6d6297fdfd32de86a043488b87c7c43)
-rw-r--r-- | keystone/identity/backends/sql.py | 2 | ||||
-rw-r--r-- | keystone/identity/backends/sql_model.py | 4 | ||||
-rw-r--r-- | keystone/tests/unit/identity/test_backend_sql.py | 117 |
3 files changed, 69 insertions, 54 deletions
diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py index 31dea1586..2f67d1440 100644 --- a/keystone/identity/backends/sql.py +++ b/keystone/identity/backends/sql.py @@ -157,8 +157,6 @@ class Identity(base.IdentityDriverV8): def update_user(self, user_id, user): with sql.session_for_write() as session: user_ref = self._get_user(session, user_id) - if 'password' in user: - self._validate_password_history(user['password'], user_ref) old_user_dict = user_ref.to_dict() user = utils.hash_user_password(user) for k in user: diff --git a/keystone/identity/backends/sql_model.py b/keystone/identity/backends/sql_model.py index 5ef4ff543..49cae64e2 100644 --- a/keystone/identity/backends/sql_model.py +++ b/keystone/identity/backends/sql_model.py @@ -109,6 +109,10 @@ class User(sql.ModelBase, sql.DictBase): now = datetime.datetime.utcnow() if not self.local_user: self.local_user = LocalUser() + # truncate extra passwords + if self.local_user.passwords: + unique_cnt = CONF.security_compliance.unique_last_password_count + self.local_user.passwords = self.local_user.passwords[-unique_cnt:] # set all previous passwords to be expired for ref in self.local_user.passwords: if not ref.expires_at or ref.expires_at > now: diff --git a/keystone/tests/unit/identity/test_backend_sql.py b/keystone/tests/unit/identity/test_backend_sql.py index 48769502d..11a497c25 100644 --- a/keystone/tests/unit/identity/test_backend_sql.py +++ b/keystone/tests/unit/identity/test_backend_sql.py @@ -157,84 +157,95 @@ class DisableInactiveUserTests(test_backend_sql.SqlTests): class PasswordHistoryValidationTests(test_backend_sql.SqlTests): def setUp(self): super(PasswordHistoryValidationTests, self).setUp() - self.passwords = [uuid.uuid4().hex, - uuid.uuid4().hex, - uuid.uuid4().hex, - uuid.uuid4().hex] self.max_cnt = 3 self.config_fixture.config(group='security_compliance', unique_last_password_count=self.max_cnt) def test_validate_password_history_with_invalid_password(self): - user = self._create_user(self.passwords[0]) - self.assertValidPasswordUpdate(user, self.passwords[1]) - # Attempt to update with the initial password - user['password'] = self.passwords[0] - self.assertRaises(exception.PasswordValidationError, - self.identity_api.update_user, - user['id'], - user) - - def test_validate_password_history_via_self_service_change_password(self): - user = self._create_user(self.passwords[0]) - # Attempt to change password to a unique password - self.identity_api.change_password(self.make_request(), - user_id=user['id'], - original_password=self.passwords[0], - new_password=self.passwords[1]) - self.identity_api.authenticate(self.make_request(), - user_id=user['id'], - password=self.passwords[1]) - # Attempt to change password with the same password + password = uuid.uuid4().hex + user = self._create_user(password) + # Attempt to change to the same password self.assertRaises(exception.PasswordValidationError, self.identity_api.change_password, self.make_request(), user_id=user['id'], - original_password=self.passwords[1], - new_password=self.passwords[1]) - # Attempt to change password with the initial password + original_password=password, + new_password=password) + # Attempt to change to a unique password + new_password = uuid.uuid4().hex + self.assertValidChangePassword(user['id'], password, new_password) + # Attempt to change back to the initial password self.assertRaises(exception.PasswordValidationError, self.identity_api.change_password, self.make_request(), user_id=user['id'], - original_password=self.passwords[1], - new_password=self.passwords[0]) + original_password=new_password, + new_password=password) def test_validate_password_history_with_valid_password(self): - user = self._create_user(self.passwords[0]) - self.assertValidPasswordUpdate(user, self.passwords[1]) - self.assertValidPasswordUpdate(user, self.passwords[2]) - self.assertValidPasswordUpdate(user, self.passwords[3]) + passwords = [uuid.uuid4().hex, uuid.uuid4().hex, uuid.uuid4().hex, + uuid.uuid4().hex] + user = self._create_user(passwords[0]) + self.assertValidChangePassword(user['id'], passwords[0], passwords[1]) + self.assertValidChangePassword(user['id'], passwords[1], passwords[2]) + self.assertValidChangePassword(user['id'], passwords[2], passwords[3]) # Now you should be able to change the password to match the initial # password because the password history only contains password elements # 1, 2, 3 - self.assertValidPasswordUpdate(user, self.passwords[0]) + self.assertValidChangePassword(user['id'], passwords[3], passwords[0]) def test_validate_password_history_but_start_with_password_none(self): + passwords = [uuid.uuid4().hex, uuid.uuid4().hex] # Create user and confirm password is None user = self._create_user(None) user_ref = self._get_user_ref(user['id']) self.assertIsNone(user_ref.password) - # Update the password - self.assertValidPasswordUpdate(user, self.passwords[0]) - self.assertValidPasswordUpdate(user, self.passwords[1]) + # Admin password reset + user['password'] = passwords[0] + self.identity_api.update_user(user['id'], user) + # Self-service change password + self.assertValidChangePassword(user['id'], passwords[0], passwords[1]) # Attempt to update with a previous password - user['password'] = self.passwords[0] self.assertRaises(exception.PasswordValidationError, - self.identity_api.update_user, - user['id'], - user) + self.identity_api.change_password, + self.make_request(), + user_id=user['id'], + original_password=passwords[1], + new_password=passwords[0]) - def test_validate_password_history_disabled_and_repeat_same_password(self): + def test_disable_password_history_and_repeat_same_password(self): self.config_fixture.config(group='security_compliance', unique_last_password_count=1) - user = self._create_user(self.passwords[0]) - # Repeatedly update the password with the same password - self.assertValidPasswordUpdate(user, self.passwords[0]) - self.assertValidPasswordUpdate(user, self.passwords[0]) + password = uuid.uuid4().hex + user = self._create_user(password) + # Repeatedly change password with the same password + self.assertValidChangePassword(user['id'], password, password) + self.assertValidChangePassword(user['id'], password, password) + + def test_admin_password_reset_is_not_validated_by_password_history(self): + passwords = [uuid.uuid4().hex, uuid.uuid4().hex] + user = self._create_user(passwords[0]) + # Attempt to change password to a unique password + user['password'] = passwords[1] + self.identity_api.update_user(user['id'], user) + self.identity_api.authenticate(self.make_request(), + user_id=user['id'], + password=passwords[1]) + # Attempt to change password with the same password + user['password'] = passwords[1] + self.identity_api.update_user(user['id'], user) + self.identity_api.authenticate(self.make_request(), + user_id=user['id'], + password=passwords[1]) + # Attempt to change password with the initial password + user['password'] = passwords[0] + self.identity_api.update_user(user['id'], user) + self.identity_api.authenticate(self.make_request(), + user_id=user['id'], + password=passwords[0]) def test_truncate_passwords(self): - user = self._create_user(self.passwords[0]) + user = self._create_user(uuid.uuid4().hex) self._add_passwords_to_history(user, n=4) user_ref = self._get_user_ref(user['id']) self.assertEqual( @@ -245,7 +256,7 @@ class PasswordHistoryValidationTests(test_backend_sql.SqlTests): expected_length = self.max_cnt + 1 self.config_fixture.config(group='security_compliance', unique_last_password_count=self.max_cnt) - user = self._create_user(self.passwords[0]) + user = self._create_user(uuid.uuid4().hex) self._add_passwords_to_history(user, n=4) user_ref = self._get_user_ref(user['id']) self.assertEqual(len(user_ref.local_user.passwords), expected_length) @@ -288,11 +299,13 @@ class PasswordHistoryValidationTests(test_backend_sql.SqlTests): } return self.identity_api.create_user(user) - def assertValidPasswordUpdate(self, user, new_password): - user['password'] = new_password - self.identity_api.update_user(user['id'], user) + def assertValidChangePassword(self, user_id, password, new_password): + self.identity_api.change_password(self.make_request(), + user_id=user_id, + original_password=password, + new_password=new_password) self.identity_api.authenticate(self.make_request(), - user_id=user['id'], + user_id=user_id, password=new_password) def _add_passwords_to_history(self, user, n): |