devise 3.5.10 → 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.
Files changed (258) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -1126
  3. data/MIT-LICENSE +2 -1
  4. data/README.md +291 -97
  5. data/app/controllers/devise/confirmations_controller.rb +3 -1
  6. data/app/controllers/devise/omniauth_callbacks_controller.rb +8 -6
  7. data/app/controllers/devise/passwords_controller.rb +7 -4
  8. data/app/controllers/devise/registrations_controller.rb +39 -18
  9. data/app/controllers/devise/sessions_controller.rb +9 -7
  10. data/app/controllers/devise/unlocks_controller.rb +4 -2
  11. data/app/controllers/devise_controller.rb +25 -12
  12. data/app/helpers/devise_helper.rb +23 -18
  13. data/app/mailers/devise/mailer.rb +10 -4
  14. data/app/views/devise/confirmations/new.html.erb +2 -2
  15. data/app/views/devise/mailer/email_changed.html.erb +7 -0
  16. data/app/views/devise/passwords/edit.html.erb +3 -3
  17. data/app/views/devise/passwords/new.html.erb +2 -2
  18. data/app/views/devise/registrations/edit.html.erb +9 -5
  19. data/app/views/devise/registrations/new.html.erb +4 -4
  20. data/app/views/devise/sessions/new.html.erb +4 -4
  21. data/app/views/devise/shared/_error_messages.html.erb +15 -0
  22. data/app/views/devise/shared/_links.html.erb +8 -8
  23. data/app/views/devise/unlocks/new.html.erb +2 -2
  24. data/config/locales/en.yml +5 -2
  25. data/lib/devise.rb +57 -40
  26. data/lib/devise/controllers/helpers.rb +30 -27
  27. data/lib/devise/controllers/rememberable.rb +3 -1
  28. data/lib/devise/controllers/scoped_views.rb +2 -0
  29. data/lib/devise/controllers/sign_in_out.rb +39 -14
  30. data/lib/devise/controllers/store_location.rb +25 -7
  31. data/lib/devise/controllers/url_helpers.rb +3 -1
  32. data/lib/devise/delegator.rb +2 -0
  33. data/lib/devise/encryptor.rb +6 -4
  34. data/lib/devise/failure_app.rb +75 -38
  35. data/lib/devise/hooks/activatable.rb +2 -0
  36. data/lib/devise/hooks/csrf_cleaner.rb +2 -0
  37. data/lib/devise/hooks/forgetable.rb +2 -0
  38. data/lib/devise/hooks/lockable.rb +4 -2
  39. data/lib/devise/hooks/proxy.rb +3 -1
  40. data/lib/devise/hooks/rememberable.rb +2 -0
  41. data/lib/devise/hooks/timeoutable.rb +4 -2
  42. data/lib/devise/hooks/trackable.rb +2 -0
  43. data/lib/devise/mailers/helpers.rb +7 -4
  44. data/lib/devise/mapping.rb +3 -1
  45. data/lib/devise/models.rb +3 -1
  46. data/lib/devise/models/authenticatable.rb +63 -33
  47. data/lib/devise/models/confirmable.rb +90 -29
  48. data/lib/devise/models/database_authenticatable.rb +93 -22
  49. data/lib/devise/models/lockable.rb +19 -5
  50. data/lib/devise/models/omniauthable.rb +2 -0
  51. data/lib/devise/models/recoverable.rb +33 -21
  52. data/lib/devise/models/registerable.rb +4 -0
  53. data/lib/devise/models/rememberable.rb +6 -11
  54. data/lib/devise/models/timeoutable.rb +2 -0
  55. data/lib/devise/models/trackable.rb +15 -1
  56. data/lib/devise/models/validatable.rb +10 -3
  57. data/lib/devise/modules.rb +2 -0
  58. data/lib/devise/omniauth.rb +4 -5
  59. data/lib/devise/omniauth/config.rb +2 -0
  60. data/lib/devise/omniauth/url_helpers.rb +14 -5
  61. data/lib/devise/orm/active_record.rb +5 -1
  62. data/lib/devise/orm/mongoid.rb +6 -2
  63. data/lib/devise/parameter_filter.rb +4 -0
  64. data/lib/devise/parameter_sanitizer.rb +139 -65
  65. data/lib/devise/rails.rb +7 -16
  66. data/lib/devise/rails/deprecated_constant_accessor.rb +39 -0
  67. data/lib/devise/rails/routes.rb +48 -37
  68. data/lib/devise/rails/warden_compat.rb +3 -10
  69. data/lib/devise/secret_key_finder.rb +27 -0
  70. data/lib/devise/strategies/authenticatable.rb +3 -1
  71. data/lib/devise/strategies/base.rb +2 -0
  72. data/lib/devise/strategies/database_authenticatable.rb +11 -4
  73. data/lib/devise/strategies/rememberable.rb +2 -0
  74. data/lib/devise/test/controller_helpers.rb +167 -0
  75. data/lib/devise/test/integration_helpers.rb +63 -0
  76. data/lib/devise/test_helpers.rb +7 -124
  77. data/lib/devise/time_inflector.rb +2 -0
  78. data/lib/devise/token_generator.rb +3 -41
  79. data/lib/devise/version.rb +3 -1
  80. data/lib/generators/active_record/devise_generator.rb +46 -12
  81. data/lib/generators/active_record/templates/migration.rb +4 -2
  82. data/lib/generators/active_record/templates/migration_existing.rb +4 -2
  83. data/lib/generators/devise/controllers_generator.rb +3 -1
  84. data/lib/generators/devise/devise_generator.rb +5 -3
  85. data/lib/generators/devise/install_generator.rb +18 -5
  86. data/lib/generators/devise/orm_helpers.rb +10 -21
  87. data/lib/generators/devise/views_generator.rb +8 -9
  88. data/lib/generators/mongoid/devise_generator.rb +7 -5
  89. data/lib/generators/templates/README +9 -8
  90. data/lib/generators/templates/controllers/confirmations_controller.rb +2 -0
  91. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +3 -1
  92. data/lib/generators/templates/controllers/passwords_controller.rb +2 -0
  93. data/lib/generators/templates/controllers/registrations_controller.rb +6 -4
  94. data/lib/generators/templates/controllers/sessions_controller.rb +4 -2
  95. data/lib/generators/templates/controllers/unlocks_controller.rb +2 -0
  96. data/lib/generators/templates/devise.rb +63 -21
  97. data/lib/generators/templates/markerb/email_changed.markerb +7 -0
  98. data/lib/generators/templates/markerb/password_change.markerb +2 -2
  99. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +5 -1
  100. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +10 -2
  101. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +4 -1
  102. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +11 -3
  103. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +11 -3
  104. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +7 -2
  105. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +4 -1
  106. metadata +19 -317
  107. data/.gitignore +0 -10
  108. data/.travis.yml +0 -44
  109. data/.yardopts +0 -9
  110. data/CODE_OF_CONDUCT.md +0 -22
  111. data/CONTRIBUTING.md +0 -16
  112. data/Gemfile +0 -30
  113. data/Gemfile.lock +0 -187
  114. data/Rakefile +0 -36
  115. data/devise.gemspec +0 -27
  116. data/devise.png +0 -0
  117. data/gemfiles/Gemfile.rails-3.2-stable +0 -29
  118. data/gemfiles/Gemfile.rails-3.2-stable.lock +0 -172
  119. data/gemfiles/Gemfile.rails-4.0-stable +0 -30
  120. data/gemfiles/Gemfile.rails-4.0-stable.lock +0 -166
  121. data/gemfiles/Gemfile.rails-4.1-stable +0 -30
  122. data/gemfiles/Gemfile.rails-4.1-stable.lock +0 -171
  123. data/gemfiles/Gemfile.rails-4.2-stable +0 -30
  124. data/gemfiles/Gemfile.rails-4.2-stable.lock +0 -193
  125. data/script/cached-bundle +0 -49
  126. data/script/s3-put +0 -71
  127. data/test/controllers/custom_registrations_controller_test.rb +0 -40
  128. data/test/controllers/custom_strategy_test.rb +0 -62
  129. data/test/controllers/helper_methods_test.rb +0 -21
  130. data/test/controllers/helpers_test.rb +0 -316
  131. data/test/controllers/inherited_controller_i18n_messages_test.rb +0 -51
  132. data/test/controllers/internal_helpers_test.rb +0 -129
  133. data/test/controllers/load_hooks_controller_test.rb +0 -19
  134. data/test/controllers/passwords_controller_test.rb +0 -31
  135. data/test/controllers/sessions_controller_test.rb +0 -103
  136. data/test/controllers/url_helpers_test.rb +0 -65
  137. data/test/delegator_test.rb +0 -19
  138. data/test/devise_test.rb +0 -107
  139. data/test/failure_app_test.rb +0 -315
  140. data/test/generators/active_record_generator_test.rb +0 -109
  141. data/test/generators/controllers_generator_test.rb +0 -48
  142. data/test/generators/devise_generator_test.rb +0 -39
  143. data/test/generators/install_generator_test.rb +0 -13
  144. data/test/generators/mongoid_generator_test.rb +0 -23
  145. data/test/generators/views_generator_test.rb +0 -103
  146. data/test/helpers/devise_helper_test.rb +0 -49
  147. data/test/integration/authenticatable_test.rb +0 -729
  148. data/test/integration/confirmable_test.rb +0 -324
  149. data/test/integration/database_authenticatable_test.rb +0 -95
  150. data/test/integration/http_authenticatable_test.rb +0 -105
  151. data/test/integration/lockable_test.rb +0 -239
  152. data/test/integration/omniauthable_test.rb +0 -135
  153. data/test/integration/recoverable_test.rb +0 -347
  154. data/test/integration/registerable_test.rb +0 -359
  155. data/test/integration/rememberable_test.rb +0 -214
  156. data/test/integration/timeoutable_test.rb +0 -184
  157. data/test/integration/trackable_test.rb +0 -92
  158. data/test/mailers/confirmation_instructions_test.rb +0 -115
  159. data/test/mailers/reset_password_instructions_test.rb +0 -96
  160. data/test/mailers/unlock_instructions_test.rb +0 -91
  161. data/test/mapping_test.rb +0 -134
  162. data/test/models/authenticatable_test.rb +0 -23
  163. data/test/models/confirmable_test.rb +0 -511
  164. data/test/models/database_authenticatable_test.rb +0 -269
  165. data/test/models/lockable_test.rb +0 -350
  166. data/test/models/omniauthable_test.rb +0 -7
  167. data/test/models/recoverable_test.rb +0 -251
  168. data/test/models/registerable_test.rb +0 -7
  169. data/test/models/rememberable_test.rb +0 -169
  170. data/test/models/serializable_test.rb +0 -49
  171. data/test/models/timeoutable_test.rb +0 -51
  172. data/test/models/trackable_test.rb +0 -41
  173. data/test/models/validatable_test.rb +0 -127
  174. data/test/models_test.rb +0 -153
  175. data/test/omniauth/config_test.rb +0 -57
  176. data/test/omniauth/url_helpers_test.rb +0 -54
  177. data/test/orm/active_record.rb +0 -10
  178. data/test/orm/mongoid.rb +0 -13
  179. data/test/parameter_sanitizer_test.rb +0 -81
  180. data/test/rails_app/Rakefile +0 -6
  181. data/test/rails_app/app/active_record/admin.rb +0 -6
  182. data/test/rails_app/app/active_record/shim.rb +0 -2
  183. data/test/rails_app/app/active_record/user.rb +0 -6
  184. data/test/rails_app/app/active_record/user_on_engine.rb +0 -7
  185. data/test/rails_app/app/active_record/user_on_main_app.rb +0 -7
  186. data/test/rails_app/app/active_record/user_without_email.rb +0 -8
  187. data/test/rails_app/app/controllers/admins/sessions_controller.rb +0 -6
  188. data/test/rails_app/app/controllers/admins_controller.rb +0 -6
  189. data/test/rails_app/app/controllers/application_controller.rb +0 -12
  190. data/test/rails_app/app/controllers/application_with_fake_engine.rb +0 -30
  191. data/test/rails_app/app/controllers/custom/registrations_controller.rb +0 -31
  192. data/test/rails_app/app/controllers/home_controller.rb +0 -25
  193. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +0 -2
  194. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +0 -2
  195. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +0 -14
  196. data/test/rails_app/app/controllers/users_controller.rb +0 -31
  197. data/test/rails_app/app/helpers/application_helper.rb +0 -3
  198. data/test/rails_app/app/mailers/users/from_proc_mailer.rb +0 -3
  199. data/test/rails_app/app/mailers/users/mailer.rb +0 -3
  200. data/test/rails_app/app/mailers/users/reply_to_mailer.rb +0 -4
  201. data/test/rails_app/app/mongoid/admin.rb +0 -29
  202. data/test/rails_app/app/mongoid/shim.rb +0 -23
  203. data/test/rails_app/app/mongoid/user.rb +0 -39
  204. data/test/rails_app/app/mongoid/user_on_engine.rb +0 -39
  205. data/test/rails_app/app/mongoid/user_on_main_app.rb +0 -39
  206. data/test/rails_app/app/mongoid/user_without_email.rb +0 -33
  207. data/test/rails_app/app/views/admins/index.html.erb +0 -1
  208. data/test/rails_app/app/views/admins/sessions/new.html.erb +0 -2
  209. data/test/rails_app/app/views/home/admin_dashboard.html.erb +0 -1
  210. data/test/rails_app/app/views/home/index.html.erb +0 -1
  211. data/test/rails_app/app/views/home/join.html.erb +0 -1
  212. data/test/rails_app/app/views/home/private.html.erb +0 -1
  213. data/test/rails_app/app/views/home/user_dashboard.html.erb +0 -1
  214. data/test/rails_app/app/views/layouts/application.html.erb +0 -24
  215. data/test/rails_app/app/views/users/edit_form.html.erb +0 -1
  216. data/test/rails_app/app/views/users/index.html.erb +0 -1
  217. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +0 -1
  218. data/test/rails_app/app/views/users/sessions/new.html.erb +0 -1
  219. data/test/rails_app/bin/bundle +0 -3
  220. data/test/rails_app/bin/rails +0 -4
  221. data/test/rails_app/bin/rake +0 -4
  222. data/test/rails_app/config.ru +0 -4
  223. data/test/rails_app/config/application.rb +0 -40
  224. data/test/rails_app/config/boot.rb +0 -14
  225. data/test/rails_app/config/database.yml +0 -18
  226. data/test/rails_app/config/environment.rb +0 -5
  227. data/test/rails_app/config/environments/development.rb +0 -30
  228. data/test/rails_app/config/environments/production.rb +0 -84
  229. data/test/rails_app/config/environments/test.rb +0 -41
  230. data/test/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  231. data/test/rails_app/config/initializers/devise.rb +0 -180
  232. data/test/rails_app/config/initializers/inflections.rb +0 -2
  233. data/test/rails_app/config/initializers/secret_token.rb +0 -8
  234. data/test/rails_app/config/initializers/session_store.rb +0 -1
  235. data/test/rails_app/config/routes.rb +0 -125
  236. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -71
  237. data/test/rails_app/db/schema.rb +0 -55
  238. data/test/rails_app/lib/shared_admin.rb +0 -17
  239. data/test/rails_app/lib/shared_user.rb +0 -29
  240. data/test/rails_app/lib/shared_user_without_email.rb +0 -26
  241. data/test/rails_app/lib/shared_user_without_omniauth.rb +0 -13
  242. data/test/rails_app/public/404.html +0 -26
  243. data/test/rails_app/public/422.html +0 -26
  244. data/test/rails_app/public/500.html +0 -26
  245. data/test/rails_app/public/favicon.ico +0 -0
  246. data/test/rails_test.rb +0 -9
  247. data/test/routes_test.rb +0 -264
  248. data/test/support/action_controller/record_identifier.rb +0 -10
  249. data/test/support/assertions.rb +0 -39
  250. data/test/support/helpers.rb +0 -77
  251. data/test/support/integration.rb +0 -92
  252. data/test/support/locale/en.yml +0 -8
  253. data/test/support/mongoid.yml +0 -6
  254. data/test/support/webrat/integrations/rails.rb +0 -24
  255. data/test/test_helper.rb +0 -34
  256. data/test/test_helpers_test.rb +0 -178
  257. data/test/test_models.rb +0 -33
  258. data/test/time_helpers.rb +0 -137
