devise 3.2.2 → 4.8.0
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/CHANGELOG.md +281 -957
- data/MIT-LICENSE +2 -1
- data/README.md +410 -120
- data/app/controllers/devise/confirmations_controller.rb +11 -5
- data/app/controllers/devise/omniauth_callbacks_controller.rb +12 -6
- data/app/controllers/devise/passwords_controller.rb +21 -8
- data/app/controllers/devise/registrations_controller.rb +59 -26
- data/app/controllers/devise/sessions_controller.rb +47 -17
- data/app/controllers/devise/unlocks_controller.rb +9 -4
- data/app/controllers/devise_controller.rb +69 -33
- data/app/helpers/devise_helper.rb +23 -18
- data/app/mailers/devise/mailer.rb +13 -3
- data/app/views/devise/confirmations/new.html.erb +9 -5
- data/app/views/devise/mailer/confirmation_instructions.html.erb +1 -1
- 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/mailer/reset_password_instructions.html.erb +1 -1
- data/app/views/devise/mailer/unlock_instructions.html.erb +1 -1
- data/app/views/devise/passwords/edit.html.erb +16 -7
- data/app/views/devise/passwords/new.html.erb +9 -5
- data/app/views/devise/registrations/edit.html.erb +29 -15
- data/app/views/devise/registrations/new.html.erb +20 -9
- data/app/views/devise/sessions/new.html.erb +19 -10
- 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 +9 -5
- data/config/locales/en.yml +24 -18
- data/lib/devise/controllers/helpers.rb +113 -33
- data/lib/devise/controllers/rememberable.rb +15 -6
- data/lib/devise/controllers/scoped_views.rb +3 -1
- data/lib/devise/controllers/sign_in_out.rb +47 -29
- data/lib/devise/controllers/store_location.rb +31 -5
- data/lib/devise/controllers/url_helpers.rb +10 -8
- data/lib/devise/delegator.rb +2 -0
- data/lib/devise/encryptor.rb +24 -0
- data/lib/devise/failure_app.rb +119 -40
- data/lib/devise/hooks/activatable.rb +7 -6
- data/lib/devise/hooks/csrf_cleaner.rb +5 -1
- data/lib/devise/hooks/forgetable.rb +2 -0
- data/lib/devise/hooks/lockable.rb +5 -3
- data/lib/devise/hooks/proxy.rb +4 -2
- data/lib/devise/hooks/rememberable.rb +4 -2
- data/lib/devise/hooks/timeoutable.rb +16 -9
- data/lib/devise/hooks/trackable.rb +3 -1
- data/lib/devise/mailers/helpers.rb +15 -12
- data/lib/devise/mapping.rb +9 -3
- data/lib/devise/models/authenticatable.rb +91 -61
- data/lib/devise/models/confirmable.rb +138 -43
- data/lib/devise/models/database_authenticatable.rb +112 -31
- data/lib/devise/models/lockable.rb +39 -18
- data/lib/devise/models/omniauthable.rb +3 -1
- data/lib/devise/models/recoverable.rb +64 -28
- data/lib/devise/models/registerable.rb +4 -0
- data/lib/devise/models/rememberable.rb +62 -33
- data/lib/devise/models/timeoutable.rb +4 -8
- data/lib/devise/models/trackable.rb +20 -4
- data/lib/devise/models/validatable.rb +16 -9
- data/lib/devise/models.rb +3 -1
- data/lib/devise/modules.rb +12 -10
- data/lib/devise/omniauth/config.rb +2 -0
- data/lib/devise/omniauth/url_helpers.rb +14 -5
- data/lib/devise/omniauth.rb +4 -5
- 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/deprecated_constant_accessor.rb +39 -0
- data/lib/devise/rails/routes.rb +151 -120
- data/lib/devise/rails/warden_compat.rb +3 -10
- data/lib/devise/rails.rb +10 -13
- data/lib/devise/secret_key_finder.rb +27 -0
- data/lib/devise/strategies/authenticatable.rb +21 -10
- data/lib/devise/strategies/base.rb +3 -1
- data/lib/devise/strategies/database_authenticatable.rb +14 -6
- data/lib/devise/strategies/rememberable.rb +15 -3
- data/lib/devise/test/controller_helpers.rb +167 -0
- data/lib/devise/test/integration_helpers.rb +63 -0
- data/lib/devise/test_helpers.rb +7 -124
- data/lib/devise/time_inflector.rb +4 -2
- data/lib/devise/token_generator.rb +3 -41
- data/lib/devise/version.rb +3 -1
- data/lib/devise.rb +107 -84
- data/lib/generators/active_record/devise_generator.rb +64 -12
- data/lib/generators/active_record/templates/migration.rb +9 -7
- data/lib/generators/active_record/templates/migration_existing.rb +9 -7
- data/lib/generators/devise/controllers_generator.rb +46 -0
- data/lib/generators/devise/devise_generator.rb +8 -6
- data/lib/generators/devise/install_generator.rb +18 -1
- data/lib/generators/devise/orm_helpers.rb +10 -21
- data/lib/generators/devise/views_generator.rb +49 -28
- data/lib/generators/mongoid/devise_generator.rb +21 -19
- data/lib/generators/templates/README +13 -12
- 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 +94 -37
- 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 +6 -2
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +12 -4
- data/lib/generators/templates/simple_form_for/passwords/new.html.erb +5 -2
- data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +14 -6
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +12 -4
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +11 -6
- data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +5 -2
- metadata +50 -284
- data/.gitignore +0 -10
- data/.travis.yml +0 -20
- data/.yardopts +0 -9
- data/CONTRIBUTING.md +0 -14
- data/Gemfile +0 -31
- 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.x +0 -31
- data/gemfiles/Gemfile.rails-3.2.x.lock +0 -159
- 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 -120
- data/test/controllers/passwords_controller_test.rb +0 -31
- data/test/controllers/sessions_controller_test.rb +0 -99
- 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 -67
- 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 -298
- 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 -104
- 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 -250
- 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/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,29 @@
|
|
|
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.
|
|
9
|
+
#
|
|
10
|
+
# This module defines a `password=` method. This method will hash the argument
|
|
11
|
+
# and store it in the `encrypted_password` column, bypassing any pre-existing
|
|
12
|
+
# `password` column if it exists.
|
|
13
13
|
#
|
|
14
14
|
# == Options
|
|
15
15
|
#
|
|
16
|
-
#
|
|
16
|
+
# DatabaseAuthenticatable adds the following options to devise_for:
|
|
17
17
|
#
|
|
18
18
|
# * +pepper+: a random string used to provide a more secure hash. Use
|
|
19
|
-
# `
|
|
19
|
+
# `rails secret` to generate new keys.
|
|
20
20
|
#
|
|
21
21
|
# * +stretches+: the cost given to bcrypt.
|
|
22
22
|
#
|
|
23
|
+
# * +send_email_changed_notification+: notify original email when it changes.
|
|
24
|
+
#
|
|
25
|
+
# * +send_password_change_notification+: notify email when password changes.
|
|
26
|
+
#
|
|
23
27
|
# == Examples
|
|
24
28
|
#
|
|
25
29
|
# User.find(1).valid_password?('password123') # returns true/false
|
|
@@ -28,26 +32,44 @@ module Devise
|
|
|
28
32
|
extend ActiveSupport::Concern
|
|
29
33
|
|
|
30
34
|
included do
|
|
35
|
+
after_update :send_email_changed_notification, if: :send_email_changed_notification?
|
|
36
|
+
after_update :send_password_change_notification, if: :send_password_change_notification?
|
|
37
|
+
|
|
31
38
|
attr_reader :password, :current_password
|
|
32
39
|
attr_accessor :password_confirmation
|
|
33
40
|
end
|
|
34
41
|
|
|
42
|
+
def initialize(*args, &block)
|
|
43
|
+
@skip_email_changed_notification = false
|
|
44
|
+
@skip_password_change_notification = false
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Skips sending the email changed notification after_update
|
|
49
|
+
def skip_email_changed_notification!
|
|
50
|
+
@skip_email_changed_notification = true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Skips sending the password change notification after_update
|
|
54
|
+
def skip_password_change_notification!
|
|
55
|
+
@skip_password_change_notification = true
|
|
56
|
+
end
|
|
57
|
+
|
|
35
58
|
def self.required_fields(klass)
|
|
36
59
|
[:encrypted_password] + klass.authentication_keys
|
|
37
60
|
end
|
|
38
61
|
|
|
39
|
-
# Generates password
|
|
62
|
+
# Generates a hashed password based on the given value.
|
|
63
|
+
# For legacy reasons, we use `encrypted_password` to store
|
|
64
|
+
# the hashed password.
|
|
40
65
|
def password=(new_password)
|
|
41
66
|
@password = new_password
|
|
42
67
|
self.encrypted_password = password_digest(@password) if @password.present?
|
|
43
68
|
end
|
|
44
69
|
|
|
45
|
-
# Verifies whether
|
|
70
|
+
# Verifies whether a password (ie from sign in) is the user password.
|
|
46
71
|
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)
|
|
72
|
+
Devise::Encryptor.compare(self.class, encrypted_password, password)
|
|
51
73
|
end
|
|
52
74
|
|
|
53
75
|
# Set password and password confirmation to nil
|
|
@@ -55,10 +77,23 @@ module Devise
|
|
|
55
77
|
self.password = self.password_confirmation = nil
|
|
56
78
|
end
|
|
57
79
|
|
|
58
|
-
# Update record attributes when :current_password matches, otherwise
|
|
59
|
-
# error on :current_password.
|
|
60
|
-
#
|
|
80
|
+
# Update record attributes when :current_password matches, otherwise
|
|
81
|
+
# returns error on :current_password.
|
|
82
|
+
#
|
|
83
|
+
# This method also rejects the password field if it is blank (allowing
|
|
84
|
+
# users to change relevant information like the e-mail without changing
|
|
85
|
+
# their password). In case the password field is rejected, the confirmation
|
|
86
|
+
# is also rejected as long as it is also blank.
|
|
61
87
|
def update_with_password(params, *options)
|
|
88
|
+
if options.present?
|
|
89
|
+
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
|
|
90
|
+
[Devise] The second argument of `DatabaseAuthenticatable#update_with_password`
|
|
91
|
+
(`options`) is deprecated and it will be removed in the next major version.
|
|
92
|
+
It was added to support a feature deprecated in Rails 4, so you can safely remove it
|
|
93
|
+
from your code.
|
|
94
|
+
DEPRECATION
|
|
95
|
+
end
|
|
96
|
+
|
|
62
97
|
current_password = params.delete(:current_password)
|
|
63
98
|
|
|
64
99
|
if params[:password].blank?
|
|
@@ -67,11 +102,11 @@ module Devise
|
|
|
67
102
|
end
|
|
68
103
|
|
|
69
104
|
result = if valid_password?(current_password)
|
|
70
|
-
|
|
105
|
+
update(params, *options)
|
|
71
106
|
else
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
assign_attributes(params, *options)
|
|
108
|
+
valid?
|
|
109
|
+
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
75
110
|
false
|
|
76
111
|
end
|
|
77
112
|
|
|
@@ -92,10 +127,19 @@ module Devise
|
|
|
92
127
|
# end
|
|
93
128
|
#
|
|
94
129
|
def update_without_password(params, *options)
|
|
130
|
+
if options.present?
|
|
131
|
+
ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
|
|
132
|
+
[Devise] The second argument of `DatabaseAuthenticatable#update_without_password`
|
|
133
|
+
(`options`) is deprecated and it will be removed in the next major version.
|
|
134
|
+
It was added to support a feature deprecated in Rails 4, so you can safely remove it
|
|
135
|
+
from your code.
|
|
136
|
+
DEPRECATION
|
|
137
|
+
end
|
|
138
|
+
|
|
95
139
|
params.delete(:password)
|
|
96
140
|
params.delete(:password_confirmation)
|
|
97
141
|
|
|
98
|
-
result =
|
|
142
|
+
result = update(params, *options)
|
|
99
143
|
clean_up_passwords
|
|
100
144
|
result
|
|
101
145
|
end
|
|
@@ -107,8 +151,8 @@ module Devise
|
|
|
107
151
|
result = if valid_password?(current_password)
|
|
108
152
|
destroy
|
|
109
153
|
else
|
|
110
|
-
|
|
111
|
-
|
|
154
|
+
valid?
|
|
155
|
+
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
112
156
|
false
|
|
113
157
|
end
|
|
114
158
|
|
|
@@ -133,19 +177,56 @@ module Devise
|
|
|
133
177
|
encrypted_password[0,29] if encrypted_password
|
|
134
178
|
end
|
|
135
179
|
|
|
180
|
+
if Devise.activerecord51?
|
|
181
|
+
# Send notification to user when email changes.
|
|
182
|
+
def send_email_changed_notification
|
|
183
|
+
send_devise_notification(:email_changed, to: email_before_last_save)
|
|
184
|
+
end
|
|
185
|
+
else
|
|
186
|
+
# Send notification to user when email changes.
|
|
187
|
+
def send_email_changed_notification
|
|
188
|
+
send_devise_notification(:email_changed, to: email_was)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Send notification to user when password changes.
|
|
193
|
+
def send_password_change_notification
|
|
194
|
+
send_devise_notification(:password_change)
|
|
195
|
+
end
|
|
196
|
+
|
|
136
197
|
protected
|
|
137
198
|
|
|
138
|
-
#
|
|
199
|
+
# Hashes the password using bcrypt. Custom hash functions should override
|
|
139
200
|
# this method to apply their own algorithm.
|
|
140
201
|
#
|
|
141
|
-
# See https://github.com/
|
|
142
|
-
# of other
|
|
202
|
+
# See https://github.com/heartcombo/devise-encryptable for examples
|
|
203
|
+
# of other hashing engines.
|
|
143
204
|
def password_digest(password)
|
|
144
|
-
Devise.
|
|
205
|
+
Devise::Encryptor.digest(self.class, password)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
if Devise.activerecord51?
|
|
209
|
+
def send_email_changed_notification?
|
|
210
|
+
self.class.send_email_changed_notification && saved_change_to_email? && !@skip_email_changed_notification
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
def send_email_changed_notification?
|
|
214
|
+
self.class.send_email_changed_notification && email_changed? && !@skip_email_changed_notification
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
if Devise.activerecord51?
|
|
219
|
+
def send_password_change_notification?
|
|
220
|
+
self.class.send_password_change_notification && saved_change_to_encrypted_password? && !@skip_password_change_notification
|
|
221
|
+
end
|
|
222
|
+
else
|
|
223
|
+
def send_password_change_notification?
|
|
224
|
+
self.class.send_password_change_notification && encrypted_password_changed? && !@skip_password_change_notification
|
|
225
|
+
end
|
|
145
226
|
end
|
|
146
227
|
|
|
147
228
|
module ClassMethods
|
|
148
|
-
Devise::Models.config(self, :pepper, :stretches)
|
|
229
|
+
Devise::Models.config(self, :pepper, :stretches, :send_email_changed_notification, :send_password_change_notification)
|
|
149
230
|
|
|
150
231
|
# We assume this method already gets the sanitized values from the
|
|
151
232
|
# 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
|
#
|
|
@@ -22,7 +24,7 @@ module Devise
|
|
|
22
24
|
module Lockable
|
|
23
25
|
extend ActiveSupport::Concern
|
|
24
26
|
|
|
25
|
-
delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :
|
|
27
|
+
delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, to: "self.class"
|
|
26
28
|
|
|
27
29
|
def self.required_fields(klass)
|
|
28
30
|
attributes = []
|
|
@@ -34,13 +36,16 @@ module Devise
|
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
# Lock a user setting its locked_at to actual time.
|
|
37
|
-
|
|
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 = { })
|
|
38
43
|
self.locked_at = Time.now.utc
|
|
39
44
|
|
|
40
|
-
if unlock_strategy_enabled?(:email)
|
|
45
|
+
if unlock_strategy_enabled?(:email) && opts.fetch(:send_instructions, true)
|
|
41
46
|
send_unlock_instructions
|
|
42
47
|
else
|
|
43
|
-
save(:
|
|
48
|
+
save(validate: false)
|
|
44
49
|
end
|
|
45
50
|
end
|
|
46
51
|
|
|
@@ -49,7 +54,15 @@ module Devise
|
|
|
49
54
|
self.locked_at = nil
|
|
50
55
|
self.failed_attempts = 0 if respond_to?(:failed_attempts=)
|
|
51
56
|
self.unlock_token = nil if respond_to?(:unlock_token=)
|
|
52
|
-
save(:
|
|
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
|
|
53
66
|
end
|
|
54
67
|
|
|
55
68
|
# Verifies whether a user is locked or not.
|
|
@@ -61,7 +74,7 @@ module Devise
|
|
|
61
74
|
def send_unlock_instructions
|
|
62
75
|
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
|
|
63
76
|
self.unlock_token = enc
|
|
64
|
-
|
|
77
|
+
save(validate: false)
|
|
65
78
|
send_devise_notification(:unlock_instructions, raw, {})
|
|
66
79
|
raw
|
|
67
80
|
end
|
|
@@ -96,26 +109,30 @@ module Devise
|
|
|
96
109
|
if super && !access_locked?
|
|
97
110
|
true
|
|
98
111
|
else
|
|
99
|
-
|
|
100
|
-
self.failed_attempts += 1
|
|
112
|
+
increment_failed_attempts
|
|
101
113
|
if attempts_exceeded?
|
|
102
114
|
lock_access! unless access_locked?
|
|
103
115
|
else
|
|
104
|
-
save(:
|
|
116
|
+
save(validate: false)
|
|
105
117
|
end
|
|
106
118
|
false
|
|
107
119
|
end
|
|
108
120
|
end
|
|
109
121
|
|
|
122
|
+
def increment_failed_attempts
|
|
123
|
+
self.class.increment_counter(:failed_attempts, id)
|
|
124
|
+
reload
|
|
125
|
+
end
|
|
126
|
+
|
|
110
127
|
def unauthenticated_message
|
|
111
128
|
# If set to paranoid mode, do not show the locked message because it
|
|
112
129
|
# leaks the existence of an account.
|
|
113
130
|
if Devise.paranoid
|
|
114
131
|
super
|
|
115
|
-
elsif lock_strategy_enabled?(:failed_attempts) &&
|
|
116
|
-
:last_attempt
|
|
117
|
-
elsif lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?
|
|
132
|
+
elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?)
|
|
118
133
|
:locked
|
|
134
|
+
elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? && self.class.last_attempt_warning
|
|
135
|
+
:last_attempt
|
|
119
136
|
else
|
|
120
137
|
super
|
|
121
138
|
end
|
|
@@ -124,11 +141,11 @@ module Devise
|
|
|
124
141
|
protected
|
|
125
142
|
|
|
126
143
|
def attempts_exceeded?
|
|
127
|
-
self.failed_attempts
|
|
144
|
+
self.failed_attempts >= self.class.maximum_attempts
|
|
128
145
|
end
|
|
129
146
|
|
|
130
147
|
def last_attempt?
|
|
131
|
-
self.failed_attempts == self.class.maximum_attempts
|
|
148
|
+
self.failed_attempts == self.class.maximum_attempts - 1
|
|
132
149
|
end
|
|
133
150
|
|
|
134
151
|
# Tells if the lock is expired if :time unlock strategy is active
|
|
@@ -152,11 +169,14 @@ module Devise
|
|
|
152
169
|
end
|
|
153
170
|
|
|
154
171
|
module ClassMethods
|
|
172
|
+
# List of strategies that are enabled/supported if :both is used.
|
|
173
|
+
BOTH_STRATEGIES = [:time, :email]
|
|
174
|
+
|
|
155
175
|
# Attempt to find a user by its unlock keys. If a record is found, send new
|
|
156
176
|
# unlock instructions to it. If not user is found, returns a new user
|
|
157
177
|
# with an email not found error.
|
|
158
178
|
# Options must contain the user's unlock keys
|
|
159
|
-
def send_unlock_instructions(attributes={})
|
|
179
|
+
def send_unlock_instructions(attributes = {})
|
|
160
180
|
lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
|
|
161
181
|
lockable.resend_unlock_instructions if lockable.persisted?
|
|
162
182
|
lockable
|
|
@@ -178,7 +198,8 @@ module Devise
|
|
|
178
198
|
|
|
179
199
|
# Is the unlock enabled for the given unlock strategy?
|
|
180
200
|
def unlock_strategy_enabled?(strategy)
|
|
181
|
-
|
|
201
|
+
self.unlock_strategy == strategy ||
|
|
202
|
+
(self.unlock_strategy == :both && BOTH_STRATEGIES.include?(strategy))
|
|
182
203
|
end
|
|
183
204
|
|
|
184
205
|
# Is the lock enabled for the given lock strategy?
|
|
@@ -186,7 +207,7 @@ module Devise
|
|
|
186
207
|
self.lock_strategy == strategy
|
|
187
208
|
end
|
|
188
209
|
|
|
189
|
-
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
|
|
210
|
+
Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys, :last_attempt_warning)
|
|
190
211
|
end
|
|
191
212
|
end
|
|
192
213
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'devise/omniauth'
|
|
2
4
|
|
|
3
5
|
module Devise
|
|
@@ -10,7 +12,7 @@ module Devise
|
|
|
10
12
|
#
|
|
11
13
|
# * +omniauth_providers+: Which providers are available to this model. It expects an array:
|
|
12
14
|
#
|
|
13
|
-
# devise_for :database_authenticatable, :omniauthable, :
|
|
15
|
+
# devise_for :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
|
|
14
16
|
#
|
|
15
17
|
module Omniauthable
|
|
16
18
|
extend ActiveSupport::Concern
|
|
@@ -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,15 +86,52 @@ 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.
|
|
97
133
|
# Attributes must contain the user's email
|
|
98
|
-
def send_reset_password_instructions(attributes={})
|
|
134
|
+
def send_reset_password_instructions(attributes = {})
|
|
99
135
|
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
|
|
100
136
|
recoverable.send_reset_password_instructions if recoverable.persisted?
|
|
101
137
|
recoverable
|
|
@@ -106,7 +142,7 @@ module Devise
|
|
|
106
142
|
# try saving the record. If not user is found, returns a new user
|
|
107
143
|
# containing an error in reset_password_token attribute.
|
|
108
144
|
# Attributes must contain reset_password_token, password and confirmation
|
|
109
|
-
def reset_password_by_token(attributes={})
|
|
145
|
+
def reset_password_by_token(attributes = {})
|
|
110
146
|
original_token = attributes[:reset_password_token]
|
|
111
147
|
reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
|
|
112
148
|
|
|
@@ -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
|