devise 3.3.0 → 3.5.10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of devise might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +29 -20
- data/CHANGELOG.md +219 -102
- data/CODE_OF_CONDUCT.md +22 -0
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +101 -80
- data/MIT-LICENSE +1 -1
- data/README.md +87 -43
- data/Rakefile +2 -1
- data/app/controllers/devise/confirmations_controller.rb +5 -1
- data/app/controllers/devise/omniauth_callbacks_controller.rb +4 -0
- data/app/controllers/devise/passwords_controller.rb +14 -4
- data/app/controllers/devise/registrations_controller.rb +10 -11
- data/app/controllers/devise/sessions_controller.rb +7 -2
- data/app/controllers/devise/unlocks_controller.rb +3 -0
- data/app/controllers/devise_controller.rb +44 -21
- data/app/mailers/devise/mailer.rb +4 -0
- data/app/views/devise/confirmations/new.html.erb +7 -3
- data/app/views/devise/mailer/password_change.html.erb +3 -0
- data/app/views/devise/passwords/edit.html.erb +14 -5
- data/app/views/devise/passwords/new.html.erb +7 -3
- data/app/views/devise/registrations/edit.html.erb +19 -9
- data/app/views/devise/registrations/new.html.erb +18 -7
- data/app/views/devise/sessions/new.html.erb +15 -6
- data/app/views/devise/shared/{_links.erb → _links.html.erb} +1 -1
- data/app/views/devise/unlocks/new.html.erb +7 -3
- data/config/locales/en.yml +4 -2
- data/devise.gemspec +2 -2
- data/gemfiles/Gemfile.rails-3.2-stable.lock +54 -48
- data/gemfiles/Gemfile.rails-4.0-stable +1 -0
- data/gemfiles/Gemfile.rails-4.0-stable.lock +63 -59
- data/gemfiles/{Gemfile.rails-head → Gemfile.rails-4.1-stable} +3 -5
- data/gemfiles/Gemfile.rails-4.1-stable.lock +171 -0
- data/gemfiles/Gemfile.rails-4.2-stable +30 -0
- data/gemfiles/Gemfile.rails-4.2-stable.lock +193 -0
- data/lib/devise/controllers/helpers.rb +12 -6
- data/lib/devise/controllers/rememberable.rb +9 -2
- data/lib/devise/controllers/sign_in_out.rb +2 -8
- data/lib/devise/controllers/store_location.rb +3 -1
- data/lib/devise/controllers/url_helpers.rb +7 -9
- data/lib/devise/encryptor.rb +22 -0
- data/lib/devise/failure_app.rb +56 -14
- data/lib/devise/hooks/timeoutable.rb +5 -7
- data/lib/devise/mapping.rb +2 -1
- data/lib/devise/models/authenticatable.rb +28 -28
- data/lib/devise/models/confirmable.rb +51 -17
- data/lib/devise/models/database_authenticatable.rb +17 -11
- data/lib/devise/models/lockable.rb +7 -3
- data/lib/devise/models/recoverable.rb +23 -15
- data/lib/devise/models/rememberable.rb +56 -22
- data/lib/devise/models/timeoutable.rb +0 -6
- data/lib/devise/models/trackable.rb +1 -2
- data/lib/devise/models/validatable.rb +3 -3
- data/lib/devise/models.rb +1 -1
- data/lib/devise/rails/routes.rb +33 -27
- data/lib/devise/rails.rb +1 -1
- data/lib/devise/strategies/authenticatable.rb +8 -6
- data/lib/devise/strategies/database_authenticatable.rb +2 -1
- data/lib/devise/strategies/rememberable.rb +13 -3
- data/lib/devise/test_helpers.rb +2 -2
- data/lib/devise/version.rb +1 -1
- data/lib/devise.rb +39 -37
- data/lib/generators/active_record/devise_generator.rb +2 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- data/lib/generators/active_record/templates/migration_existing.rb +1 -1
- data/lib/generators/devise/controllers_generator.rb +44 -0
- data/lib/generators/devise/views_generator.rb +14 -3
- data/lib/generators/templates/controllers/README +14 -0
- data/lib/generators/templates/controllers/confirmations_controller.rb +28 -0
- data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +28 -0
- data/lib/generators/templates/controllers/passwords_controller.rb +32 -0
- data/lib/generators/templates/controllers/registrations_controller.rb +60 -0
- data/lib/generators/templates/controllers/sessions_controller.rb +25 -0
- data/lib/generators/templates/controllers/unlocks_controller.rb +28 -0
- data/lib/generators/templates/devise.rb +19 -13
- data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
- data/lib/generators/templates/markerb/password_change.markerb +3 -0
- data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
- data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +1 -1
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +1 -1
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +2 -2
- data/test/controllers/custom_registrations_controller_test.rb +6 -1
- data/test/controllers/helper_methods_test.rb +21 -0
- data/test/controllers/helpers_test.rb +5 -0
- data/test/controllers/inherited_controller_i18n_messages_test.rb +51 -0
- data/test/controllers/internal_helpers_test.rb +10 -4
- data/test/controllers/load_hooks_controller_test.rb +19 -0
- data/test/controllers/passwords_controller_test.rb +1 -1
- data/test/controllers/sessions_controller_test.rb +3 -3
- data/test/controllers/url_helpers_test.rb +6 -0
- data/test/devise_test.rb +3 -3
- data/test/failure_app_test.rb +47 -0
- data/test/generators/controllers_generator_test.rb +48 -0
- data/test/generators/views_generator_test.rb +8 -1
- data/test/helpers/devise_helper_test.rb +9 -12
- data/test/integration/authenticatable_test.rb +1 -1
- data/test/integration/database_authenticatable_test.rb +11 -0
- data/test/integration/http_authenticatable_test.rb +1 -1
- data/test/integration/omniauthable_test.rb +12 -10
- data/test/integration/recoverable_test.rb +13 -0
- data/test/integration/rememberable_test.rb +50 -3
- data/test/integration/timeoutable_test.rb +13 -18
- data/test/mailers/confirmation_instructions_test.rb +1 -1
- data/test/mapping_test.rb +7 -0
- data/test/models/authenticatable_test.rb +10 -0
- data/test/models/confirmable_test.rb +99 -42
- data/test/models/database_authenticatable_test.rb +20 -0
- data/test/models/lockable_test.rb +45 -17
- data/test/models/recoverable_test.rb +62 -7
- data/test/models/rememberable_test.rb +68 -97
- data/test/models/validatable_test.rb +5 -5
- data/test/models_test.rb +15 -6
- data/test/rails_app/app/active_record/user_without_email.rb +8 -0
- data/test/rails_app/app/controllers/admins_controller.rb +0 -5
- data/test/rails_app/app/controllers/custom/registrations_controller.rb +10 -0
- data/test/rails_app/app/mailers/users/from_proc_mailer.rb +3 -0
- data/test/rails_app/app/mailers/users/mailer.rb +0 -9
- data/test/rails_app/app/mailers/users/reply_to_mailer.rb +4 -0
- data/test/rails_app/app/mongoid/user_without_email.rb +33 -0
- data/test/rails_app/config/application.rb +1 -1
- data/test/rails_app/config/environments/production.rb +6 -2
- data/test/rails_app/config/environments/test.rb +7 -2
- data/test/rails_app/config/initializers/devise.rb +12 -15
- data/test/rails_app/config/routes.rb +6 -3
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +2 -2
- data/test/rails_app/lib/shared_user.rb +1 -1
- data/test/rails_app/lib/shared_user_without_email.rb +26 -0
- data/test/rails_test.rb +9 -0
- data/test/support/helpers.rb +13 -6
- data/test/support/integration.rb +2 -2
- data/test/test_helper.rb +5 -0
- data/test/test_helpers_test.rb +22 -7
- data/test/test_models.rb +2 -2
- data/test/time_helpers.rb +137 -0
- metadata +58 -8
- data/gemfiles/Gemfile.rails-head.lock +0 -190
@@ -5,6 +5,14 @@ module Devise
|
|
5
5
|
# Confirmation instructions are sent to the user email after creating a
|
6
6
|
# record and when manually requested by a new confirmation instruction request.
|
7
7
|
#
|
8
|
+
# Confirmable tracks the following columns:
|
9
|
+
#
|
10
|
+
# * confirmation_token - A unique random token
|
11
|
+
# * confirmed_at - A timestamp when the user clicked the confirmation link
|
12
|
+
# * confirmation_sent_at - A timestamp when the confirmation_token was generated (not sent)
|
13
|
+
# * unconfirmed_email - An email address copied from the email attr. After confirmation
|
14
|
+
# this value is copied to the email attr then cleared
|
15
|
+
#
|
8
16
|
# == Options
|
9
17
|
#
|
10
18
|
# Confirmable adds the following options to +devise+:
|
@@ -16,15 +24,17 @@ module Devise
|
|
16
24
|
# By default allow_unconfirmed_access_for is zero, it means users always have to confirm to sign in.
|
17
25
|
# * +reconfirmable+: requires any email changes to be confirmed (exactly the same way as
|
18
26
|
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
19
|
-
# db field to be setup (t.reconfirmable in migrations). Until confirmed new email is
|
27
|
+
# db field to be setup (t.reconfirmable in migrations). Until confirmed, new email is
|
20
28
|
# stored in unconfirmed email column, and copied to email column on successful
|
21
29
|
# confirmation.
|
22
30
|
# * +confirm_within+: the time before a sent confirmation token becomes invalid.
|
23
31
|
# You can use this to force the user to confirm within a set period of time.
|
32
|
+
# Confirmable will not generate a new token if a repeat confirmation is requested
|
33
|
+
# during this time frame, unless the user's email changed too.
|
24
34
|
#
|
25
35
|
# == Examples
|
26
36
|
#
|
27
|
-
# User.find(1).confirm
|
37
|
+
# User.find(1).confirm # returns true unless it's already confirmed
|
28
38
|
# User.find(1).confirmed? # true/false
|
29
39
|
# User.find(1).send_confirmation_instructions # manually send instructions
|
30
40
|
#
|
@@ -56,7 +66,7 @@ module Devise
|
|
56
66
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
57
67
|
# is already confirmed, add an error to email field. If the user is invalid
|
58
68
|
# add errors
|
59
|
-
def confirm
|
69
|
+
def confirm(args={})
|
60
70
|
pending_any_confirmation do
|
61
71
|
if confirmation_period_expired?
|
62
72
|
self.errors.add(:email, :confirmation_period_expired,
|
@@ -64,7 +74,6 @@ module Devise
|
|
64
74
|
return false
|
65
75
|
end
|
66
76
|
|
67
|
-
self.confirmation_token = nil
|
68
77
|
self.confirmed_at = Time.now.utc
|
69
78
|
|
70
79
|
saved = if self.class.reconfirmable && unconfirmed_email.present?
|
@@ -75,7 +84,7 @@ module Devise
|
|
75
84
|
# We need to validate in such cases to enforce e-mail uniqueness
|
76
85
|
save(validate: true)
|
77
86
|
else
|
78
|
-
save(validate:
|
87
|
+
save(validate: args[:ensure_valid] == true)
|
79
88
|
end
|
80
89
|
|
81
90
|
after_confirmation if saved
|
@@ -83,6 +92,11 @@ module Devise
|
|
83
92
|
end
|
84
93
|
end
|
85
94
|
|
95
|
+
def confirm!(args={})
|
96
|
+
ActiveSupport::Deprecation.warn "confirm! is deprecated in favor of confirm"
|
97
|
+
confirm(args)
|
98
|
+
end
|
99
|
+
|
86
100
|
# Verifies whether a user is confirmed or not
|
87
101
|
def confirmed?
|
88
102
|
!!confirmed_at
|
@@ -156,6 +170,7 @@ module Devise
|
|
156
170
|
# in models to map to a nice sign up e-mail.
|
157
171
|
def send_on_create_confirmation_instructions
|
158
172
|
send_confirmation_instructions
|
173
|
+
skip_reconfirmation!
|
159
174
|
end
|
160
175
|
|
161
176
|
# Callback to overwrite if confirmation is required or not.
|
@@ -202,7 +217,7 @@ module Devise
|
|
202
217
|
# confirmation_period_expired? # will always return false
|
203
218
|
#
|
204
219
|
def confirmation_period_expired?
|
205
|
-
self.class.confirm_within && (Time.now > self.confirmation_sent_at + self.class.confirm_within
|
220
|
+
self.class.confirm_within && self.confirmation_sent_at && (Time.now > self.confirmation_sent_at + self.class.confirm_within)
|
206
221
|
end
|
207
222
|
|
208
223
|
# Checks whether the record requires any confirmation.
|
@@ -216,12 +231,15 @@ module Devise
|
|
216
231
|
end
|
217
232
|
|
218
233
|
# Generates a new random token for confirmation, and stores
|
219
|
-
# the time this token is being generated
|
234
|
+
# the time this token is being generated in confirmation_sent_at
|
220
235
|
def generate_confirmation_token
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
236
|
+
if self.confirmation_token && !confirmation_period_expired?
|
237
|
+
@raw_confirmation_token = self.confirmation_token
|
238
|
+
else
|
239
|
+
raw, _ = Devise.token_generator.generate(self.class, :confirmation_token)
|
240
|
+
self.confirmation_token = @raw_confirmation_token = raw
|
241
|
+
self.confirmation_sent_at = Time.now.utc
|
242
|
+
end
|
225
243
|
end
|
226
244
|
|
227
245
|
def generate_confirmation_token!
|
@@ -232,6 +250,7 @@ module Devise
|
|
232
250
|
@reconfirmation_required = true
|
233
251
|
self.unconfirmed_email = self.email
|
234
252
|
self.email = self.email_was
|
253
|
+
self.confirmation_token = nil
|
235
254
|
generate_confirmation_token
|
236
255
|
end
|
237
256
|
|
@@ -242,13 +261,23 @@ module Devise
|
|
242
261
|
end
|
243
262
|
|
244
263
|
def reconfirmation_required?
|
245
|
-
self.class.reconfirmable && @reconfirmation_required && self.email.present?
|
264
|
+
self.class.reconfirmable && @reconfirmation_required && (self.email.present? || self.unconfirmed_email.present?)
|
246
265
|
end
|
247
266
|
|
248
267
|
def send_confirmation_notification?
|
249
268
|
confirmation_required? && !@skip_confirmation_notification && self.email.present?
|
250
269
|
end
|
251
270
|
|
271
|
+
# A callback initiated after successfully confirming. This can be
|
272
|
+
# used to insert your own logic that is only run after the user successfully
|
273
|
+
# confirms.
|
274
|
+
#
|
275
|
+
# Example:
|
276
|
+
#
|
277
|
+
# def after_confirmation
|
278
|
+
# self.update_attribute(:invite_code, nil)
|
279
|
+
# end
|
280
|
+
#
|
252
281
|
def after_confirmation
|
253
282
|
end
|
254
283
|
|
@@ -271,12 +300,17 @@ module Devise
|
|
271
300
|
# If the user is already confirmed, create an error for the user
|
272
301
|
# Options must have the confirmation_token
|
273
302
|
def confirm_by_token(confirmation_token)
|
274
|
-
|
275
|
-
|
303
|
+
confirmable = find_first_by_auth_conditions(confirmation_token: confirmation_token)
|
304
|
+
unless confirmable
|
305
|
+
confirmation_digest = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
306
|
+
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_digest)
|
307
|
+
end
|
308
|
+
|
309
|
+
# TODO: replace above lines with
|
310
|
+
# confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
311
|
+
# after enough time has passed that Devise clients do not use digested tokens
|
276
312
|
|
277
|
-
confirmable
|
278
|
-
confirmable.confirm! if confirmable.persisted?
|
279
|
-
confirmable.confirmation_token = original_token
|
313
|
+
confirmable.confirm if confirmable.persisted?
|
280
314
|
confirmable
|
281
315
|
end
|
282
316
|
|
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'devise/strategies/database_authenticatable'
|
2
|
-
require 'bcrypt'
|
3
2
|
|
4
3
|
module Devise
|
5
|
-
# Digests the password using bcrypt.
|
6
4
|
def self.bcrypt(klass, password)
|
7
|
-
::
|
5
|
+
ActiveSupport::Deprecation.warn "Devise.bcrypt is deprecated; use Devise::Encryptor.digest instead"
|
6
|
+
Devise::Encryptor.digest(klass, password)
|
8
7
|
end
|
9
8
|
|
10
9
|
module Models
|
@@ -13,7 +12,7 @@ module Devise
|
|
13
12
|
#
|
14
13
|
# == Options
|
15
14
|
#
|
16
|
-
#
|
15
|
+
# DatabaseAuthenticatable adds the following options to devise_for:
|
17
16
|
#
|
18
17
|
# * +pepper+: a random string used to provide a more secure hash. Use
|
19
18
|
# `rake secret` to generate new keys.
|
@@ -28,6 +27,8 @@ module Devise
|
|
28
27
|
extend ActiveSupport::Concern
|
29
28
|
|
30
29
|
included do
|
30
|
+
after_update :send_password_change_notification, if: :send_password_change_notification?
|
31
|
+
|
31
32
|
attr_reader :password, :current_password
|
32
33
|
attr_accessor :password_confirmation
|
33
34
|
end
|
@@ -42,12 +43,9 @@ module Devise
|
|
42
43
|
self.encrypted_password = password_digest(@password) if @password.present?
|
43
44
|
end
|
44
45
|
|
45
|
-
# Verifies whether
|
46
|
+
# Verifies whether a password (ie from sign in) is the user password.
|
46
47
|
def valid_password?(password)
|
47
|
-
|
48
|
-
bcrypt = ::BCrypt::Password.new(encrypted_password)
|
49
|
-
password = ::BCrypt::Engine.hash_secret("#{password}#{self.class.pepper}", bcrypt.salt)
|
50
|
-
Devise.secure_compare(password, encrypted_password)
|
48
|
+
Devise::Encryptor.compare(self.class, encrypted_password, password)
|
51
49
|
end
|
52
50
|
|
53
51
|
# Set password and password confirmation to nil
|
@@ -137,6 +135,10 @@ module Devise
|
|
137
135
|
encrypted_password[0,29] if encrypted_password
|
138
136
|
end
|
139
137
|
|
138
|
+
def send_password_change_notification
|
139
|
+
send_devise_notification(:password_change)
|
140
|
+
end
|
141
|
+
|
140
142
|
protected
|
141
143
|
|
142
144
|
# Digests the password using bcrypt. Custom encryption should override
|
@@ -145,11 +147,15 @@ module Devise
|
|
145
147
|
# See https://github.com/plataformatec/devise-encryptable for examples
|
146
148
|
# of other encryption engines.
|
147
149
|
def password_digest(password)
|
148
|
-
Devise.
|
150
|
+
Devise::Encryptor.digest(self.class, password)
|
151
|
+
end
|
152
|
+
|
153
|
+
def send_password_change_notification?
|
154
|
+
self.class.send_password_change_notification && encrypted_password_changed?
|
149
155
|
end
|
150
156
|
|
151
157
|
module ClassMethods
|
152
|
-
Devise::Models.config(self, :pepper, :stretches)
|
158
|
+
Devise::Models.config(self, :pepper, :stretches, :send_password_change_notification)
|
153
159
|
|
154
160
|
# We assume this method already gets the sanitized values from the
|
155
161
|
# DatabaseAuthenticatable strategy. If you are using this method on
|
@@ -117,7 +117,7 @@ module Devise
|
|
117
117
|
super
|
118
118
|
elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?)
|
119
119
|
:locked
|
120
|
-
elsif lock_strategy_enabled?(:failed_attempts) && last_attempt?
|
120
|
+
elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? && self.class.last_attempt_warning
|
121
121
|
:last_attempt
|
122
122
|
else
|
123
123
|
super
|
@@ -155,6 +155,9 @@ module Devise
|
|
155
155
|
end
|
156
156
|
|
157
157
|
module ClassMethods
|
158
|
+
# List of strategies that are enabled/supported if :both is used.
|
159
|
+
BOTH_STRATEGIES = [:time, :email]
|
160
|
+
|
158
161
|
# Attempt to find a user by its unlock keys. If a record is found, send new
|
159
162
|
# unlock instructions to it. If not user is found, returns a new user
|
160
163
|
# with an email not found error.
|
@@ -181,7 +184,8 @@ module Devise
|
|
181
184
|
|
182
185
|
# Is the unlock enabled for the given unlock strategy?
|
183
186
|
def unlock_strategy_enabled?(strategy)
|
184
|
-
|
187
|
+
self.unlock_strategy == strategy ||
|
188
|
+
(self.unlock_strategy == :both && BOTH_STRATEGIES.include?(strategy))
|
185
189
|
end
|
186
190
|
|
187
191
|
# Is the lock enabled for the given lock strategy?
|
@@ -189,7 +193,7 @@ module Devise
|
|
189
193
|
self.lock_strategy == strategy
|
190
194
|
end
|
191
195
|
|
192
|
-
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
|
196
|
+
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys, :last_attempt_warning)
|
193
197
|
end
|
194
198
|
end
|
195
199
|
end
|
@@ -8,15 +8,13 @@ module Devise
|
|
8
8
|
# Recoverable adds the following options to devise_for:
|
9
9
|
#
|
10
10
|
# * +reset_password_keys+: the keys you want to use when recovering the password for an account
|
11
|
+
# * +reset_password_within+: the time period within which the password must be reset or the token expires.
|
12
|
+
# * +sign_in_after_reset_password+: whether or not to sign in the user automatically after a password reset.
|
11
13
|
#
|
12
14
|
# == Examples
|
13
15
|
#
|
14
16
|
# # resets the user password and save the record, true if valid passwords are given, otherwise false
|
15
|
-
# User.find(1).reset_password
|
16
|
-
#
|
17
|
-
# # only resets the user password, without saving the record
|
18
|
-
# user = User.find(1)
|
19
|
-
# user.reset_password('password123', 'password123')
|
17
|
+
# User.find(1).reset_password('password123', 'password123')
|
20
18
|
#
|
21
19
|
# # creates a new token and send it with instructions about how to reset the password
|
22
20
|
# User.find(1).send_reset_password_instructions
|
@@ -28,20 +26,33 @@ module Devise
|
|
28
26
|
[:reset_password_sent_at, :reset_password_token]
|
29
27
|
end
|
30
28
|
|
29
|
+
included do
|
30
|
+
before_update do
|
31
|
+
if (respond_to?(:email_changed?) && email_changed?) || encrypted_password_changed?
|
32
|
+
clear_reset_password_token
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
31
37
|
# Update password saving the record and clearing token. Returns true if
|
32
38
|
# the passwords are valid and the record was saved, false otherwise.
|
33
|
-
def reset_password
|
39
|
+
def reset_password(new_password, new_password_confirmation)
|
34
40
|
self.password = new_password
|
35
41
|
self.password_confirmation = new_password_confirmation
|
36
42
|
|
37
|
-
if valid?
|
38
|
-
|
43
|
+
if respond_to?(:after_password_reset) && valid?
|
44
|
+
ActiveSupport::Deprecation.warn "after_password_reset is deprecated"
|
39
45
|
after_password_reset
|
40
46
|
end
|
41
47
|
|
42
48
|
save
|
43
49
|
end
|
44
50
|
|
51
|
+
def reset_password!(new_password, new_password_confirmation)
|
52
|
+
ActiveSupport::Deprecation.warn "reset_password! is deprecated in favor of reset_password"
|
53
|
+
reset_password(new_password, new_password_confirmation)
|
54
|
+
end
|
55
|
+
|
45
56
|
# Resets reset password token and send reset password instructions by email.
|
46
57
|
# Returns the token sent in the e-mail.
|
47
58
|
def send_reset_password_instructions
|
@@ -72,7 +83,7 @@ module Devise
|
|
72
83
|
# reset_password_period_valid? # will always return false
|
73
84
|
#
|
74
85
|
def reset_password_period_valid?
|
75
|
-
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
|
86
|
+
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago.utc
|
76
87
|
end
|
77
88
|
|
78
89
|
protected
|
@@ -83,9 +94,6 @@ module Devise
|
|
83
94
|
self.reset_password_sent_at = nil
|
84
95
|
end
|
85
96
|
|
86
|
-
def after_password_reset
|
87
|
-
end
|
88
|
-
|
89
97
|
def set_reset_password_token
|
90
98
|
raw, enc = Devise.token_generator.generate(self.class, :reset_password_token)
|
91
99
|
|
@@ -130,17 +138,17 @@ module Devise
|
|
130
138
|
|
131
139
|
if recoverable.persisted?
|
132
140
|
if recoverable.reset_password_period_valid?
|
133
|
-
recoverable.reset_password
|
141
|
+
recoverable.reset_password(attributes[:password], attributes[:password_confirmation])
|
134
142
|
else
|
135
143
|
recoverable.errors.add(:reset_password_token, :expired)
|
136
144
|
end
|
137
145
|
end
|
138
146
|
|
139
|
-
recoverable.reset_password_token = original_token
|
147
|
+
recoverable.reset_password_token = original_token if recoverable.reset_password_token.present?
|
140
148
|
recoverable
|
141
149
|
end
|
142
150
|
|
143
|
-
Devise::Models.config(self, :reset_password_keys, :reset_password_within)
|
151
|
+
Devise::Models.config(self, :reset_password_keys, :reset_password_within, :sign_in_after_reset_password)
|
144
152
|
end
|
145
153
|
end
|
146
154
|
end
|
@@ -39,17 +39,17 @@ module Devise
|
|
39
39
|
module Rememberable
|
40
40
|
extend ActiveSupport::Concern
|
41
41
|
|
42
|
-
attr_accessor :remember_me
|
42
|
+
attr_accessor :remember_me
|
43
43
|
|
44
44
|
def self.required_fields(klass)
|
45
45
|
[:remember_created_at]
|
46
46
|
end
|
47
47
|
|
48
|
-
#
|
49
|
-
#
|
50
|
-
def remember_me!(
|
51
|
-
self.remember_token
|
52
|
-
self.remember_created_at
|
48
|
+
# TODO: We were used to receive a extend period argument but we no longer do.
|
49
|
+
# Remove this for Devise 4.0.
|
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
53
|
save(validate: false) if self.changed?
|
54
54
|
end
|
55
55
|
|
@@ -57,25 +57,28 @@ module Devise
|
|
57
57
|
# it exists), and save the record without validations.
|
58
58
|
def forget_me!
|
59
59
|
return unless persisted?
|
60
|
-
self.remember_token = nil if respond_to?(:remember_token
|
60
|
+
self.remember_token = nil if respond_to?(:remember_token)
|
61
61
|
self.remember_created_at = nil if self.class.expire_all_remember_me_on_sign_out
|
62
62
|
save(validate: false)
|
63
63
|
end
|
64
64
|
|
65
65
|
# Remember token should be expired if expiration time not overpass now.
|
66
66
|
def remember_expired?
|
67
|
-
remember_created_at.nil?
|
67
|
+
remember_created_at.nil?
|
68
68
|
end
|
69
69
|
|
70
|
-
# Remember token expires at created time + remember_for configuration
|
71
70
|
def remember_expires_at
|
72
|
-
|
71
|
+
self.class.remember_for.from_now
|
72
|
+
end
|
73
|
+
|
74
|
+
def extend_remember_period
|
75
|
+
self.class.extend_remember_period
|
73
76
|
end
|
74
77
|
|
75
78
|
def rememberable_value
|
76
79
|
if respond_to?(:remember_token)
|
77
80
|
remember_token
|
78
|
-
elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt)
|
81
|
+
elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
|
79
82
|
salt
|
80
83
|
else
|
81
84
|
raise "authenticable_salt returned nil for the #{self.class.name} model. " \
|
@@ -89,29 +92,60 @@ module Devise
|
|
89
92
|
self.class.rememberable_options
|
90
93
|
end
|
91
94
|
|
92
|
-
|
95
|
+
# A callback initiated after successfully being remembered. This can be
|
96
|
+
# used to insert your own logic that is only run after the user is
|
97
|
+
# remembered.
|
98
|
+
#
|
99
|
+
# Example:
|
100
|
+
#
|
101
|
+
# def after_remembered
|
102
|
+
# self.update_attribute(:invite_code, nil)
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
def after_remembered
|
106
|
+
end
|
107
|
+
|
108
|
+
def remember_me?(token, generated_at)
|
109
|
+
# TODO: Normalize the JSON type coercion along with the Timeoutable hook
|
110
|
+
# in a single place https://github.com/plataformatec/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
|
111
|
+
if generated_at.is_a?(String)
|
112
|
+
generated_at = time_from_json(generated_at)
|
113
|
+
end
|
93
114
|
|
94
|
-
|
95
|
-
|
115
|
+
# The token is only valid if:
|
116
|
+
# 1. we have a date
|
117
|
+
# 2. the current time does not pass the expiry period
|
118
|
+
# 3. the record has a remember_created_at date
|
119
|
+
# 4. the token date is bigger than the remember_created_at
|
120
|
+
# 5. the token matches
|
121
|
+
generated_at.is_a?(Time) &&
|
122
|
+
(self.class.remember_for.ago < generated_at) &&
|
123
|
+
(generated_at > (remember_created_at || Time.now).utc) &&
|
124
|
+
Devise.secure_compare(rememberable_value, token)
|
96
125
|
end
|
97
126
|
|
98
|
-
|
99
|
-
|
100
|
-
def
|
101
|
-
|
127
|
+
private
|
128
|
+
|
129
|
+
def time_from_json(value)
|
130
|
+
if value =~ /\A\d+\.\d+\Z/
|
131
|
+
Time.at(value.to_f)
|
132
|
+
else
|
133
|
+
Time.parse(value) rescue nil
|
134
|
+
end
|
102
135
|
end
|
103
136
|
|
104
137
|
module ClassMethods
|
105
138
|
# Create the cookie key using the record id and remember_token
|
106
139
|
def serialize_into_cookie(record)
|
107
|
-
[record.to_key, record.rememberable_value]
|
140
|
+
[record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
|
108
141
|
end
|
109
142
|
|
110
143
|
# Recreate the user based on the stored cookie
|
111
|
-
def serialize_from_cookie(
|
144
|
+
def serialize_from_cookie(*args)
|
145
|
+
id, token, generated_at = *args
|
146
|
+
|
112
147
|
record = to_adapter.get(id)
|
113
|
-
record if record &&
|
114
|
-
Devise.secure_compare(record.rememberable_value, remember_token)
|
148
|
+
record if record && record.remember_me?(token, generated_at)
|
115
149
|
end
|
116
150
|
|
117
151
|
# Generate a token checking if one does not already exist in the database.
|
@@ -26,7 +26,6 @@ module Devise
|
|
26
26
|
|
27
27
|
# Checks whether the user session has expired based on configured time.
|
28
28
|
def timedout?(last_access)
|
29
|
-
return false if remember_exists_and_not_expired?
|
30
29
|
!timeout_in.nil? && last_access && last_access <= timeout_in.ago
|
31
30
|
end
|
32
31
|
|
@@ -36,11 +35,6 @@ module Devise
|
|
36
35
|
|
37
36
|
private
|
38
37
|
|
39
|
-
def remember_exists_and_not_expired?
|
40
|
-
return false unless respond_to?(:remember_created_at) && respond_to?(:remember_expired?)
|
41
|
-
remember_created_at && !remember_expired?
|
42
|
-
end
|
43
|
-
|
44
38
|
module ClassMethods
|
45
39
|
Devise::Models.config(self, :timeout_in)
|
46
40
|
end
|
@@ -30,8 +30,7 @@ module Devise
|
|
30
30
|
|
31
31
|
def update_tracked_fields!(request)
|
32
32
|
update_tracked_fields(request)
|
33
|
-
save(validate: false)
|
34
|
-
"Please make sure a model using trackable can be saved at sign in."
|
33
|
+
save(validate: false)
|
35
34
|
end
|
36
35
|
end
|
37
36
|
end
|
@@ -10,12 +10,12 @@ module Devise
|
|
10
10
|
# Validatable adds the following options to devise_for:
|
11
11
|
#
|
12
12
|
# * +email_regexp+: the regular expression used to validate e-mails;
|
13
|
-
# * +password_length+: a range expressing password length. Defaults to 8..
|
13
|
+
# * +password_length+: a range expressing password length. Defaults to 8..72.
|
14
14
|
#
|
15
15
|
module Validatable
|
16
16
|
# All validations used by this module.
|
17
|
-
VALIDATIONS = [
|
18
|
-
|
17
|
+
VALIDATIONS = [:validates_presence_of, :validates_uniqueness_of, :validates_format_of,
|
18
|
+
:validates_confirmation_of, :validates_length_of].freeze
|
19
19
|
|
20
20
|
def self.required_fields(klass)
|
21
21
|
[]
|
data/lib/devise/models.rb
CHANGED
@@ -12,7 +12,7 @@ module Devise
|
|
12
12
|
|
13
13
|
# Creates configuration values for Devise and for the given module.
|
14
14
|
#
|
15
|
-
# Devise::Models.config(Devise::
|
15
|
+
# Devise::Models.config(Devise::DatabaseAuthenticatable, :stretches)
|
16
16
|
#
|
17
17
|
# The line above creates:
|
18
18
|
#
|
data/lib/devise/rails/routes.rb
CHANGED
@@ -94,10 +94,24 @@ module ActionDispatch::Routing
|
|
94
94
|
#
|
95
95
|
# devise_for :users, path: 'accounts'
|
96
96
|
#
|
97
|
-
# * singular: setup the singular name for the given resource. This is used as the
|
98
|
-
#
|
97
|
+
# * singular: setup the singular name for the given resource. This is used as the helper methods
|
98
|
+
# names in controller ("authenticate_#{singular}!", "#{singular}_signed_in?", "current_#{singular}"
|
99
|
+
# and "#{singular}_session"), as the scope name in routes and as the scope given to warden.
|
99
100
|
#
|
100
|
-
# devise_for :
|
101
|
+
# devise_for :admins, singular: :manager
|
102
|
+
#
|
103
|
+
# devise_scope :manager do
|
104
|
+
# ...
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# class ManagerController < ApplicationController
|
108
|
+
# before_filter authenticate_manager!
|
109
|
+
#
|
110
|
+
# def show
|
111
|
+
# @manager = current_manager
|
112
|
+
# ...
|
113
|
+
# end
|
114
|
+
# end
|
101
115
|
#
|
102
116
|
# * path_names: configure different path names to overwrite defaults :sign_in, :sign_out, :sign_up,
|
103
117
|
# :password, :confirmation, :unlock.
|
@@ -119,7 +133,7 @@ module ActionDispatch::Routing
|
|
119
133
|
# * sign_out_via: the HTTP method(s) accepted for the :sign_out action (default: :get),
|
120
134
|
# if you wish to restrict this to accept only :post or :delete requests you should do:
|
121
135
|
#
|
122
|
-
# devise_for :users, sign_out_via: [
|
136
|
+
# devise_for :users, sign_out_via: [:post, :delete]
|
123
137
|
#
|
124
138
|
# You need to make sure that your sign_out controls trigger a request with a matching HTTP method.
|
125
139
|
#
|
@@ -402,21 +416,16 @@ module ActionDispatch::Routing
|
|
402
416
|
def devise_omniauth_callback(mapping, controllers) #:nodoc:
|
403
417
|
if mapping.fullpath =~ /:[a-zA-Z_]/
|
404
418
|
raise <<-ERROR
|
405
|
-
Devise does not support scoping
|
419
|
+
Devise does not support scoping OmniAuth callbacks under a dynamic segment
|
406
420
|
and you have set #{mapping.fullpath.inspect}. You can work around by passing
|
407
|
-
`skip: :omniauth_callbacks`
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
match "/users/auth/:action/callback",
|
416
|
-
constraints: { action: /google|facebook/ },
|
417
|
-
to: "devise/omniauth_callbacks",
|
418
|
-
as: :omniauth_callback,
|
419
|
-
via: [:get, :post]
|
421
|
+
`skip: :omniauth_callbacks` to the `devise_for` call and extract omniauth
|
422
|
+
options to another `devise_for` call outside the scope. Here is an example:
|
423
|
+
|
424
|
+
devise_for :users, only: :omniauth_callbacks, controllers: {omniauth_callbacks: 'users/omniauth_callbacks'}
|
425
|
+
|
426
|
+
scope '/(:locale)', locale: /ru|en/ do
|
427
|
+
devise_for :users, skip: :omniauth_callbacks
|
428
|
+
end
|
420
429
|
ERROR
|
421
430
|
end
|
422
431
|
|
@@ -435,26 +444,23 @@ ERROR
|
|
435
444
|
|
436
445
|
match "#{path_prefix}/:action/callback",
|
437
446
|
constraints: { action: providers },
|
438
|
-
to: controllers[:omniauth_callbacks],
|
447
|
+
to: "#{controllers[:omniauth_callbacks]}#:action",
|
439
448
|
as: :omniauth_callback,
|
440
449
|
via: [:get, :post]
|
441
450
|
ensure
|
442
451
|
@scope[:path] = path
|
443
452
|
end
|
444
453
|
|
445
|
-
DEVISE_SCOPE_KEYS = [:as, :path, :module, :constraints, :defaults, :options]
|
446
|
-
|
447
454
|
def with_devise_exclusive_scope(new_path, new_as, options) #:nodoc:
|
448
|
-
|
449
|
-
DEVISE_SCOPE_KEYS.each { |k| old[k] = @scope[k] }
|
455
|
+
current_scope = @scope.dup
|
450
456
|
|
451
|
-
|
452
|
-
|
457
|
+
exclusive = { as: new_as, path: new_path, module: nil }
|
458
|
+
exclusive.merge!(options.slice(:constraints, :defaults, :options))
|
453
459
|
|
454
|
-
@scope
|
460
|
+
exclusive.each_pair { |key, value| @scope[key] = value }
|
455
461
|
yield
|
456
462
|
ensure
|
457
|
-
@scope
|
463
|
+
@scope = current_scope
|
458
464
|
end
|
459
465
|
|
460
466
|
def constraints_for(method_to_apply, scope=nil, block=nil)
|
data/lib/devise/rails.rb
CHANGED
@@ -17,7 +17,7 @@ module Devise
|
|
17
17
|
Devise.include_helpers(Devise::Controllers)
|
18
18
|
end
|
19
19
|
|
20
|
-
initializer "devise.omniauth" do |app|
|
20
|
+
initializer "devise.omniauth", after: :load_config_initializers, before: :build_middleware_stack do |app|
|
21
21
|
Devise.omniauth_configs.each do |provider, config|
|
22
22
|
app.middleware.use config.strategy_class, *config.args do |strategy|
|
23
23
|
config.strategy = strategy
|