Module: Negroni::Models::Lockable

Extended by:
ActiveSupport::Concern
Defined in:
lib/negroni/models/lockable.rb

Overview

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 ‘negroni`:

* `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.

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.required_fields(klass) ⇒ Object

Required fields



36
37
38
39
40
41
42
# File 'lib/negroni/models/lockable.rb', line 36

def self.required_fields(klass)
  att = []
  att << :failed_attempts if klass.lock_strategy_enabled? :failed_attempts
  att << :locked_at if klass.unlock_strategy_enabled? :time
  att << :unlock_token if klass.unlock_strategy_enabled? :email
  att
end

Instance Method Details

#access_locked?Boolean

Returns true if the user has access locked

Returns:

  • (Boolean)


70
71
72
# File 'lib/negroni/models/lockable.rb', line 70

def access_locked?
  locked_at && !lock_expired?
end

#active_for_auth?Boolean

Override active_for_auth? for locking purposes

Returns:

  • (Boolean)


90
91
92
# File 'lib/negroni/models/lockable.rb', line 90

def active_for_auth?
  super && !access_locked?
end

#inactive_messageObject

Override for locking purposes



95
96
97
# File 'lib/negroni/models/lockable.rb', line 95

def inactive_message
  access_locked? ? :locked : super
end

#lock_access!(opts = {}) ⇒ Object

Lock a user setting its locked_at to actual time.

Parameters:

  • opts (Hash) (defaults to: {})

    hash of options

Options Hash (opts):

  • :send_instructions (Boolean)

    pass ‘false` to not send the email



49
50
51
52
53
54
55
56
57
58
# File 'lib/negroni/models/lockable.rb', line 49

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

#lock_strategy_enabled?(strategy) ⇒ Boolean

Returns if the passed strategy is enabled.

Returns:

  • (Boolean)

    if the passed strategy is enabled



31
32
33
# File 'lib/negroni/models/lockable.rb', line 31

delegate :lock_strategy_enabled?,
:unlock_strategy_enabled?,
to: 'self.class'

#resend_unlock_instructionsObject

Resend instructions if the user is locked



85
86
87
# File 'lib/negroni/models/lockable.rb', line 85

def resend_unlock_instructions
  if_access_locked { send_unlock_instructions }
end

#send_unlock_instructionsObject

send instructions to unlock



75
76
77
78
79
80
81
82
# File 'lib/negroni/models/lockable.rb', line 75

def send_unlock_instructions
  raw, enc = Negroni.token_generator.generate(self.class, :unlock_token)
  self.unlock_token = enc

  save(validate: false) # rubocop:disable Rails/SaveBang
  send_auth_notification(:unlock_instructions, raw, {})
  raw
end

#unauthenticated_messageObject

Override for locking



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/negroni/models/lockable.rb', line 123

def unauthenticated_message
  if Negroni.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_access!Object

Unlock a user, clearing ‘locked_at` and `failed_attempts`.



61
62
63
64
65
66
# File 'lib/negroni/models/lockable.rb', line 61

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

#valid_for_auth?Boolean

Override for locking purposes

Returns:

  • (Boolean)


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/negroni/models/lockable.rb', line 102

def valid_for_auth?
  return super unless persisted? &&
                      lock_strategy_enabled?(:failed_attempts)

  unlock_access! if lock_expired?

  return true if super && !access_locked?

  self.failed_attempts ||= 0
  self.failed_attempts += 1

  if attempts_exceeded?
    lock_access! unless access_locked?
  else
    save(validate: false)
  end

  false
end