devise 3.2.2 → 4.6.0
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 +7 -0
- data/CHANGELOG.md +242 -978
- data/MIT-LICENSE +1 -1
- data/README.md +371 -100
- 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 +67 -31
- data/app/helpers/devise_helper.rb +12 -19
- data/app/mailers/devise/mailer.rb +10 -0
- 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 +23 -17
- data/lib/devise/controllers/helpers.rb +112 -32
- 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 +42 -26
- 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 +125 -39
- 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 +7 -2
- 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 +8 -2
- data/lib/devise/models/authenticatable.rb +82 -56
- data/lib/devise/models/confirmable.rb +125 -42
- data/lib/devise/models/database_authenticatable.rb +110 -32
- data/lib/devise/models/lockable.rb +30 -17
- data/lib/devise/models/omniauthable.rb +3 -1
- data/lib/devise/models/recoverable.rb +62 -26
- 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 +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 +147 -116
- 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 +20 -9
- 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 +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 +4 -2
- data/lib/devise/token_generator.rb +3 -41
- data/lib/devise/version.rb +3 -1
- data/lib/devise.rb +111 -84
- data/lib/generators/active_record/devise_generator.rb +49 -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 +7 -5
- data/lib/generators/devise/install_generator.rb +21 -0
- 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 +5 -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 +81 -36
- 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 +9 -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 +52 -280
- 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,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,37 +28,68 @@ 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
|
-
self.encrypted_password = password_digest(@password)
|
63
|
+
self.encrypted_password = password_digest(@password)
|
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
|
54
72
|
def clean_up_passwords
|
55
|
-
|
73
|
+
@password = @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,57 @@ 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
|
-
|
201
|
+
return if password.blank?
|
202
|
+
Devise::Encryptor.digest(self.class, password)
|
203
|
+
end
|
204
|
+
|
205
|
+
if Devise.activerecord51?
|
206
|
+
def send_email_changed_notification?
|
207
|
+
self.class.send_email_changed_notification && saved_change_to_email? && !@skip_email_changed_notification
|
208
|
+
end
|
209
|
+
else
|
210
|
+
def send_email_changed_notification?
|
211
|
+
self.class.send_email_changed_notification && email_changed? && !@skip_email_changed_notification
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
if Devise.activerecord51?
|
216
|
+
def send_password_change_notification?
|
217
|
+
self.class.send_password_change_notification && saved_change_to_encrypted_password? && !@skip_password_change_notification
|
218
|
+
end
|
219
|
+
else
|
220
|
+
def send_password_change_notification?
|
221
|
+
self.class.send_password_change_notification && encrypted_password_changed? && !@skip_password_change_notification
|
222
|
+
end
|
145
223
|
end
|
146
224
|
|
147
225
|
module ClassMethods
|
148
|
-
Devise::Models.config(self, :pepper, :stretches)
|
226
|
+
Devise::Models.config(self, :pepper, :stretches, :send_email_changed_notification, :send_password_change_notification)
|
149
227
|
|
150
228
|
# We assume this method already gets the sanitized values from the
|
151
229
|
# 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,7 @@ 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)
|
53
58
|
end
|
54
59
|
|
55
60
|
# Verifies whether a user is locked or not.
|
@@ -61,7 +66,7 @@ module Devise
|
|
61
66
|
def send_unlock_instructions
|
62
67
|
raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
|
63
68
|
self.unlock_token = enc
|
64
|
-
|
69
|
+
save(validate: false)
|
65
70
|
send_devise_notification(:unlock_instructions, raw, {})
|
66
71
|
raw
|
67
72
|
end
|
@@ -96,26 +101,30 @@ module Devise
|
|
96
101
|
if super && !access_locked?
|
97
102
|
true
|
98
103
|
else
|
99
|
-
|
100
|
-
self.failed_attempts += 1
|
104
|
+
increment_failed_attempts
|
101
105
|
if attempts_exceeded?
|
102
106
|
lock_access! unless access_locked?
|
103
107
|
else
|
104
|
-
save(:
|
108
|
+
save(validate: false)
|
105
109
|
end
|
106
110
|
false
|
107
111
|
end
|
108
112
|
end
|
113
|
+
|
114
|
+
def increment_failed_attempts
|
115
|
+
self.class.increment_counter(:failed_attempts, id)
|
116
|
+
reload
|
117
|
+
end
|
109
118
|
|
110
119
|
def unauthenticated_message
|
111
120
|
# If set to paranoid mode, do not show the locked message because it
|
112
121
|
# leaks the existence of an account.
|
113
122
|
if Devise.paranoid
|
114
123
|
super
|
115
|
-
elsif lock_strategy_enabled?(:failed_attempts) &&
|
116
|
-
:last_attempt
|
117
|
-
elsif lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?
|
124
|
+
elsif access_locked? || (lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?)
|
118
125
|
:locked
|
126
|
+
elsif lock_strategy_enabled?(:failed_attempts) && last_attempt? && self.class.last_attempt_warning
|
127
|
+
:last_attempt
|
119
128
|
else
|
120
129
|
super
|
121
130
|
end
|
@@ -124,11 +133,11 @@ module Devise
|
|
124
133
|
protected
|
125
134
|
|
126
135
|
def attempts_exceeded?
|
127
|
-
self.failed_attempts
|
136
|
+
self.failed_attempts >= self.class.maximum_attempts
|
128
137
|
end
|
129
138
|
|
130
139
|
def last_attempt?
|
131
|
-
self.failed_attempts == self.class.maximum_attempts
|
140
|
+
self.failed_attempts == self.class.maximum_attempts - 1
|
132
141
|
end
|
133
142
|
|
134
143
|
# Tells if the lock is expired if :time unlock strategy is active
|
@@ -152,6 +161,9 @@ module Devise
|
|
152
161
|
end
|
153
162
|
|
154
163
|
module ClassMethods
|
164
|
+
# List of strategies that are enabled/supported if :both is used.
|
165
|
+
BOTH_STRATEGIES = [:time, :email]
|
166
|
+
|
155
167
|
# Attempt to find a user by its unlock keys. If a record is found, send new
|
156
168
|
# unlock instructions to it. If not user is found, returns a new user
|
157
169
|
# with an email not found error.
|
@@ -178,7 +190,8 @@ module Devise
|
|
178
190
|
|
179
191
|
# Is the unlock enabled for the given unlock strategy?
|
180
192
|
def unlock_strategy_enabled?(strategy)
|
181
|
-
|
193
|
+
self.unlock_strategy == strategy ||
|
194
|
+
(self.unlock_strategy == :both && BOTH_STRATEGIES.include?(strategy))
|
182
195
|
end
|
183
196
|
|
184
197
|
# Is the lock enabled for the given lock strategy?
|
@@ -186,7 +199,7 @@ module Devise
|
|
186
199
|
self.lock_strategy == strategy
|
187
200
|
end
|
188
201
|
|
189
|
-
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)
|
190
203
|
end
|
191
204
|
end
|
192
205
|
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,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
|