module Devise::Models::Lockable
Handles blocking a user access after a certain number of attempts. Lockable accepts two different strategies to unlock a user after it's blocked: email and time. The former will send an email to the user when the lock happens, containing a link to unlock its account. The second will unlock the user automatically after some configured time (ie 2.hours). It's also possible to set up lockable to use both email and time strategies.
Options¶ ↑
Lockable adds the following options to
devise
:
* +maximum_attempts+: how many attempts should be accepted before blocking the user. * +lock_strategy+: lock the user account by :failed_attempts or :none. * +unlock_strategy+: unlock the user account by :time, :email, :both or :none. * +unlock_in+: the time you want to lock the user after to lock happens. Only available when unlock_strategy is :time or :both. * +unlock_keys+: the keys you want to use when locking and unlocking an account
Public Class Methods
# File lib/devise/models/lockable.rb, line 28 def self.required_fields(klass) attributes = [] attributes << :failed_attempts if klass.lock_strategy_enabled?(:failed_attempts) attributes << :locked_at if klass.unlock_strategy_enabled?(:time) attributes << :unlock_token if klass.unlock_strategy_enabled?(:email) attributes end
Public Instance Methods
Verifies whether a user is locked or not.
# File lib/devise/models/lockable.rb, line 60 def access_locked? !!locked_at && !lock_expired? end
Overwrites active_for_authentication? from Devise::Models::Activatable for locking purposes by verifying whether a user is active to sign in or not based on locked?
# File lib/devise/models/lockable.rb, line 80 def active_for_authentication? super && !access_locked? end
Overwrites invalid_message from Devise::Models::Authenticatable to define the correct reason for blocking the sign in.
# File lib/devise/models/lockable.rb, line 86 def inactive_message access_locked? ? :locked : super end
# File lib/devise/models/lockable.rb, line 113 def increment_failed_attempts self.failed_attempts ||= 0 self.failed_attempts += 1 end
Lock a user setting its locked_at to actual time.
-
opts
: Hash options if you don't want to send email when you lock access, you could pass the next hash `{ send_instructions: false } as option`.
# File lib/devise/models/lockable.rb, line 41 def lock_access!(opts = { }) self.locked_at = Time.now.utc if unlock_strategy_enabled?(:email) && opts.fetch(:send_instructions, true) send_unlock_instructions else save(validate: false) end end
Resend the unlock instructions if the user is locked.
# File lib/devise/models/lockable.rb, line 74 def resend_unlock_instructions if_access_locked { send_unlock_instructions } end
Send unlock instructions by email
# File lib/devise/models/lockable.rb, line 65 def send_unlock_instructions raw, enc = Devise.token_generator.generate(self.class, :unlock_token) self.unlock_token = enc save(validate: false) send_devise_notification(:unlock_instructions, raw, {}) raw end
# File lib/devise/models/lockable.rb, line 118 def unauthenticated_message # If set to paranoid mode, do not show the locked message because it # leaks the existence of an account. if Devise.paranoid super elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?) :locked elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? && self.class.last_attempt_warning :last_attempt else super end end
Unlock a user by cleaning locked_at and failed_attempts.
# File lib/devise/models/lockable.rb, line 52 def unlock_access! self.locked_at = nil self.failed_attempts = 0 if respond_to?(:failed_attempts=) self.unlock_token = nil if respond_to?(:unlock_token=) save(validate: false) end
Overwrites valid_for_authentication? from Devise::Models::Authenticatable for verifying whether a user is allowed to sign in or not. If the user is locked, it should never be allowed.
# File lib/devise/models/lockable.rb, line 93 def valid_for_authentication? return super unless persisted? && lock_strategy_enabled?(:failed_attempts) # Unlock the user if the lock is expired, no matter # if the user can login or not (wrong password, etc) unlock_access! if lock_expired? if super && !access_locked? true else increment_failed_attempts if attempts_exceeded? lock_access! unless access_locked? else save(validate: false) end false end end
Protected Instance Methods
# File lib/devise/models/lockable.rb, line 134 def attempts_exceeded? self.failed_attempts >= self.class.maximum_attempts end
Checks whether the record is locked or not, yielding to the block if it's locked, otherwise adds an error to email.
# File lib/devise/models/lockable.rb, line 153 def if_access_locked if access_locked? yield else self.errors.add(Devise.unlock_keys.first, :not_locked) false end end
# File lib/devise/models/lockable.rb, line 138 def last_attempt? self.failed_attempts == self.class.maximum_attempts - 1 end
Tells if the lock is expired if :time unlock strategy is active
# File lib/devise/models/lockable.rb, line 143 def lock_expired? if unlock_strategy_enabled?(:time) locked_at && locked_at < self.class.unlock_in.ago else false end end