@@ -1,24 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/strategies/database_authenticatable'
2
4
 
3
5
  module Devise
4
- def self.bcrypt(klass, password)
5
- ActiveSupport::Deprecation.warn "Devise.bcrypt is deprecated; use Devise::Encryptor.digest instead"
6
- Devise::Encryptor.digest(klass, password)
7
- end
8
-
9
6
  module Models
10
- # Authenticatable Module, responsible for encrypting password and validating
11
- # 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.
12
13
  #
13
14
  # == Options
14
15
  #
15
16
  # DatabaseAuthenticatable adds the following options to devise_for:
16
17
  #
17
18
  # * +pepper+: a random string used to provide a more secure hash. Use
18
- # `rake secret` to generate new keys.
19
+ # `rails secret` to generate new keys.
19
20
  #
20
21
  # * +stretches+: the cost given to bcrypt.
21
22
  #
23
+ # * +send_email_changed_notification+: notify original email when it changes.
24
+ #
25
+ # * +send_password_change_notification+: notify email when password changes.
26
+ #
22
27
  # == Examples
23
28
  #
24
29
  # User.find(1).valid_password?('password123') # returns true/false
@@ -27,17 +32,36 @@ module Devise
27
32
  extend ActiveSupport::Concern
28
33
 
29
34
  included do
