devise 3.2.4 → 4.7.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 +5 -5
- data/CHANGELOG.md +259 -994
- data/MIT-LICENSE +1 -1
- data/README.md +336 -99
- data/app/controllers/devise/confirmations_controller.rb +9 -3
- data/app/controllers/devise/omniauth_callbacks_controller.rb +12 -6
- data/app/controllers/devise/passwords_controller.rb +19 -6
- data/app/controllers/devise/registrations_controller.rb +55 -22
- data/app/controllers/devise/sessions_controller.rb +44 -14
- data/app/controllers/devise/unlocks_controller.rb +7 -2
- data/app/controllers/devise_controller.rb +65 -29
- data/app/helpers/devise_helper.rb +12 -19
- data/app/mailers/devise/mailer.rb +10 -0
- data/app/views/devise/confirmations/new.html.erb +8 -4
- data/app/views/devise/mailer/email_changed.html.erb +7 -0
- data/app/views/devise/mailer/password_change.html.erb +3 -0
- data/app/views/devise/passwords/edit.html.erb +15 -6
- data/app/views/devise/passwords/new.html.erb +8 -4
- data/app/views/devise/registrations/edit.html.erb +27 -13
- data/app/views/devise/registrations/new.html.erb +19 -8
- data/app/views/devise/sessions/new.html.erb +18 -9
- data/app/views/devise/shared/_error_messages.html.erb +15 -0
- data/app/views/devise/shared/{_links.erb → _links.html.erb} +9 -9
- data/app/views/devise/unlocks/new.html.erb +8 -4
- data/config/locales/en.yml +22 -16
- data/lib/devise/controllers/helpers.rb +109 -29
- data/lib/devise/controllers/rememberable.rb +12 -3
- data/lib/devise/controllers/scoped_views.rb +2 -0
- data/lib/devise/controllers/sign_in_out.rb +36 -20
- data/lib/devise/controllers/store_location.rb +31 -5
- data/lib/devise/controllers/url_helpers.rb +9 -7
- data/lib/devise/delegator.rb +2 -0
- data/lib/devise/encryptor.rb +24 -0
- data/lib/devise/failure_app.rb +116 -36
- data/lib/devise/hooks/activatable.rb +5 -4
- data/lib/devise/hooks/csrf_cleaner.rb +5 -1
- data/lib/devise/hooks/forgetable.rb +2 -0
- data/lib/devise/hooks/lockable.rb +6 -1
- data/lib/devise/hooks/proxy.rb +3 -1
- data/lib/devise/hooks/rememberable.rb +2 -0
- data/lib/devise/hooks/timeoutable.rb +15 -8
- data/lib/devise/hooks/trackable.rb +2 -0
- data/lib/devise/mailers/helpers.rb +7 -4
- data/lib/devise/mapping.rb +8 -2
- data/lib/devise/models/authenticatable.rb +76 -51
- data/lib/devise/models/confirmable.rb +129 -34
- data/lib/devise/models/database_authenticatable.rb +107 -30
- data/lib/devise/models/lockable.rb +19 -9
- data/lib/devise/models/omniauthable.rb +2 -0
- data/lib/devise/models/recoverable.rb +62 -26
- data/lib/devise/models/registerable.rb +4 -0
- data/lib/devise/models/rememberable.rb +58 -29
- data/lib/devise/models/timeoutable.rb +2 -6
- data/lib/devise/models/trackable.rb +20 -4
- data/lib/devise/models/validatable.rb +12 -5
- data/lib/devise/models.rb +3 -1
- data/lib/devise/modules.rb +2 -0
- data/lib/devise/omniauth/config.rb +2 -0
- data/lib/devise/omniauth/url_helpers.rb +14 -5
- data/lib/devise/omniauth.rb +2 -0
- data/lib/devise/orm/active_record.rb +5 -1
- data/lib/devise/orm/mongoid.rb +6 -2
- data/lib/devise/parameter_filter.rb +4 -0
- data/lib/devise/parameter_sanitizer.rb +139 -65
- data/lib/devise/rails/routes.rb +80 -61
- data/lib/devise/rails/warden_compat.rb +3 -10
- data/lib/devise/rails.rb +8 -17
- data/lib/devise/secret_key_finder.rb +27 -0
- data/lib/devise/strategies/authenticatable.rb +18 -7
- data/lib/devise/strategies/base.rb +2 -0
- data/lib/devise/strategies/database_authenticatable.rb +13 -5
- data/lib/devise/strategies/rememberable.rb +15 -3
- data/lib/devise/test/controller_helpers.rb +165 -0
- data/lib/devise/test/integration_helpers.rb +63 -0
- data/lib/devise/test_helpers.rb +7 -124
- data/lib/devise/time_inflector.rb +2 -0
- data/lib/devise/token_generator.rb +3 -41
- data/lib/devise/version.rb +3 -1
- data/lib/devise.rb +106 -79
- data/lib/generators/active_record/devise_generator.rb +44 -7
- data/lib/generators/active_record/templates/migration.rb +5 -3
- data/lib/generators/active_record/templates/migration_existing.rb +5 -3
- data/lib/generators/devise/controllers_generator.rb +46 -0
- data/lib/generators/devise/devise_generator.rb +4 -2
- data/lib/generators/devise/install_generator.rb +17 -0
- data/lib/generators/devise/orm_helpers.rb +10 -21
- data/lib/generators/devise/views_generator.rb +21 -11
- data/lib/generators/mongoid/devise_generator.rb +7 -5
- data/lib/generators/templates/README +2 -9
- data/lib/generators/templates/controllers/README +14 -0
- data/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
- data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
- data/lib/generators/templates/controllers/passwords_controller.rb +34 -0
- data/lib/generators/templates/controllers/registrations_controller.rb +62 -0
- data/lib/generators/templates/controllers/sessions_controller.rb +27 -0
- data/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
- data/lib/generators/templates/devise.rb +69 -30
- data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
- data/lib/generators/templates/markerb/email_changed.markerb +7 -0
- 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/confirmations/new.html.erb +5 -1
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +10 -2
- data/lib/generators/templates/simple_form_for/passwords/new.html.erb +4 -1
- data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +11 -3
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +11 -3
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +9 -4
- data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +4 -1
- metadata +31 -259
- data/.gitignore +0 -11
- data/.travis.yml +0 -28
- data/.yardopts +0 -9
- data/CONTRIBUTING.md +0 -14
- data/Gemfile +0 -29
- data/Gemfile.lock +0 -160
- data/Rakefile +0 -35
- data/devise.gemspec +0 -27
- data/devise.png +0 -0
- data/gemfiles/Gemfile.rails-3.2-stable +0 -29
- data/gemfiles/Gemfile.rails-4.0-stable +0 -29
- data/gemfiles/Gemfile.rails-head +0 -29
- data/test/controllers/custom_strategy_test.rb +0 -62
- data/test/controllers/helpers_test.rb +0 -276
- data/test/controllers/internal_helpers_test.rb +0 -123
- data/test/controllers/passwords_controller_test.rb +0 -31
- data/test/controllers/sessions_controller_test.rb +0 -103
- data/test/controllers/url_helpers_test.rb +0 -59
- data/test/delegator_test.rb +0 -19
- data/test/devise_test.rb +0 -94
- data/test/failure_app_test.rb +0 -232
- data/test/generators/active_record_generator_test.rb +0 -103
- data/test/generators/devise_generator_test.rb +0 -39
- data/test/generators/install_generator_test.rb +0 -13
- data/test/generators/mongoid_generator_test.rb +0 -23
- data/test/generators/views_generator_test.rb +0 -96
- data/test/helpers/devise_helper_test.rb +0 -51
- data/test/integration/authenticatable_test.rb +0 -713
- data/test/integration/confirmable_test.rb +0 -284
- data/test/integration/database_authenticatable_test.rb +0 -84
- data/test/integration/http_authenticatable_test.rb +0 -105
- data/test/integration/lockable_test.rb +0 -239
- data/test/integration/omniauthable_test.rb +0 -133
- data/test/integration/recoverable_test.rb +0 -334
- data/test/integration/registerable_test.rb +0 -349
- data/test/integration/rememberable_test.rb +0 -167
- data/test/integration/timeoutable_test.rb +0 -183
- data/test/integration/trackable_test.rb +0 -92
- data/test/mailers/confirmation_instructions_test.rb +0 -115
- data/test/mailers/reset_password_instructions_test.rb +0 -96
- data/test/mailers/unlock_instructions_test.rb +0 -91
- data/test/mapping_test.rb +0 -127
- data/test/models/authenticatable_test.rb +0 -13
- data/test/models/confirmable_test.rb +0 -454
- data/test/models/database_authenticatable_test.rb +0 -249
- data/test/models/lockable_test.rb +0 -316
- data/test/models/omniauthable_test.rb +0 -7
- data/test/models/recoverable_test.rb +0 -184
- data/test/models/registerable_test.rb +0 -7
- data/test/models/rememberable_test.rb +0 -183
- data/test/models/serializable_test.rb +0 -49
- data/test/models/timeoutable_test.rb +0 -51
- data/test/models/trackable_test.rb +0 -13
- data/test/models/validatable_test.rb +0 -127
- data/test/models_test.rb +0 -144
- data/test/omniauth/config_test.rb +0 -57
- data/test/omniauth/url_helpers_test.rb +0 -54
- data/test/orm/active_record.rb +0 -10
- data/test/orm/mongoid.rb +0 -13
- data/test/parameter_sanitizer_test.rb +0 -81
- data/test/rails_app/Rakefile +0 -6
- data/test/rails_app/app/active_record/admin.rb +0 -6
- data/test/rails_app/app/active_record/shim.rb +0 -2
- data/test/rails_app/app/active_record/user.rb +0 -6
- data/test/rails_app/app/controllers/admins/sessions_controller.rb +0 -6
- data/test/rails_app/app/controllers/admins_controller.rb +0 -11
- data/test/rails_app/app/controllers/application_controller.rb +0 -9
- data/test/rails_app/app/controllers/home_controller.rb +0 -25
- data/test/rails_app/app/controllers/publisher/registrations_controller.rb +0 -2
- data/test/rails_app/app/controllers/publisher/sessions_controller.rb +0 -2
- data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +0 -14
- data/test/rails_app/app/controllers/users_controller.rb +0 -31
- data/test/rails_app/app/helpers/application_helper.rb +0 -3
- data/test/rails_app/app/mailers/users/mailer.rb +0 -12
- data/test/rails_app/app/mongoid/admin.rb +0 -29
- data/test/rails_app/app/mongoid/shim.rb +0 -23
- data/test/rails_app/app/mongoid/user.rb +0 -39
- data/test/rails_app/app/views/admins/index.html.erb +0 -1
- data/test/rails_app/app/views/admins/sessions/new.html.erb +0 -2
- data/test/rails_app/app/views/home/admin_dashboard.html.erb +0 -1
- data/test/rails_app/app/views/home/index.html.erb +0 -1
- data/test/rails_app/app/views/home/join.html.erb +0 -1
- data/test/rails_app/app/views/home/private.html.erb +0 -1
- data/test/rails_app/app/views/home/user_dashboard.html.erb +0 -1
- data/test/rails_app/app/views/layouts/application.html.erb +0 -24
- data/test/rails_app/app/views/users/edit_form.html.erb +0 -1
- data/test/rails_app/app/views/users/index.html.erb +0 -1
- data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +0 -1
- data/test/rails_app/app/views/users/sessions/new.html.erb +0 -1
- data/test/rails_app/bin/bundle +0 -3
- data/test/rails_app/bin/rails +0 -4
- data/test/rails_app/bin/rake +0 -4
- data/test/rails_app/config/application.rb +0 -40
- data/test/rails_app/config/boot.rb +0 -14
- data/test/rails_app/config/database.yml +0 -18
- data/test/rails_app/config/environment.rb +0 -5
- data/test/rails_app/config/environments/development.rb +0 -30
- data/test/rails_app/config/environments/production.rb +0 -80
- data/test/rails_app/config/environments/test.rb +0 -36
- data/test/rails_app/config/initializers/backtrace_silencers.rb +0 -7
- data/test/rails_app/config/initializers/devise.rb +0 -181
- data/test/rails_app/config/initializers/inflections.rb +0 -2
- data/test/rails_app/config/initializers/secret_token.rb +0 -8
- data/test/rails_app/config/initializers/session_store.rb +0 -1
- data/test/rails_app/config/routes.rb +0 -105
- data/test/rails_app/config.ru +0 -4
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -71
- data/test/rails_app/db/schema.rb +0 -55
- data/test/rails_app/lib/shared_admin.rb +0 -17
- data/test/rails_app/lib/shared_user.rb +0 -29
- data/test/rails_app/public/404.html +0 -26
- data/test/rails_app/public/422.html +0 -26
- data/test/rails_app/public/500.html +0 -26
- data/test/rails_app/public/favicon.ico +0 -0
- data/test/routes_test.rb +0 -262
- data/test/support/action_controller/record_identifier.rb +0 -10
- data/test/support/assertions.rb +0 -40
- data/test/support/helpers.rb +0 -70
- data/test/support/integration.rb +0 -92
- data/test/support/locale/en.yml +0 -8
- data/test/support/mongoid.yml +0 -6
- data/test/support/webrat/integrations/rails.rb +0 -24
- data/test/test_helper.rb +0 -27
- data/test/test_helpers_test.rb +0 -173
- data/test/test_models.rb +0 -33
|
@@ -1,25 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'devise/strategies/database_authenticatable'
|
|
2
|
-
require 'bcrypt'
|
|
3
4
|
|
|
4
5
|
module Devise
|
|
5
|
-
# Digests the password using bcrypt.
|
|
6
|
-
def self.bcrypt(klass, password)
|
|
7
|
-
::BCrypt::Password.create("#{password}#{klass.pepper}", cost: klass.stretches).to_s
|
|
8
|
-
end
|
|
9
|
-
|
|
10
6
|
module Models
|
|
11
|
-
# Authenticatable Module, responsible for
|
|
12
|
-
# authenticity of a user while signing in.
|
|
7
|
+
# Authenticatable Module, responsible for hashing the password and
|
|
8
|
+
# validating the authenticity of a user while signing in.
|
|
13
9
|
#
|
|
14
10
|
# == Options
|
|
15
11
|
#
|
|
16
|
-
#
|
|
12
|
+
# DatabaseAuthenticatable adds the following options to devise_for:
|
|
17
13
|
#
|
|
18
14
|
# * +pepper+: a random string used to provide a more secure hash. Use
|
|
19
|
-
# `
|
|
15
|
+
# `rails secret` to generate new keys.
|
|
20
16
|
#
|
|
21
17
|
# * +stretches+: the cost given to bcrypt.
|
|
22
18
|
#
|
|
19
|
+
# * +send_email_changed_notification+: notify original email when it changes.
|
|
20
|
+
#
|
|
21
|
+
# * +send_password_change_notification+: notify email when password changes.
|
|
22
|
+
#
|
|
23
23
|
# == Examples
|
|
24
24
|
#
|
|
25
25
|
# User.find(1).valid_password?('password123') # returns true/false
|
|
@@ -28,26 +28,44 @@ module Devise
|
|
|
28
28
|
extend ActiveSupport::Concern
|
|
29
29
|
|
|
30
30
|
included do
|
|
31
|
+
after_update :send_email_changed_notification, if: :send_email_changed_notification?
|
|
32
|
+
after_update :send_password_change_notification, if: :send_password_change_notification?
|
|
33
|
+
|
|
31
34
|
attr_reader :password, :current_password
|
|
32
35
|
attr_accessor :password_confirmation
|
|
33
36
|
end
|
|
34
37
|
|
|
38
|
+
def initialize(*args, &block)
|
|
39
|
+
@skip_email_changed_notification = false
|
|
40
|
+
@skip_password_change_notification = false
|
|
41
|
+
super
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Skips sending the email changed notification after_update
|
|
45
|
+
def skip_email_changed_notification!
|
|
46
|
+
@skip_email_changed_notification = true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Skips sending the password change notification after_update
|
|
50
|
+
def skip_password_change_notification!
|
|
51
|
+
@skip_password_change_notification = true
|
|
52
|
+
end
|
|
53
|
+
|
|
35
54
|
def self.required_fields(klass)
|
|
36
55
|
[:encrypted_password] + klass.authentication_keys
|
|
37
56
|
end
|
|
38
57
|
|
|
39
|
-
# Generates password
|
|
58
|
+
# Generates a hashed password based on the given value.
|
|
59
|
+
# For legacy reasons, we use `encrypted_password` to store
|
|
60
|
+
# the hashed password.
|
|
40
61
|
def password=(new_password)
|
|
41
62
|
@password = new_password
|
|
42
63
|
self.encrypted_password = password_digest(@password) if @password.present?
|
|
43
64
|
end
|
|
44
65
|
|
|
45
|
-
# Verifies whether
|
|
66
|
+
# Verifies whether a password (ie from sign in) is the user password.
|
|
46
67
|
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)
|
|
68
|
+
Devise::Encryptor.compare(self.class, encrypted_password, password)
|
|
51
69
|
end
|
|
52
70
|
|
|
53
71
|
# Set password and password confirmation to nil
|
|
@@ -55,10 +73,23 @@ module Devise
|
|
|
55
73
|
self.password = self.password_confirmation = nil
|
|
56
74
|
end
|
|
57
75
|
|
|
58
|
-
# Update record attributes when :current_password matches, otherwise
|
|
59
|
-
# error on :current_password.
|
|
60
|
-
#
|
|
76
|
+
# Update record attributes when :current_password matches, otherwise
|
|
77
|
+
# returns error on :current_password.
|
|
78
|
+
#
|
|
79
|
+
# This method also rejects the password field if it is blank (allowing
|
|
80
|
+
# users to change relevant information like the e-mail without changing
|
|
81
|
+
# their password). In case the password field is rejected, the confirmation
|
|
82
|
+
# is also rejected as long as it is also blank.
|
|
61
83
|
def update_with_password(params, *options)
|
|
84
|
+
if options.present?
|
|
85
|
+
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
|
|
86
|
+
[Devise] The second argument of `DatabaseAuthenticatable#update_with_password`
|
|
87
|
+
(`options`) is deprecated and it will be removed in the next major version.
|
|
88
|
+
It was added to support a feature deprecated in Rails 4, so you can safely remove it
|
|
89
|
+
from your code.
|
|
90
|
+
DEPRECATION
|
|
91
|
+
end
|
|
92
|
+
|
|
62
93
|
current_password = params.delete(:current_password)
|
|
63
94
|
|
|
64
95
|
if params[:password].blank?
|
|
@@ -67,11 +98,11 @@ module Devise
|
|
|
67
98
|
end
|
|
68
99
|
|
|
69
100
|
result = if valid_password?(current_password)
|
|
70
|
-
|
|
101
|
+
update(params, *options)
|
|
71
102
|
else
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
103
|
+
assign_attributes(params, *options)
|
|
104
|
+
valid?
|
|
105
|
+
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
75
106
|
false
|
|
76
107
|
end
|
|
77
108
|
|
|
@@ -92,10 +123,19 @@ module Devise
|
|
|
92
123
|
# end
|
|
93
124
|
#
|
|
94
125
|
def update_without_password(params, *options)
|
|
126
|
+
if options.present?
|
|
127
|
+
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
|
|
128
|
+
[Devise] The second argument of `DatabaseAuthenticatable#update_without_password`
|
|
129
|
+
(`options`) is deprecated and it will be removed in the next major version.
|
|
130
|
+
It was added to support a feature deprecated in Rails 4, so you can safely remove it
|
|
131
|
+
from your code.
|
|
132
|
+
DEPRECATION
|
|
133
|
+
end
|
|
134
|
+
|
|
95
135
|
params.delete(:password)
|
|
96
136
|
params.delete(:password_confirmation)
|
|
97
137
|
|
|
98
|
-
result =
|
|
138
|
+
result = update(params, *options)
|
|
99
139
|
clean_up_passwords
|
|
100
140
|
result
|
|
101
141
|
end
|
|
@@ -107,8 +147,8 @@ module Devise
|
|
|
107
147
|
result = if valid_password?(current_password)
|
|
108
148
|
destroy
|
|
109
149
|
else
|
|
110
|
-
|
|
111
|
-
|
|
150
|
+
valid?
|
|
151
|
+
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
112
152
|
false
|
|
113
153
|
end
|
|
114
154
|
|
|
@@ -133,19 +173,56 @@ module Devise
|
|
|
133
173
|
encrypted_password[0,29] if encrypted_password
|
|
134
174
|
end
|
|
135
175
|
|
|
176
|
+
if Devise.activerecord51?
|
|
177
|
+
# Send notification to user when email changes.
|
|
178
|
+
def send_email_changed_notification
|
|
179
|
+
send_devise_notification(:email_changed, to: email_before_last_save)
|
|
180
|
+
end
|
|
181
|
+
else
|
|
182
|
+
# Send notification to user when email changes.
|
|
183
|
+
def send_email_changed_notification
|
|
184
|
+
send_devise_notification(:email_changed, to: email_was)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Send notification to user when password changes.
|
|
189
|
+
def send_password_change_notification
|
|
190
|
+
send_devise_notification(:password_change)
|
|
191
|
+
end
|
|
192
|
+
|
|
136
193
|
protected
|
|
137
194
|
|
|
138
|
-
#
|
|
195
|
+
# Hashes the password using bcrypt. Custom hash functions should override
|
|
139
196
|
# this method to apply their own algorithm.
|
|
140
197
|
#
|
|
141
198
|
# See https://github.com/plataformatec/devise-encryptable for examples
|
|
142
|
-
# of other
|
|
199
|
+
# of other hashing engines.
|
|
143
200
|
def password_digest(password)
|
|
144
|
-
Devise.
|
|
201
|
+
Devise::Encryptor.digest(self.class, password)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
if Devise.activerecord51?
|
|
205
|
+
def send_email_changed_notification?
|
|
206
|
+
self.class.send_email_changed_notification && saved_change_to_email? && !@skip_email_changed_notification
|
|
207
|
+
end
|
|
208
|
+
else
|
|
209
|
+
def send_email_changed_notification?
|
|
210
|
+
self.class.send_email_changed_notification && email_changed? && !@skip_email_changed_notification
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
if Devise.activerecord51?
|
|
215
|
+
def send_password_change_notification?
|
|
216
|
+
self.class.send_password_change_notification && saved_change_to_encrypted_password? && !@skip_password_change_notification
|
|
217
|
+
end
|
|
218
|
+
else
|
|
219
|
+
def send_password_change_notification?
|
|
220
|
+
self.class.send_password_change_notification && encrypted_password_changed? && !@skip_password_change_notification
|
|
221
|
+
end
|
|
145
222
|
end
|
|
146
223
|
|
|
147
224
|
module ClassMethods
|
|
148
|
-
Devise::Models.config(self, :pepper, :stretches)
|
|
225
|
+
Devise::Models.config(self, :pepper, :stretches, :send_email_changed_notification, :send_password_change_notification)
|
|
149
226
|
|
|
150
227
|
# We assume this method already gets the sanitized values from the
|
|
151
228
|
# DatabaseAuthenticatable strategy. If you are using this method on
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "devise/hooks/lockable"
|
|
2
4
|
|
|
3
5
|
module Devise
|
|
@@ -7,7 +9,7 @@ module Devise
|
|
|
7
9
|
# blocked: email and time. The former will send an email to the user when
|
|
8
10
|
# the lock happens, containing a link to unlock its account. The second
|
|
9
11
|
# will unlock the user automatically after some configured time (ie 2.hours).
|
|
10
|
-
# It's also possible to
|
|
12
|
+
# It's also possible to set up lockable to use both email and time strategies.
|
|
11
13
|
#
|
|
12
14
|
# == Options
|
|
13
15
|
#
|
|
@@ -64,7 +66,7 @@ module Devise
|
|
|
64
66
|
def send_unlock_instructions
|
|
65
67
|
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
|
|
66
68
|
self.unlock_token = enc
|
|
67
|
-
|
|
69
|
+
save(validate: false)
|
|
68
70
|
send_devise_notification(:unlock_instructions, raw, {})
|
|
69
71
|
raw
|
|
70
72
|
end
|
|
@@ -99,8 +101,7 @@ module Devise
|
|
|
99
101
|
if super && !access_locked?
|
|
100
102
|
true
|
|
101
103
|
else
|
|
102
|
-
|
|
103
|
-
self.failed_attempts += 1
|
|
104
|
+
increment_failed_attempts
|
|
104
105
|
if attempts_exceeded?
|
|
105
106
|
lock_access! unless access_locked?
|
|
106
107
|
else
|
|
@@ -109,16 +110,21 @@ module Devise
|
|
|
109
110
|
false
|
|
110
111
|
end
|
|
111
112
|
end
|
|
113
|
+
|
|
114
|
+
def increment_failed_attempts
|
|
115
|
+
self.class.increment_counter(:failed_attempts, id)
|
|
116
|
+
reload
|
|
117
|
+
end
|
|
112
118
|
|
|
113
119
|
def unauthenticated_message
|
|
114
120
|
# If set to paranoid mode, do not show the locked message because it
|
|
115
121
|
# leaks the existence of an account.
|
|
116
122
|
if Devise.paranoid
|
|
117
123
|
super
|
|
118
|
-
elsif lock_strategy_enabled?(:failed_attempts) &&
|
|
119
|
-
:last_attempt
|
|
120
|
-
elsif lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?
|
|
124
|
+
elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?)
|
|
121
125
|
:locked
|
|
126
|
+
elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? && self.class.last_attempt_warning
|
|
127
|
+
:last_attempt
|
|
122
128
|
else
|
|
123
129
|
super
|
|
124
130
|
end
|
|
@@ -155,6 +161,9 @@ module Devise
|
|
|
155
161
|
end
|
|
156
162
|
|
|
157
163
|
module ClassMethods
|
|
164
|
+
# List of strategies that are enabled/supported if :both is used.
|
|
165
|
+
BOTH_STRATEGIES = [:time, :email]
|
|
166
|
+
|
|
158
167
|
# Attempt to find a user by its unlock keys. If a record is found, send new
|
|
159
168
|
# unlock instructions to it. If not user is found, returns a new user
|
|
160
169
|
# with an email not found error.
|
|
@@ -181,7 +190,8 @@ module Devise
|
|
|
181
190
|
|
|
182
191
|
# Is the unlock enabled for the given unlock strategy?
|
|
183
192
|
def unlock_strategy_enabled?(strategy)
|
|
184
|
-
|
|
193
|
+
self.unlock_strategy == strategy ||
|
|
194
|
+
(self.unlock_strategy == :both && BOTH_STRATEGIES.include?(strategy))
|
|
185
195
|
end
|
|
186
196
|
|
|
187
197
|
# Is the lock enabled for the given lock strategy?
|
|
@@ -189,7 +199,7 @@ module Devise
|
|
|
189
199
|
self.lock_strategy == strategy
|
|
190
200
|
end
|
|
191
201
|
|
|
192
|
-
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
|
|
202
|
+
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys, :last_attempt_warning)
|
|
193
203
|
end
|
|
194
204
|
end
|
|
195
205
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Devise
|
|
2
4
|
module Models
|
|
3
5
|
|
|
@@ -8,15 +10,13 @@ module Devise
|
|
|
8
10
|
# Recoverable adds the following options to devise_for:
|
|
9
11
|
#
|
|
10
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.
|
|
11
15
|
#
|
|
12
16
|
# == Examples
|
|
13
17
|
#
|
|
14
18
|
# # 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')
|
|
19
|
+
# User.find(1).reset_password('password123', 'password123')
|
|
20
20
|
#
|
|
21
21
|
# # creates a new token and send it with instructions about how to reset the password
|
|
22
22
|
# User.find(1).send_reset_password_instructions
|
|
@@ -28,31 +28,30 @@ module Devise
|
|
|
28
28
|
[:reset_password_sent_at, :reset_password_token]
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
included do
|
|
32
|
+
before_update :clear_reset_password_token, if: :clear_reset_password_token?
|
|
33
|
+
end
|
|
34
|
+
|
|
31
35
|
# Update password saving the record and clearing token. Returns true if
|
|
32
36
|
# the passwords are valid and the record was saved, false otherwise.
|
|
33
|
-
def reset_password
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
40
45
|
end
|
|
41
|
-
|
|
42
|
-
save
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
# Resets reset password token and send reset password instructions by email.
|
|
46
49
|
# Returns the token sent in the e-mail.
|
|
47
50
|
def send_reset_password_instructions
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
self.reset_password_token = enc
|
|
51
|
-
self.reset_password_sent_at = Time.now.utc
|
|
52
|
-
self.save(validate: false)
|
|
51
|
+
token = set_reset_password_token
|
|
52
|
+
send_reset_password_instructions_notification(token)
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
raw
|
|
54
|
+
token
|
|
56
55
|
end
|
|
57
56
|
|
|
58
57
|
# Checks if the reset password token sent is within the limit time.
|
|
@@ -76,7 +75,7 @@ module Devise
|
|
|
76
75
|
# reset_password_period_valid? # will always return false
|
|
77
76
|
#
|
|
78
77
|
def reset_password_period_valid?
|
|
79
|
-
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
|
|
78
|
+
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago.utc
|
|
80
79
|
end
|
|
81
80
|
|
|
82
81
|
protected
|
|
@@ -87,10 +86,47 @@ module Devise
|
|
|
87
86
|
self.reset_password_sent_at = nil
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
def
|
|
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
|
+
if Devise.activerecord51?
|
|
103
|
+
def clear_reset_password_token?
|
|
104
|
+
encrypted_password_changed = respond_to?(:will_save_change_to_encrypted_password?) && will_save_change_to_encrypted_password?
|
|
105
|
+
authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
|
|
106
|
+
respond_to?("will_save_change_to_#{attribute}?") && send("will_save_change_to_#{attribute}?")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
authentication_keys_changed || encrypted_password_changed
|
|
110
|
+
end
|
|
111
|
+
else
|
|
112
|
+
def clear_reset_password_token?
|
|
113
|
+
encrypted_password_changed = respond_to?(:encrypted_password_changed?) && encrypted_password_changed?
|
|
114
|
+
authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
|
|
115
|
+
respond_to?("#{attribute}_changed?") && send("#{attribute}_changed?")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
authentication_keys_changed || encrypted_password_changed
|
|
119
|
+
end
|
|
91
120
|
end
|
|
92
121
|
|
|
93
122
|
module ClassMethods
|
|
123
|
+
# Attempt to find a user by password reset token. If a user is found, return it
|
|
124
|
+
# If a user is not found, return nil
|
|
125
|
+
def with_reset_password_token(token)
|
|
126
|
+
reset_password_token = Devise.token_generator.digest(self, :reset_password_token, token)
|
|
127
|
+
to_adapter.find_first(reset_password_token: reset_password_token)
|
|
128
|
+
end
|
|
129
|
+
|
|
94
130
|
# Attempt to find a user by its email. If a record is found, send new
|
|
95
131
|
# password instructions to it. If user is not found, returns a new user
|
|
96
132
|
# with an email not found error.
|
|
@@ -114,17 +150,17 @@ module Devise
|
|
|
114
150
|
|
|
115
151
|
if recoverable.persisted?
|
|
116
152
|
if recoverable.reset_password_period_valid?
|
|
117
|
-
recoverable.reset_password
|
|
153
|
+
recoverable.reset_password(attributes[:password], attributes[:password_confirmation])
|
|
118
154
|
else
|
|
119
155
|
recoverable.errors.add(:reset_password_token, :expired)
|
|
120
156
|
end
|
|
121
157
|
end
|
|
122
158
|
|
|
123
|
-
recoverable.reset_password_token = original_token
|
|
159
|
+
recoverable.reset_password_token = original_token if recoverable.reset_password_token.present?
|
|
124
160
|
recoverable
|
|
125
161
|
end
|
|
126
162
|
|
|
127
|
-
Devise::Models.config(self, :reset_password_keys, :reset_password_within)
|
|
163
|
+
Devise::Models.config(self, :reset_password_keys, :reset_password_within, :sign_in_after_reset_password)
|
|
128
164
|
end
|
|
129
165
|
end
|
|
130
166
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Devise
|
|
2
4
|
module Models
|
|
3
5
|
# Registerable is responsible for everything related to registering a new
|
|
@@ -19,6 +21,8 @@ module Devise
|
|
|
19
21
|
def new_with_session(params, session)
|
|
20
22
|
new(params)
|
|
21
23
|
end
|
|
24
|
+
|
|
25
|
+
Devise::Models.config(self, :sign_in_after_change_password)
|
|
22
26
|
end
|
|
23
27
|
end
|
|
24
28
|
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'devise/strategies/rememberable'
|
|
2
4
|
require 'devise/hooks/rememberable'
|
|
3
5
|
require 'devise/hooks/forgetable'
|
|
4
6
|
|
|
5
7
|
module Devise
|
|
6
8
|
module Models
|
|
7
|
-
# Rememberable manages generating and clearing token for
|
|
9
|
+
# Rememberable manages generating and clearing token for remembering the user
|
|
8
10
|
# from a saved cookie. Rememberable also has utility methods for dealing
|
|
9
11
|
# with serializing the user into the cookie and back from the cookie, trying
|
|
10
12
|
# to lookup the record based on the saved information.
|
|
@@ -39,17 +41,15 @@ module Devise
|
|
|
39
41
|
module Rememberable
|
|
40
42
|
extend ActiveSupport::Concern
|
|
41
43
|
|
|
42
|
-
attr_accessor :remember_me
|
|
44
|
+
attr_accessor :remember_me
|
|
43
45
|
|
|
44
46
|
def self.required_fields(klass)
|
|
45
47
|
[:remember_created_at]
|
|
46
48
|
end
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
self.remember_token = self.class.remember_token if generate_remember_token?
|
|
52
|
-
self.remember_created_at = Time.now.utc if generate_remember_timestamp?(extend_period)
|
|
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,28 +57,26 @@ 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
|
|
61
|
-
self.remember_created_at = nil
|
|
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
62
|
save(validate: false)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
remember_created_at.nil? || (remember_expires_at <= Time.now.utc)
|
|
65
|
+
def remember_expires_at
|
|
66
|
+
self.class.remember_for.from_now
|
|
68
67
|
end
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
remember_created_at + self.class.remember_for
|
|
69
|
+
def extend_remember_period
|
|
70
|
+
self.class.extend_remember_period
|
|
73
71
|
end
|
|
74
72
|
|
|
75
73
|
def rememberable_value
|
|
76
74
|
if respond_to?(:remember_token)
|
|
77
75
|
remember_token
|
|
78
|
-
elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt)
|
|
76
|
+
elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
|
|
79
77
|
salt
|
|
80
78
|
else
|
|
81
|
-
raise "
|
|
79
|
+
raise "authenticatable_salt returned nil for the #{self.class.name} model. " \
|
|
82
80
|
"In order to use rememberable, you must ensure a password is always set " \
|
|
83
81
|
"or have a remember_token column in your model or implement your own " \
|
|
84
82
|
"rememberable_value in the model with custom logic."
|
|
@@ -89,29 +87,60 @@ module Devise
|
|
|
89
87
|
self.class.rememberable_options
|
|
90
88
|
end
|
|
91
89
|
|
|
92
|
-
|
|
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/plataformatec/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
|
|
93
109
|
|
|
94
|
-
|
|
95
|
-
|
|
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)
|
|
96
120
|
end
|
|
97
121
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def
|
|
101
|
-
|
|
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
|
|
102
130
|
end
|
|
103
131
|
|
|
104
132
|
module ClassMethods
|
|
105
133
|
# Create the cookie key using the record id and remember_token
|
|
106
134
|
def serialize_into_cookie(record)
|
|
107
|
-
[record.to_key, record.rememberable_value]
|
|
135
|
+
[record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
|
|
108
136
|
end
|
|
109
137
|
|
|
110
138
|
# Recreate the user based on the stored cookie
|
|
111
|
-
def serialize_from_cookie(
|
|
139
|
+
def serialize_from_cookie(*args)
|
|
140
|
+
id, token, generated_at = *args
|
|
141
|
+
|
|
112
142
|
record = to_adapter.get(id)
|
|
113
|
-
record if record &&
|
|
114
|
-
Devise.secure_compare(record.rememberable_value, remember_token)
|
|
143
|
+
record if record && record.remember_me?(token, generated_at)
|
|
115
144
|
end
|
|
116
145
|
|
|
117
146
|
# Generate a token checking if one does not already exist in the database.
|
|
@@ -122,7 +151,7 @@ module Devise
|
|
|
122
151
|
end
|
|
123
152
|
end
|
|
124
153
|
|
|
125
|
-
Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options)
|
|
154
|
+
Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options, :expire_all_remember_me_on_sign_out)
|
|
126
155
|
end
|
|
127
156
|
end
|
|
128
157
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'devise/hooks/timeoutable'
|
|
2
4
|
|
|
3
5
|
module Devise
|
|
@@ -26,7 +28,6 @@ module Devise
|
|
|
26
28
|
|
|
27
29
|
# Checks whether the user session has expired based on configured time.
|
|
28
30
|
def timedout?(last_access)
|
|
29
|
-
return false if remember_exists_and_not_expired?
|
|
30
31
|
!timeout_in.nil? && last_access && last_access <= timeout_in.ago
|
|
31
32
|
end
|
|
32
33
|
|
|
@@ -36,11 +37,6 @@ module Devise
|
|
|
36
37
|
|
|
37
38
|
private
|
|
38
39
|
|
|
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
40
|
module ClassMethods
|
|
45
41
|
Devise::Models.config(self, :timeout_in)
|
|
46
42
|
end
|