From 82383ea7765fcd2c6cf499c7859786350b4c09cf Mon Sep 17 00:00:00 2001 From: Alan Guo Xiang Tan Date: Wed, 5 Jun 2024 15:22:40 +0800 Subject: [PATCH] DEV: Avoid unique validation in `UserPasswordExpirer.expire_user_password` (#27343) This commit updates the `UserPasswordExpirer.expire_user_password` method to update `UserPassword#password_expired_at` when an existing `UserPassword` record exists with the same `password_salt`, `password_hash` and `password_algorithm`. This is to prevent the unique validation error on `UserPassword#user_id` and `UserPassword#password_hash` from being raised when the method is called twice for a user that has not changed its password. --- app/services/user_password_expirer.rb | 16 +++++++------- spec/services/user_password_expirer_spec.rb | 23 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/app/services/user_password_expirer.rb b/app/services/user_password_expirer.rb index 2e294254e65..4a1944932ce 100644 --- a/app/services/user_password_expirer.rb +++ b/app/services/user_password_expirer.rb @@ -2,12 +2,14 @@ class UserPasswordExpirer def self.expire_user_password(user) - UserPassword.create!( - user:, - password_hash: user.password_hash, - password_salt: user.salt, - password_algorithm: user.password_algorithm, - password_expired_at: Time.zone.now, - ) + UserPassword + .where( + user:, + password_hash: user.password_hash, + password_salt: user.salt, + password_algorithm: user.password_algorithm, + ) + .first_or_initialize + .update!(password_expired_at: Time.zone.now) end end diff --git a/spec/services/user_password_expirer_spec.rb b/spec/services/user_password_expirer_spec.rb index 13f6854ad93..9498c853aac 100644 --- a/spec/services/user_password_expirer_spec.rb +++ b/spec/services/user_password_expirer_spec.rb @@ -19,5 +19,28 @@ RSpec.describe UserPasswordExpirer do expect(user_password.password_algorithm).to eq(user.password_algorithm) expect(user_password.password_expired_at).to eq_time(Time.zone.now) end + + it "should update `UserPassword#password_expired_at` if the user already has an existing UserPassword record with the same password hash, salt and algorithm" do + freeze_time(1.hour.ago) do + described_class.expire_user_password(user) + + user_password = user.passwords.first + + expect(user_password.password_expired_at).to eq_time(Time.zone.now) + end + + freeze_time do + described_class.expire_user_password(user) + + expect(user.passwords.count).to eq(1) + + user_password = user.passwords.first + + expect(user_password.password_hash).to eq(user.password_hash) + expect(user_password.password_salt).to eq(user.salt) + expect(user_password.password_algorithm).to eq(user.password_algorithm) + expect(user_password.password_expired_at).to eq_time(Time.zone.now) + end + end end end