35
+ after_update :send_email_changed_notification, if: :send_email_changed_notification?
30
36
  after_update :send_password_change_notification, if: :send_password_change_notification?
31
37
 
32
38
  attr_reader :password, :current_password
33
39
  attr_accessor :password_confirmation
34
40
  end
35
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
+
36
58
  def self.required_fields(klass)
37
59
  [:encrypted_password] + klass.authentication_keys
38
60
  end
39
61
 
40
- # Generates password encryption based on the given value.
62
+ # Generates a hashed password based on the given value.
63
+ # For legacy reasons, we use `encrypted_password` to store
64
+ # the hashed password.
41
65
  def password=(new_password)
42
66
  @password = new_password
43
67
  self.encrypted_password = password_digest(@password) if @password.present?
@@ -61,6 +85,15 @@ module Devise
61
85
  # their password). In case the password field is rejected, the confirmation
62
86
  # is also rejected as long as it is also blank.
63
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
+
64
97
  current_password = params.delete(:current_password)
65
98
 
66
99
  if params[:password].blank?
@@ -69,11 +102,11 @@ module Devise
69
102
  end
70
103
 
71
104
  result = if valid_password?(current_password)
72
- update_attributes(params, *options)
105
+ update(params, *options)
73
106
  else
74
- self.assign_attributes(params, *options)
75
- self.valid?
76
- self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
107
+ assign_attributes(params, *options)
108
+ valid?
109
+ errors.add(:current_password, current_password.blank? ? :blank : :invalid)
77
110
  false
