ultra-max-kit 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/devise-5.0.4/CHANGELOG.md +88 -0
- data/devise-5.0.4/MIT-LICENSE +21 -0
- data/devise-5.0.4/README.md +776 -0
- data/devise-5.0.4/app/controllers/devise/confirmations_controller.rb +54 -0
- data/devise-5.0.4/app/controllers/devise/omniauth_callbacks_controller.rb +36 -0
- data/devise-5.0.4/app/controllers/devise/passwords_controller.rb +88 -0
- data/devise-5.0.4/app/controllers/devise/registrations_controller.rb +169 -0
- data/devise-5.0.4/app/controllers/devise/sessions_controller.rb +83 -0
- data/devise-5.0.4/app/controllers/devise/unlocks_controller.rb +52 -0
- data/devise-5.0.4/app/controllers/devise_controller.rb +226 -0
- data/devise-5.0.4/app/helpers/devise_helper.rb +5 -0
- data/devise-5.0.4/app/mailers/devise/mailer.rb +30 -0
- data/devise-5.0.4/app/views/devise/confirmations/new.html.erb +16 -0
- data/devise-5.0.4/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/devise-5.0.4/app/views/devise/mailer/email_changed.html.erb +7 -0
- data/devise-5.0.4/app/views/devise/mailer/password_change.html.erb +3 -0
- data/devise-5.0.4/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/devise-5.0.4/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/devise-5.0.4/app/views/devise/passwords/edit.html.erb +25 -0
- data/devise-5.0.4/app/views/devise/passwords/new.html.erb +16 -0
- data/devise-5.0.4/app/views/devise/registrations/edit.html.erb +42 -0
- data/devise-5.0.4/app/views/devise/registrations/new.html.erb +29 -0
- data/devise-5.0.4/app/views/devise/sessions/new.html.erb +26 -0
- data/devise-5.0.4/app/views/devise/shared/_error_messages.html.erb +15 -0
- data/devise-5.0.4/app/views/devise/shared/_links.html.erb +25 -0
- data/devise-5.0.4/app/views/devise/unlocks/new.html.erb +16 -0
- data/devise-5.0.4/config/locales/en.yml +65 -0
- data/devise-5.0.4/lib/devise/controllers/helpers.rb +295 -0
- data/devise-5.0.4/lib/devise/controllers/rememberable.rb +56 -0
- data/devise-5.0.4/lib/devise/controllers/responder.rb +35 -0
- data/devise-5.0.4/lib/devise/controllers/scoped_views.rb +19 -0
- data/devise-5.0.4/lib/devise/controllers/sign_in_out.rb +106 -0
- data/devise-5.0.4/lib/devise/controllers/store_location.rb +76 -0
- data/devise-5.0.4/lib/devise/controllers/url_helpers.rb +69 -0
- data/devise-5.0.4/lib/devise/delegator.rb +18 -0
- data/devise-5.0.4/lib/devise/encryptor.rb +24 -0
- data/devise-5.0.4/lib/devise/failure_app.rb +287 -0
- data/devise-5.0.4/lib/devise/hooks/activatable.rb +12 -0
- data/devise-5.0.4/lib/devise/hooks/csrf_cleaner.rb +14 -0
- data/devise-5.0.4/lib/devise/hooks/forgetable.rb +11 -0
- data/devise-5.0.4/lib/devise/hooks/lockable.rb +9 -0
- data/devise-5.0.4/lib/devise/hooks/proxy.rb +23 -0
- data/devise-5.0.4/lib/devise/hooks/rememberable.rb +9 -0
- data/devise-5.0.4/lib/devise/hooks/timeoutable.rb +35 -0
- data/devise-5.0.4/lib/devise/hooks/trackable.rb +11 -0
- data/devise-5.0.4/lib/devise/mailers/helpers.rb +87 -0
- data/devise-5.0.4/lib/devise/mapping.rb +178 -0
- data/devise-5.0.4/lib/devise/models/authenticatable.rb +302 -0
- data/devise-5.0.4/lib/devise/models/confirmable.rb +371 -0
- data/devise-5.0.4/lib/devise/models/database_authenticatable.rb +204 -0
- data/devise-5.0.4/lib/devise/models/lockable.rb +214 -0
- data/devise-5.0.4/lib/devise/models/omniauthable.rb +29 -0
- data/devise-5.0.4/lib/devise/models/recoverable.rb +156 -0
- data/devise-5.0.4/lib/devise/models/registerable.rb +29 -0
- data/devise-5.0.4/lib/devise/models/rememberable.rb +158 -0
- data/devise-5.0.4/lib/devise/models/timeoutable.rb +45 -0
- data/devise-5.0.4/lib/devise/models/trackable.rb +51 -0
- data/devise-5.0.4/lib/devise/models/validatable.rb +70 -0
- data/devise-5.0.4/lib/devise/models.rb +122 -0
- data/devise-5.0.4/lib/devise/modules.rb +30 -0
- data/devise-5.0.4/lib/devise/omniauth/config.rb +47 -0
- data/devise-5.0.4/lib/devise/omniauth/url_helpers.rb +27 -0
- data/devise-5.0.4/lib/devise/omniauth.rb +27 -0
- data/devise-5.0.4/lib/devise/orm/active_record.rb +7 -0
- data/devise-5.0.4/lib/devise/orm/mongoid.rb +7 -0
- data/devise-5.0.4/lib/devise/orm.rb +80 -0
- data/devise-5.0.4/lib/devise/parameter_filter.rb +44 -0
- data/devise-5.0.4/lib/devise/parameter_sanitizer.rb +172 -0
- data/devise-5.0.4/lib/devise/rails/routes.rb +514 -0
- data/devise-5.0.4/lib/devise/rails/warden_compat.rb +15 -0
- data/devise-5.0.4/lib/devise/rails.rb +57 -0
- data/devise-5.0.4/lib/devise/strategies/authenticatable.rb +178 -0
- data/devise-5.0.4/lib/devise/strategies/base.rb +22 -0
- data/devise-5.0.4/lib/devise/strategies/database_authenticatable.rb +31 -0
- data/devise-5.0.4/lib/devise/strategies/rememberable.rb +67 -0
- data/devise-5.0.4/lib/devise/test/controller_helpers.rb +156 -0
- data/devise-5.0.4/lib/devise/test/integration_helpers.rb +63 -0
- data/devise-5.0.4/lib/devise/time_inflector.rb +16 -0
- data/devise-5.0.4/lib/devise/token_generator.rb +32 -0
- data/devise-5.0.4/lib/devise/version.rb +5 -0
- data/devise-5.0.4/lib/devise.rb +533 -0
- data/devise-5.0.4/lib/generators/active_record/devise_generator.rb +119 -0
- data/devise-5.0.4/lib/generators/active_record/templates/migration.rb +20 -0
- data/devise-5.0.4/lib/generators/active_record/templates/migration_existing.rb +27 -0
- data/devise-5.0.4/lib/generators/devise/controllers_generator.rb +46 -0
- data/devise-5.0.4/lib/generators/devise/devise_generator.rb +28 -0
- data/devise-5.0.4/lib/generators/devise/install_generator.rb +42 -0
- data/devise-5.0.4/lib/generators/devise/orm_helpers.rb +36 -0
- data/devise-5.0.4/lib/generators/devise/views_generator.rb +145 -0
- data/devise-5.0.4/lib/generators/mongoid/devise_generator.rb +57 -0
- data/devise-5.0.4/lib/generators/templates/README +36 -0
- data/devise-5.0.4/lib/generators/templates/controllers/README +14 -0
- data/devise-5.0.4/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
- data/devise-5.0.4/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
- data/devise-5.0.4/lib/generators/templates/controllers/passwords_controller.rb +34 -0
- data/devise-5.0.4/lib/generators/templates/controllers/registrations_controller.rb +62 -0
- data/devise-5.0.4/lib/generators/templates/controllers/sessions_controller.rb +27 -0
- data/devise-5.0.4/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
- data/devise-5.0.4/lib/generators/templates/devise.rb +316 -0
- data/devise-5.0.4/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
- data/devise-5.0.4/lib/generators/templates/markerb/email_changed.markerb +7 -0
- data/devise-5.0.4/lib/generators/templates/markerb/password_change.markerb +3 -0
- data/devise-5.0.4/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
- data/devise-5.0.4/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
- data/devise-5.0.4/lib/generators/templates/simple_form_for/confirmations/new.html.erb +20 -0
- data/devise-5.0.4/lib/generators/templates/simple_form_for/passwords/edit.html.erb +27 -0
- data/devise-5.0.4/lib/generators/templates/simple_form_for/passwords/new.html.erb +18 -0
- data/devise-5.0.4/lib/generators/templates/simple_form_for/registrations/edit.html.erb +35 -0
- data/devise-5.0.4/lib/generators/templates/simple_form_for/registrations/new.html.erb +25 -0
- data/devise-5.0.4/lib/generators/templates/simple_form_for/sessions/new.html.erb +20 -0
- data/devise-5.0.4/lib/generators/templates/simple_form_for/unlocks/new.html.erb +19 -0
- data/ultra-max-kit.gemspec +12 -0
- metadata +153 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "devise/hooks/lockable"
|
|
4
|
+
|
|
5
|
+
module Devise
|
|
6
|
+
module Models
|
|
7
|
+
# Handles blocking a user access after a certain number of attempts.
|
|
8
|
+
# Lockable accepts two different strategies to unlock a user after it's
|
|
9
|
+
# blocked: email and time. The former will send an email to the user when
|
|
10
|
+
# the lock happens, containing a link to unlock its account. The second
|
|
11
|
+
# will unlock the user automatically after some configured time (ie 2.hours).
|
|
12
|
+
# It's also possible to set up lockable to use both email and time strategies.
|
|
13
|
+
#
|
|
14
|
+
# == Options
|
|
15
|
+
#
|
|
16
|
+
# Lockable adds the following options to +devise+:
|
|
17
|
+
#
|
|
18
|
+
# * +maximum_attempts+: how many attempts should be accepted before blocking the user.
|
|
19
|
+
# * +lock_strategy+: lock the user account by :failed_attempts or :none.
|
|
20
|
+
# * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
|
|
21
|
+
# * +unlock_in+: the time you want to unlock the user after lock happens. Only available when unlock_strategy is :time or :both.
|
|
22
|
+
# * +unlock_keys+: the keys you want to use when locking and unlocking an account
|
|
23
|
+
#
|
|
24
|
+
module Lockable
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
|
|
27
|
+
delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, to: "self.class"
|
|
28
|
+
|
|
29
|
+
def self.required_fields(klass)
|
|
30
|
+
attributes = []
|
|
31
|
+
attributes << :failed_attempts if klass.lock_strategy_enabled?(:failed_attempts)
|
|
32
|
+
attributes << :locked_at if klass.unlock_strategy_enabled?(:time)
|
|
33
|
+
attributes << :unlock_token if klass.unlock_strategy_enabled?(:email)
|
|
34
|
+
|
|
35
|
+
attributes
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Lock a user setting its locked_at to actual time.
|
|
39
|
+
# * +opts+: Hash options if you don't want to send email
|
|
40
|
+
# when you lock access, you could pass the next hash
|
|
41
|
+
# `{ send_instructions: false } as option`.
|
|
42
|
+
def lock_access!(opts = { })
|
|
43
|
+
self.locked_at = Time.now.utc
|
|
44
|
+
|
|
45
|
+
if unlock_strategy_enabled?(:email) && opts.fetch(:send_instructions, true)
|
|
46
|
+
send_unlock_instructions
|
|
47
|
+
else
|
|
48
|
+
save(validate: false)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Unlock a user by cleaning locked_at and failed_attempts.
|
|
53
|
+
def unlock_access!
|
|
54
|
+
self.locked_at = nil
|
|
55
|
+
self.failed_attempts = 0 if respond_to?(:failed_attempts=)
|
|
56
|
+
self.unlock_token = nil if respond_to?(:unlock_token=)
|
|
57
|
+
save(validate: false)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Resets failed attempts counter to 0.
|
|
61
|
+
def reset_failed_attempts!
|
|
62
|
+
if respond_to?(:failed_attempts) && !failed_attempts.to_i.zero?
|
|
63
|
+
self.failed_attempts = 0
|
|
64
|
+
save(validate: false)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Verifies whether a user is locked or not.
|
|
69
|
+
def access_locked?
|
|
70
|
+
!!locked_at && !lock_expired?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Send unlock instructions by email
|
|
74
|
+
def send_unlock_instructions
|
|
75
|
+
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
|
|
76
|
+
self.unlock_token = enc
|
|
77
|
+
save(validate: false)
|
|
78
|
+
send_devise_notification(:unlock_instructions, raw, {})
|
|
79
|
+
raw
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Resend the unlock instructions if the user is locked.
|
|
83
|
+
def resend_unlock_instructions
|
|
84
|
+
if_access_locked { send_unlock_instructions }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Overwrites active_for_authentication? from Devise::Models::Authenticatable for locking purposes
|
|
88
|
+
# by verifying whether a user is active to sign in or not based on locked?
|
|
89
|
+
def active_for_authentication?
|
|
90
|
+
super && !access_locked?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Overwrites invalid_message from Devise::Models::Authenticatable to define
|
|
94
|
+
# the correct reason for blocking the sign in.
|
|
95
|
+
def inactive_message
|
|
96
|
+
access_locked? ? :locked : super
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Overwrites valid_for_authentication? from Devise::Models::Authenticatable
|
|
100
|
+
# for verifying whether a user is allowed to sign in or not. If the user
|
|
101
|
+
# is locked, it should never be allowed.
|
|
102
|
+
def valid_for_authentication?
|
|
103
|
+
return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
|
|
104
|
+
|
|
105
|
+
# Unlock the user if the lock is expired, no matter
|
|
106
|
+
# if the user can login or not (wrong password, etc)
|
|
107
|
+
unlock_access! if lock_expired?
|
|
108
|
+
|
|
109
|
+
if super && !access_locked?
|
|
110
|
+
true
|
|
111
|
+
else
|
|
112
|
+
increment_failed_attempts
|
|
113
|
+
if attempts_exceeded?
|
|
114
|
+
lock_access! unless access_locked?
|
|
115
|
+
else
|
|
116
|
+
save(validate: false)
|
|
117
|
+
end
|
|
118
|
+
false
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def increment_failed_attempts
|
|
123
|
+
self.class.increment_counter(:failed_attempts, id)
|
|
124
|
+
reload
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def unauthenticated_message
|
|
128
|
+
# If set to paranoid mode, do not show the locked message because it
|
|
129
|
+
# leaks the existence of an account.
|
|
130
|
+
if Devise.paranoid
|
|
131
|
+
super
|
|
132
|
+
elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?)
|
|
133
|
+
:locked
|
|
134
|
+
elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? && self.class.last_attempt_warning
|
|
135
|
+
:last_attempt
|
|
136
|
+
else
|
|
137
|
+
super
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
protected
|
|
142
|
+
|
|
143
|
+
def attempts_exceeded?
|
|
144
|
+
self.failed_attempts >= self.class.maximum_attempts
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def last_attempt?
|
|
148
|
+
self.failed_attempts == self.class.maximum_attempts - 1
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Tells if the lock is expired if :time unlock strategy is active
|
|
152
|
+
def lock_expired?
|
|
153
|
+
if unlock_strategy_enabled?(:time)
|
|
154
|
+
locked_at && locked_at < self.class.unlock_in.ago
|
|
155
|
+
else
|
|
156
|
+
false
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Checks whether the record is locked or not, yielding to the block
|
|
161
|
+
# if it's locked, otherwise adds an error to email.
|
|
162
|
+
def if_access_locked
|
|
163
|
+
if access_locked?
|
|
164
|
+
yield
|
|
165
|
+
else
|
|
166
|
+
self.errors.add(Devise.unlock_keys.first, :not_locked)
|
|
167
|
+
false
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
module ClassMethods
|
|
172
|
+
# List of strategies that are enabled/supported if :both is used.
|
|
173
|
+
BOTH_STRATEGIES = [:time, :email]
|
|
174
|
+
|
|
175
|
+
# Attempt to find a user by its unlock keys. If a record is found, send new
|
|
176
|
+
# unlock instructions to it. If not user is found, returns a new user
|
|
177
|
+
# with an email not found error.
|
|
178
|
+
# Options must contain the user's unlock keys
|
|
179
|
+
def send_unlock_instructions(attributes = {})
|
|
180
|
+
lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
|
|
181
|
+
lockable.resend_unlock_instructions if lockable.persisted?
|
|
182
|
+
lockable
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Find a user by its unlock token and try to unlock it.
|
|
186
|
+
# If no user is found, returns a new user with an error.
|
|
187
|
+
# If the user is not locked, creates an error for the user
|
|
188
|
+
# Options must have the unlock_token
|
|
189
|
+
def unlock_access_by_token(unlock_token)
|
|
190
|
+
original_token = unlock_token
|
|
191
|
+
unlock_token = Devise.token_generator.digest(self, :unlock_token, unlock_token)
|
|
192
|
+
|
|
193
|
+
lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
|
|
194
|
+
lockable.unlock_access! if lockable.persisted?
|
|
195
|
+
lockable.unlock_token = original_token
|
|
196
|
+
lockable
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Is the unlock enabled for the given unlock strategy?
|
|
200
|
+
def unlock_strategy_enabled?(strategy)
|
|
201
|
+
self.unlock_strategy == strategy ||
|
|
202
|
+
(self.unlock_strategy == :both && BOTH_STRATEGIES.include?(strategy))
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Is the lock enabled for the given lock strategy?
|
|
206
|
+
def lock_strategy_enabled?(strategy)
|
|
207
|
+
self.lock_strategy == strategy
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys, :last_attempt_warning)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'devise/omniauth'
|
|
4
|
+
|
|
5
|
+
module Devise
|
|
6
|
+
module Models
|
|
7
|
+
# Adds OmniAuth support to your model.
|
|
8
|
+
#
|
|
9
|
+
# == Options
|
|
10
|
+
#
|
|
11
|
+
# Oauthable adds the following options to +devise+:
|
|
12
|
+
#
|
|
13
|
+
# * +omniauth_providers+: Which providers are available to this model. It expects an array:
|
|
14
|
+
#
|
|
15
|
+
# devise :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
|
|
16
|
+
#
|
|
17
|
+
module Omniauthable
|
|
18
|
+
extend ActiveSupport::Concern
|
|
19
|
+
|
|
20
|
+
def self.required_fields(klass)
|
|
21
|
+
[]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module ClassMethods
|
|
25
|
+
Devise::Models.config(self, :omniauth_providers)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Models
|
|
5
|
+
|
|
6
|
+
# Recoverable takes care of resetting the user password and send reset instructions.
|
|
7
|
+
#
|
|
8
|
+
# ==Options
|
|
9
|
+
#
|
|
10
|
+
# Recoverable adds the following options to +devise+:
|
|
11
|
+
#
|
|
12
|
+
# * +reset_password_keys+: the keys you want to use when recovering the password for an account
|
|
13
|
+
# * +reset_password_within+: the time period within which the password must be reset or the token expires.
|
|
14
|
+
# * +sign_in_after_reset_password+: whether or not to sign in the user automatically after a password reset.
|
|
15
|
+
#
|
|
16
|
+
# == Examples
|
|
17
|
+
#
|
|
18
|
+
# # resets the user password and save the record, true if valid passwords are given, otherwise false
|
|
19
|
+
# User.find(1).reset_password('password123', 'password123')
|
|
20
|
+
#
|
|
21
|
+
# # creates a new token and send it with instructions about how to reset the password
|
|
22
|
+
# User.find(1).send_reset_password_instructions
|
|
23
|
+
#
|
|
24
|
+
module Recoverable
|
|
25
|
+
extend ActiveSupport::Concern
|
|
26
|
+
|
|
27
|
+
def self.required_fields(klass)
|
|
28
|
+
[:reset_password_sent_at, :reset_password_token]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
included do
|
|
32
|
+
before_update :clear_reset_password_token, if: :clear_reset_password_token?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Update password saving the record and clearing token. Returns true if
|
|
36
|
+
# the passwords are valid and the record was saved, false otherwise.
|
|
37
|
+
def reset_password(new_password, new_password_confirmation)
|
|
38
|
+
if new_password.present?
|
|
39
|
+
self.password = new_password
|
|
40
|
+
self.password_confirmation = new_password_confirmation
|
|
41
|
+
save
|
|
42
|
+
else
|
|
43
|
+
errors.add(:password, :blank)
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Resets reset password token and send reset password instructions by email.
|
|
49
|
+
# Returns the token sent in the e-mail.
|
|
50
|
+
def send_reset_password_instructions
|
|
51
|
+
token = set_reset_password_token
|
|
52
|
+
send_reset_password_instructions_notification(token)
|
|
53
|
+
|
|
54
|
+
token
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Checks if the reset password token sent is within the limit time.
|
|
58
|
+
# We do this by calculating if the difference between today and the
|
|
59
|
+
# sending date does not exceed the confirm in time configured.
|
|
60
|
+
# Returns true if the resource is not responding to reset_password_sent_at at all.
|
|
61
|
+
# reset_password_within is a model configuration, must always be an integer value.
|
|
62
|
+
#
|
|
63
|
+
# Example:
|
|
64
|
+
#
|
|
65
|
+
# # reset_password_within = 1.day and reset_password_sent_at = today
|
|
66
|
+
# reset_password_period_valid? # returns true
|
|
67
|
+
#
|
|
68
|
+
# # reset_password_within = 5.days and reset_password_sent_at = 4.days.ago
|
|
69
|
+
# reset_password_period_valid? # returns true
|
|
70
|
+
#
|
|
71
|
+
# # reset_password_within = 5.days and reset_password_sent_at = 5.days.ago
|
|
72
|
+
# reset_password_period_valid? # returns false
|
|
73
|
+
#
|
|
74
|
+
# # reset_password_within = 0.days
|
|
75
|
+
# reset_password_period_valid? # will always return false
|
|
76
|
+
#
|
|
77
|
+
def reset_password_period_valid?
|
|
78
|
+
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago.utc
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
protected
|
|
82
|
+
|
|
83
|
+
# Removes reset_password token
|
|
84
|
+
def clear_reset_password_token
|
|
85
|
+
self.reset_password_token = nil
|
|
86
|
+
self.reset_password_sent_at = nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def set_reset_password_token
|
|
90
|
+
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
|
|
91
|
+
|
|
92
|
+
self.reset_password_token = enc
|
|
93
|
+
self.reset_password_sent_at = Time.now.utc
|
|
94
|
+
save(validate: false)
|
|
95
|
+
raw
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def send_reset_password_instructions_notification(token)
|
|
99
|
+
send_devise_notification(:reset_password_instructions, token, {})
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def clear_reset_password_token?
|
|
103
|
+
encrypted_password_changed = devise_respond_to_and_will_save_change_to_attribute?(:encrypted_password)
|
|
104
|
+
authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
|
|
105
|
+
devise_respond_to_and_will_save_change_to_attribute?(attribute)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
authentication_keys_changed || encrypted_password_changed
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
module ClassMethods
|
|
112
|
+
# Attempt to find a user by password reset token. If a user is found, return it
|
|
113
|
+
# If a user is not found, return nil
|
|
114
|
+
def with_reset_password_token(token)
|
|
115
|
+
reset_password_token = Devise.token_generator.digest(self, :reset_password_token, token)
|
|
116
|
+
to_adapter.find_first(reset_password_token: reset_password_token)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Attempt to find a user by its email. If a record is found, send new
|
|
120
|
+
# password instructions to it. If user is not found, returns a new user
|
|
121
|
+
# with an email not found error.
|
|
122
|
+
# Attributes must contain the user's email
|
|
123
|
+
def send_reset_password_instructions(attributes = {})
|
|
124
|
+
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
|
|
125
|
+
recoverable.send_reset_password_instructions if recoverable.persisted?
|
|
126
|
+
recoverable
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Attempt to find a user by its reset_password_token to reset its
|
|
130
|
+
# password. If a user is found and token is still valid, reset its password and automatically
|
|
131
|
+
# try saving the record. If not user is found, returns a new user
|
|
132
|
+
# containing an error in reset_password_token attribute.
|
|
133
|
+
# Attributes must contain reset_password_token, password and confirmation
|
|
134
|
+
def reset_password_by_token(attributes = {})
|
|
135
|
+
original_token = attributes[:reset_password_token]
|
|
136
|
+
reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
|
|
137
|
+
|
|
138
|
+
recoverable = find_or_initialize_with_error_by(:reset_password_token, reset_password_token)
|
|
139
|
+
|
|
140
|
+
if recoverable.persisted?
|
|
141
|
+
if recoverable.reset_password_period_valid?
|
|
142
|
+
recoverable.reset_password(attributes[:password], attributes[:password_confirmation])
|
|
143
|
+
else
|
|
144
|
+
recoverable.errors.add(:reset_password_token, :expired)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
recoverable.reset_password_token = original_token if recoverable.reset_password_token.present?
|
|
149
|
+
recoverable
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
Devise::Models.config(self, :reset_password_keys, :reset_password_within, :sign_in_after_reset_password)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Devise
|
|
4
|
+
module Models
|
|
5
|
+
# Registerable is responsible for everything related to registering a new
|
|
6
|
+
# resource (ie user sign up).
|
|
7
|
+
module Registerable
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
def self.required_fields(klass)
|
|
11
|
+
[]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods
|
|
15
|
+
# A convenience method that receives both parameters and session to
|
|
16
|
+
# initialize a user. This can be used by OAuth, for example, to send
|
|
17
|
+
# in the user token and be stored on initialization.
|
|
18
|
+
#
|
|
19
|
+
# By default discards all information sent by the session by calling
|
|
20
|
+
# new with params.
|
|
21
|
+
def new_with_session(params, session)
|
|
22
|
+
new(params)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Devise::Models.config(self, :sign_in_after_change_password)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'devise/strategies/rememberable'
|
|
4
|
+
require 'devise/hooks/rememberable'
|
|
5
|
+
require 'devise/hooks/forgetable'
|
|
6
|
+
|
|
7
|
+
module Devise
|
|
8
|
+
module Models
|
|
9
|
+
# Rememberable manages generating and clearing token for remembering the user
|
|
10
|
+
# from a saved cookie. Rememberable also has utility methods for dealing
|
|
11
|
+
# with serializing the user into the cookie and back from the cookie, trying
|
|
12
|
+
# to lookup the record based on the saved information.
|
|
13
|
+
# You probably wouldn't use rememberable methods directly, they are used
|
|
14
|
+
# mostly internally for handling the remember token.
|
|
15
|
+
#
|
|
16
|
+
# == Options
|
|
17
|
+
#
|
|
18
|
+
# Rememberable adds the following options to +devise+:
|
|
19
|
+
#
|
|
20
|
+
# * +remember_for+: the time you want the user will be remembered without
|
|
21
|
+
# asking for credentials. After this time the user will be blocked and
|
|
22
|
+
# will have to enter their credentials again. This configuration is also
|
|
23
|
+
# used to calculate the expires time for the cookie created to remember
|
|
24
|
+
# the user. By default remember_for is 2.weeks.
|
|
25
|
+
#
|
|
26
|
+
# * +extend_remember_period+: if true, extends the user's remember period
|
|
27
|
+
# when remembered via cookie. False by default.
|
|
28
|
+
#
|
|
29
|
+
# * +rememberable_options+: configuration options passed to the created cookie.
|
|
30
|
+
#
|
|
31
|
+
# == Examples
|
|
32
|
+
#
|
|
33
|
+
# User.find(1).remember_me! # regenerating the token
|
|
34
|
+
# User.find(1).forget_me! # clearing the token
|
|
35
|
+
#
|
|
36
|
+
# # generating info to put into cookies
|
|
37
|
+
# User.serialize_into_cookie(user)
|
|
38
|
+
#
|
|
39
|
+
# # lookup the user based on the incoming cookie information
|
|
40
|
+
# User.serialize_from_cookie(cookie_string)
|
|
41
|
+
module Rememberable
|
|
42
|
+
extend ActiveSupport::Concern
|
|
43
|
+
|
|
44
|
+
attr_accessor :remember_me
|
|
45
|
+
|
|
46
|
+
def self.required_fields(klass)
|
|
47
|
+
[:remember_created_at]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def remember_me!
|
|
51
|
+
self.remember_token ||= self.class.remember_token if respond_to?(:remember_token)
|
|
52
|
+
self.remember_created_at ||= Time.now.utc
|
|
53
|
+
save(validate: false) if self.changed?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# If the record is persisted, remove the remember token (but only if
|
|
57
|
+
# it exists), and save the record without validations.
|
|
58
|
+
def forget_me!
|
|
59
|
+
return unless persisted?
|
|
60
|
+
self.remember_token = nil if respond_to?(:remember_token)
|
|
61
|
+
self.remember_created_at = nil if self.class.expire_all_remember_me_on_sign_out
|
|
62
|
+
save(validate: false)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def remember_expires_at
|
|
66
|
+
self.class.remember_for.from_now
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def extend_remember_period
|
|
70
|
+
self.class.extend_remember_period
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def rememberable_value
|
|
74
|
+
if respond_to?(:remember_token)
|
|
75
|
+
remember_token
|
|
76
|
+
elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
|
|
77
|
+
salt
|
|
78
|
+
else
|
|
79
|
+
raise "authenticatable_salt returned nil for the #{self.class.name} model. " \
|
|
80
|
+
"In order to use rememberable, you must ensure a password is always set " \
|
|
81
|
+
"or have a remember_token column in your model or implement your own " \
|
|
82
|
+
"rememberable_value in the model with custom logic."
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def rememberable_options
|
|
87
|
+
self.class.rememberable_options
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# A callback initiated after successfully being remembered. This can be
|
|
91
|
+
# used to insert your own logic that is only run after the user is
|
|
92
|
+
# remembered.
|
|
93
|
+
#
|
|
94
|
+
# Example:
|
|
95
|
+
#
|
|
96
|
+
# def after_remembered
|
|
97
|
+
# self.update_attribute(:invite_code, nil)
|
|
98
|
+
# end
|
|
99
|
+
#
|
|
100
|
+
def after_remembered
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def remember_me?(token, generated_at)
|
|
104
|
+
# TODO: Normalize the JSON type coercion along with the Timeoutable hook
|
|
105
|
+
# in a single place https://github.com/heartcombo/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
|
|
106
|
+
if generated_at.is_a?(String)
|
|
107
|
+
generated_at = time_from_json(generated_at)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# The token is only valid if:
|
|
111
|
+
# 1. we have a date
|
|
112
|
+
# 2. the current time does not pass the expiry period
|
|
113
|
+
# 3. the record has a remember_created_at date
|
|
114
|
+
# 4. the token date is bigger than the remember_created_at
|
|
115
|
+
# 5. the token matches
|
|
116
|
+
generated_at.is_a?(Time) &&
|
|
117
|
+
(self.class.remember_for.ago < generated_at) &&
|
|
118
|
+
(generated_at > (remember_created_at || Time.now).utc) &&
|
|
119
|
+
Devise.secure_compare(rememberable_value, token)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def time_from_json(value)
|
|
125
|
+
if value =~ /\A\d+\.\d+\Z/
|
|
126
|
+
Time.at(value.to_f)
|
|
127
|
+
else
|
|
128
|
+
Time.parse(value) rescue nil
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
module ClassMethods
|
|
133
|
+
# Create the cookie key using the record id and remember_token
|
|
134
|
+
def serialize_into_cookie(record)
|
|
135
|
+
[record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Recreate the user based on the stored cookie
|
|
139
|
+
def serialize_from_cookie(*args)
|
|
140
|
+
id, token, generated_at = *args
|
|
141
|
+
|
|
142
|
+
record = to_adapter.get(id)
|
|
143
|
+
record if record && record.remember_me?(token, generated_at)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Generate a token checking if one does not already exist in the database.
|
|
147
|
+
def remember_token #:nodoc:
|
|
148
|
+
loop do
|
|
149
|
+
token = Devise.friendly_token
|
|
150
|
+
break token unless to_adapter.find_first({ remember_token: token })
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options, :expire_all_remember_me_on_sign_out)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'devise/hooks/timeoutable'
|
|
4
|
+
|
|
5
|
+
module Devise
|
|
6
|
+
module Models
|
|
7
|
+
# Timeoutable takes care of verifying whether a user session has already
|
|
8
|
+
# expired or not. When a session expires after the configured time, the user
|
|
9
|
+
# will be asked for credentials again, it means, they will be redirected
|
|
10
|
+
# to the sign in page.
|
|
11
|
+
#
|
|
12
|
+
# == Options
|
|
13
|
+
#
|
|
14
|
+
# Timeoutable adds the following options to +devise+:
|
|
15
|
+
#
|
|
16
|
+
# * +timeout_in+: the interval to timeout the user session without activity.
|
|
17
|
+
#
|
|
18
|
+
# == Examples
|
|
19
|
+
#
|
|
20
|
+
# user.timedout?(30.minutes.ago)
|
|
21
|
+
#
|
|
22
|
+
module Timeoutable
|
|
23
|
+
extend ActiveSupport::Concern
|
|
24
|
+
|
|
25
|
+
def self.required_fields(klass)
|
|
26
|
+
[]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Checks whether the user session has expired based on configured time.
|
|
30
|
+
def timedout?(last_access)
|
|
31
|
+
!timeout_in.nil? && last_access && last_access <= timeout_in.ago
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def timeout_in
|
|
35
|
+
self.class.timeout_in
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
module ClassMethods
|
|
41
|
+
Devise::Models.config(self, :timeout_in)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'devise/hooks/trackable'
|
|
4
|
+
|
|
5
|
+
module Devise
|
|
6
|
+
module Models
|
|
7
|
+
# Track information about your user sign in. It tracks the following columns:
|
|
8
|
+
#
|
|
9
|
+
# * sign_in_count - Increased every time a sign in is made (by form, openid, oauth)
|
|
10
|
+
# * current_sign_in_at - A timestamp updated when the user signs in
|
|
11
|
+
# * last_sign_in_at - Holds the timestamp of the previous sign in
|
|
12
|
+
# * current_sign_in_ip - The remote ip updated when the user sign in
|
|
13
|
+
# * last_sign_in_ip - Holds the remote ip of the previous sign in
|
|
14
|
+
#
|
|
15
|
+
module Trackable
|
|
16
|
+
def self.required_fields(klass)
|
|
17
|
+
[:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def update_tracked_fields(request)
|
|
21
|
+
old_current, new_current = self.current_sign_in_at, Time.now.utc
|
|
22
|
+
self.last_sign_in_at = old_current || new_current
|
|
23
|
+
self.current_sign_in_at = new_current
|
|
24
|
+
|
|
25
|
+
old_current, new_current = self.current_sign_in_ip, extract_ip_from(request)
|
|
26
|
+
self.last_sign_in_ip = old_current || new_current
|
|
27
|
+
self.current_sign_in_ip = new_current
|
|
28
|
+
|
|
29
|
+
self.sign_in_count ||= 0
|
|
30
|
+
self.sign_in_count += 1
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def update_tracked_fields!(request)
|
|
34
|
+
# We have to check if the user is already persisted before running
|
|
35
|
+
# `save` here because invalid users can be saved if we don't.
|
|
36
|
+
# See https://github.com/heartcombo/devise/issues/4673 for more details.
|
|
37
|
+
return if new_record?
|
|
38
|
+
|
|
39
|
+
update_tracked_fields(request)
|
|
40
|
+
save(validate: false)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
protected
|
|
44
|
+
|
|
45
|
+
def extract_ip_from(request)
|
|
46
|
+
request.remote_ip
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|