From 28a4cf86b54a325b2d3bf3b15bdf34c3cdd36aa2 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 22 Mar 2021 18:26:17 -0300 Subject: [PATCH] Create a model hook around the lockable warden hook to reset attempts Resetting failed attempts after sign in happened inside a warden hook specific for the lockable module, but that was hidden inside the hook implementation and didn't allow any user customization. One such customization needed for example is to direct these updates to a write DB when using a multi-DB setup. With the logic hidden in the warden hook this wasn't possible, now that it's exposed in a model method much like trackable, we can override the model method to wrap it in a connection switch block for example, point to a write DB, and simply call `super`. Closes #5310 Related to #5264 and #5133 --- lib/devise/hooks/lockable.rb | 7 ++----- lib/devise/models/lockable.rb | 10 +++++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/devise/hooks/lockable.rb b/lib/devise/hooks/lockable.rb index a73a1752e2..b11db1e879 100644 --- a/lib/devise/hooks/lockable.rb +++ b/lib/devise/hooks/lockable.rb @@ -3,10 +3,7 @@ # After each sign in, if resource responds to failed_attempts, sets it to 0 # This is only triggered when the user is explicitly set (with set_user) Warden::Manager.after_set_user except: :fetch do |record, warden, options| - if record.respond_to?(:failed_attempts) && warden.authenticated?(options[:scope]) - unless record.failed_attempts.to_i.zero? - record.failed_attempts = 0 - record.save(validate: false) - end + if record.respond_to?(:reset_failed_attempts!) && warden.authenticated?(options[:scope]) + record.reset_failed_attempts! end end diff --git a/lib/devise/models/lockable.rb b/lib/devise/models/lockable.rb index 578f52949d..ce9e3e57af 100644 --- a/lib/devise/models/lockable.rb +++ b/lib/devise/models/lockable.rb @@ -57,6 +57,14 @@ def unlock_access! save(validate: false) end + # Resets failed attempts counter to 0. + def reset_failed_attempts! + if respond_to?(:failed_attempts) && !failed_attempts.to_i.zero? + self.failed_attempts = 0 + save(validate: false) + end + end + # Verifies whether a user is locked or not. def access_locked? !!locked_at && !lock_expired? @@ -110,7 +118,7 @@ def valid_for_authentication? false end end - + def increment_failed_attempts self.class.increment_counter(:failed_attempts, id) reload