78
111
  end
79
112
 
@@ -94,10 +127,19 @@ module Devise
94
127
  # end
95
128
  #
96
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
+
97
139
  params.delete(:password)
98
140
  params.delete(:password_confirmation)
99
141
 
100
- result = update_attributes(params, *options)
142
+ result = update(params, *options)
101
143
  clean_up_passwords
102
144
  result
103
145
  end
@@ -109,8 +151,8 @@ module Devise
109
151
  result = if valid_password?(current_password)
110
152
  destroy
111
153
  else
112
- self.valid?
113
- self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
154
+ valid?
155
+ errors.add(:current_password, current_password.blank? ? :blank : :invalid)
114
156
  false
115
157
  end
116
158
 
@@ -135,27 +177,56 @@ module Devise
135
177
  encrypted_password[0,29] if encrypted_password
136
178
  end
137
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.
138
193
  def send_password_change_notification
139
194
  send_devise_notification(:password_change)
140
195
  end
141
196
 
142
197
  protected
143
198
 
144
- # Digests the password using bcrypt. Custom encryption should override
199
+ # Hashes the password using bcrypt. Custom hash functions should override
145
200
  # this method to apply their own algorithm.
146
201
  #
147
- # See https://github.com/plataformatec/devise-encryptable for examples
148
- # of other encryption engines.
202
+ # See https://github.com/heartcombo/devise-encryptable for examples
203
+ # of other hashing engines.
149
204
  def password_digest(password)
