diff --git a/config/locales/en.yml b/config/locales/en.yml index b0e4ed0c..a867ebd4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -11,6 +11,7 @@ en: inactive: "Your account is not activated yet." invalid: "Invalid email or password." locked: "Your account is locked." + last_attempt: "You have one more attempt before your account will be locked'" not_found_in_database: "Invalid email or password." timeout: "Your session expired. Please sign in again to continue." unauthenticated: "You need to sign in or sign up before continuing." diff --git a/lib/devise.rb b/lib/devise.rb index ae5fad7f..99e585df 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -268,6 +268,10 @@ module Devise mattr_accessor :paranoid @@paranoid = false + # When true, warn user if he just used next-to-last attempt of authentication + mattr_accessor :last_attempt_warning + @@last_attempt_warning = false + # Stores the token generator mattr_accessor :token_generator @@token_generator = nil diff --git a/lib/devise/models/lockable.rb b/lib/devise/models/lockable.rb index 49a5312e..e5640c56 100644 --- a/lib/devise/models/lockable.rb +++ b/lib/devise/models/lockable.rb @@ -112,6 +112,8 @@ module Devise # leaks the existence of an account. if Devise.paranoid super + elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? + :last_attempt elsif lock_strategy_enabled?(:failed_attempts) && attempts_exceeded? :locked else @@ -125,6 +127,10 @@ module Devise self.failed_attempts > self.class.maximum_attempts end + def last_attempt? + self.failed_attempts == self.class.maximum_attempts - 1 + end + # Tells if the lock is expired if :time unlock strategy is active def lock_expired? if unlock_strategy_enabled?(:time) diff --git a/test/models/lockable_test.rb b/test/models/lockable_test.rb index 7ac55869..ff29e8da 100644 --- a/test/models/lockable_test.rb +++ b/test/models/lockable_test.rb @@ -279,4 +279,17 @@ class LockableTest < ActiveSupport::TestCase assert_equal :invalid, user.unauthenticated_message end end + + test 'should return last attempt message if user made next-to-last attempt of password entering' do + swap Devise, :last_attempt_warning => :true do + swap Devise, :lock_strategy => :failed_attempts do + user = create_user + user.failed_attempts = Devise.maximum_attempts - 2 + assert_equal :invalid, user.unauthenticated_message + + user.failed_attempts = Devise.maximum_attempts - 1 + assert_equal :last_attempt, user.unauthenticated_message + end + end + end end