150
205
  Devise::Encryptor.digest(self.class, password)
151
206
  end
152
207
 
153
- def send_password_change_notification?
154
- self.class.send_password_change_notification && encrypted_password_changed?
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
155
226
  end
156
227
 
157
228
  module ClassMethods
158
- Devise::Models.config(self, :pepper, :stretches, :send_password_change_notification)
229
+ Devise::Models.config(self, :pepper, :stretches, :send_email_changed_notification, :send_password_change_notification)
159
230
 
160
231
  # We assume this method already gets the sanitized values from the
161
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 setup lockable to use both email and time strategies.
12
+ # It's also possible to set up lockable to use both email and time strategies.
11
13
  #
12
14
  # == Options
13
15
  #
@@ -55,6 +57,14 @@ module Devise
55
57
  save(validate: false)
56
58
  end
57
59
 
60
+ # Resets failed attempts counter to 0.
61
+ def reset_failed_attempts!
62
+ if respond_to?(:failed_attempts) && !failed_attempts.to_i.zero?
63
+ self.failed_attempts = 0
64
+ save(validate: false)
65
+ end
66
+ end
67
+
58
68
  # Verifies whether a user is locked or not.
59
69
  def access_locked?
60
70
  !!locked_at && !lock_expired?
@@ -64,7 +74,7 @@ module Devise
64
74
  def send_unlock_instructions
65
75
  raw, enc = Devise.token_generator.generate(self.class, :unlock_token)
66
76
  self.unlock_token = enc
67
- self.save(validate: false)
77
+ save(validate: false)
68
78
  send_devise_notification(:unlock_instructions, raw, {})
69
79
  raw
70
80
  end
@@ -99,8 +109,7 @@ module Devise
99
109
  if super && !access_locked?
100
110
  true
101
111
  else
102
- self.failed_attempts ||= 0
103
- self.failed_attempts += 1
112
+ increment_failed_attempts
104
113
  if attempts_exceeded?
105
114
  lock_access! unless access_locked?
106
115
  else
@@ -110,6 +119,11 @@ module Devise
110
119
  end
111
120
  end
112
121
 
122
+ def increment_failed_attempts
123
+ self.class.increment_counter(:failed_attempts, id)
124
+ reload
125
+ end
126
+
113
127
  def unauthenticated_message
114
128
  # If set to paranoid mode, do not show the locked message because it
115
129
  # leaks the existence of an account.
@@ -162,7 +176,7 @@ module Devise
162
176
  # unlock instructions to it. If not user is found, returns a new user
163
177
  # with an email not found error.
164
178
  # Options must contain the user's unlock keys
165
- def send_unlock_instructions(attributes={})
179
+ def send_unlock_instructions(attributes = {})
166
180
  lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
167
181
  lockable.resend_unlock_instructions if lockable.persisted?
168
182
  lockable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/omniauth'
2
4
 
3
5
  module Devise
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Models
3
5
 
@@ -27,30 +29,20 @@ module Devise
27
29
  end
28
30
 
29
31
  included do
30
- before_update do
31
- if (respond_to?(:email_changed?) && email_changed?) || encrypted_password_changed?
32
- clear_reset_password_token
33
- end
34
- end
32
+ before_update :clear_reset_password_token, if: :clear_reset_password_token?
35
33
  end
36
34
 
37
35
  # Update password saving the record and clearing token. Returns true if
38
36
  # the passwords are valid and the record was saved, false otherwise.
39
37
  def reset_password(new_password, new_password_confirmation)
40
- self.password = new_password
41
- self.password_confirmation = new_password_confirmation
42
-
43
- if respond_to?(:after_password_reset) && valid?
44
- ActiveSupport::Deprecation.warn "after_password_reset is deprecated"
45
- after_password_reset
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
46
45
  end
47
-
48
- save
49
- end
50
-
51
- def reset_password!(new_password, new_password_confirmation)
52
- ActiveSupport::Deprecation.warn "reset_password! is deprecated in favor of reset_password"
53
- reset_password(new_password, new_password_confirmation)
54
46
  end
55
47
 
56
48
  # Resets reset password token and send reset password instructions by email.
@@ -99,7 +91,7 @@ module Devise
99
91
 
100
92
  self.reset_password_token = enc
101
93
  self.reset_password_sent_at = Time.now.utc
102
- self.save(validate: false)
94
+ save(validate: false)
103
95
  raw
104
96
  end
105
97
 
@@ -107,6 +99,26 @@ module Devise
107
99
  send_devise_notification(:reset_password_instructions, token, {})
108
100
  end
109
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
120
+ end
121
+
110
122
  module ClassMethods
111
123
  # Attempt to find a user by password reset token. If a user is found, return it
112
124
  # If a user is not found, return nil
@@ -119,7 +131,7 @@ module Devise
119
131
  # password instructions to it. If user is not found, returns a new user
120
132
  # with an email not found error.
121
133
  # Attributes must contain the user's email
122
- def send_reset_password_instructions(attributes={})
134
+ def send_reset_password_instructions(attributes = {})
123
135
  recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
124
136
  recoverable.send_reset_password_instructions if recoverable.persisted?
125
137
  recoverable
@@ -130,7 +142,7 @@ module Devise
130
142
  # try saving the record. If not user is found, returns a new user
131
143
  # containing an error in reset_password_token attribute.
132
144
  # Attributes must contain reset_password_token, password and confirmation
133
- def reset_password_by_token(attributes={})
145
+ def reset_password_by_token(attributes = {})
134
146
  original_token = attributes[:reset_password_token]
135
147
  reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
136
148
 
@@ -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 remember the user
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.
@@ -45,9 +47,7 @@ module Devise
45
47
  [:remember_created_at]
46
48
  end
47
49
 
48
- # TODO: We were used to receive a extend period argument but we no longer do.
49
- # Remove this for Devise 4.0.
50
- def remember_me!(*)
50
+ def remember_me!
51
51
  self.remember_token ||= self.class.remember_token if respond_to?(:remember_token)
52
52
  self.remember_created_at ||= Time.now.utc
53
53
  save(validate: false) if self.changed?
@@ -62,11 +62,6 @@ module Devise
62
62
  save(validate: false)
63
63
  end
64
64
 
65
- # Remember token should be expired if expiration time not overpass now.
66
- def remember_expired?
67
- remember_created_at.nil?
68
- end
69
-
70
65
  def remember_expires_at
71
66
  self.class.remember_for.from_now
72
67
  end
@@ -81,7 +76,7 @@ module Devise
81
76
  elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
82
77
  salt
83
78
  else
84
- raise "authenticable_salt returned nil for the #{self.class.name} model. " \
79
+ raise "authenticatable_salt returned nil for the #{self.class.name} model. " \
85
80
  "In order to use rememberable, you must ensure a password is always set " \
86
81
  "or have a remember_token column in your model or implement your own " \
87
82
  "rememberable_value in the model with custom logic."
@@ -107,7 +102,7 @@ module Devise
107
102
 
108
103
  def remember_me?(token, generated_at)
109
104
  # TODO: Normalize the JSON type coercion along with the Timeoutable hook
110
- # in a single place https://github.com/plataformatec/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
105
+ # in a single place https://github.com/heartcombo/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
111
106
  if generated_at.is_a?(String)
112
107
  generated_at = time_from_json(generated_at)
113
108
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/hooks/timeoutable'
2
4
 
3
5
  module Devise
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/hooks/trackable'
2
4
 
3
5
  module Devise
@@ -20,7 +22,7 @@ module Devise
20
22
  self.last_sign_in_at = old_current || new_current
21
23
  self.current_sign_in_at = new_current
22
24
 
23
- old_current, new_current = self.current_sign_in_ip, request.remote_ip
25
+ old_current, new_current = self.current_sign_in_ip, extract_ip_from(request)
24
26
  self.last_sign_in_ip = old_current || new_current
25
27
  self.current_sign_in_ip = new_current
26
28
 
@@ -29,9 +31,21 @@ module Devise
29
31
  end
30
32
 
31
33
  def update_tracked_fields!(request)
34
+ # We have to check if the user is already persisted before running
35
+ # `save` here because invalid users can be saved if we don't.
36
+ # See https://github.com/heartcombo/devise/issues/4673 for more details.
37
+ return if new_record?
38
+
32
39
  update_tracked_fields(request)
33
40
  save(validate: false)
34
41
  end
42
+
43
+ protected
44
+
45
+ def extract_ip_from(request)
46
+ request.remote_ip
47
+ end
48
+
35
49
  end
36
50
  end
37
51
  end