devise 4.2.0 → 4.4.3

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.

Potentially problematic release.


This version of devise might be problematic. Click here for more details.

Files changed (225) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +27 -5
  3. data/CHANGELOG.md +65 -0
  4. data/CONTRIBUTING.md +68 -28
  5. data/Gemfile +3 -1
  6. data/Gemfile.lock +79 -83
  7. data/ISSUE_TEMPLATE.md +19 -0
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +108 -11
  10. data/Rakefile +1 -0
  11. data/app/controllers/devise/confirmations_controller.rb +2 -0
  12. data/app/controllers/devise/omniauth_callbacks_controller.rb +4 -2
  13. data/app/controllers/devise/passwords_controller.rb +2 -0
  14. data/app/controllers/devise/registrations_controller.rb +6 -3
  15. data/app/controllers/devise/sessions_controller.rb +3 -1
  16. data/app/controllers/devise/unlocks_controller.rb +2 -0
  17. data/app/controllers/devise_controller.rb +2 -0
  18. data/app/helpers/devise_helper.rb +2 -0
  19. data/app/mailers/devise/mailer.rb +6 -0
  20. data/app/views/devise/confirmations/new.html.erb +1 -1
  21. data/app/views/devise/mailer/email_changed.html.erb +7 -0
  22. data/app/views/devise/passwords/new.html.erb +1 -1
  23. data/app/views/devise/registrations/edit.html.erb +1 -1
  24. data/app/views/devise/registrations/new.html.erb +1 -1
  25. data/app/views/devise/sessions/new.html.erb +1 -1
  26. data/app/views/devise/unlocks/new.html.erb +1 -1
  27. data/config/locales/en.yml +2 -0
  28. data/devise.gemspec +3 -1
  29. data/gemfiles/Gemfile.rails-4.1-stable +3 -1
  30. data/gemfiles/Gemfile.rails-4.1-stable.lock +67 -70
  31. data/gemfiles/Gemfile.rails-4.2-stable +3 -1
  32. data/gemfiles/Gemfile.rails-4.2-stable.lock +69 -73
  33. data/gemfiles/Gemfile.rails-5.0-stable +33 -0
  34. data/gemfiles/Gemfile.rails-5.0-stable.lock +192 -0
  35. data/gemfiles/Gemfile.rails-5.2-rc1 +26 -0
  36. data/gemfiles/Gemfile.rails-5.2-rc1.lock +201 -0
  37. data/guides/bug_report_templates/integration_test.rb +2 -0
  38. data/lib/devise/controllers/helpers.rb +2 -0
  39. data/lib/devise/controllers/rememberable.rb +2 -0
  40. data/lib/devise/controllers/scoped_views.rb +2 -0
  41. data/lib/devise/controllers/sign_in_out.rb +6 -1
  42. data/lib/devise/controllers/store_location.rb +25 -7
  43. data/lib/devise/controllers/url_helpers.rb +2 -0
  44. data/lib/devise/delegator.rb +2 -0
  45. data/lib/devise/encryptor.rb +2 -0
  46. data/lib/devise/failure_app.rb +14 -12
  47. data/lib/devise/hooks/activatable.rb +2 -0
  48. data/lib/devise/hooks/csrf_cleaner.rb +2 -0
  49. data/lib/devise/hooks/forgetable.rb +2 -0
  50. data/lib/devise/hooks/lockable.rb +6 -1
  51. data/lib/devise/hooks/proxy.rb +2 -0
  52. data/lib/devise/hooks/rememberable.rb +2 -0
  53. data/lib/devise/hooks/timeoutable.rb +2 -0
  54. data/lib/devise/hooks/trackable.rb +2 -0
  55. data/lib/devise/mailers/helpers.rb +6 -3
  56. data/lib/devise/mapping.rb +2 -0
  57. data/lib/devise/models/authenticatable.rb +4 -2
  58. data/lib/devise/models/confirmable.rb +53 -17
  59. data/lib/devise/models/database_authenticatable.rb +40 -4
  60. data/lib/devise/models/lockable.rb +8 -2
  61. data/lib/devise/models/omniauthable.rb +2 -0
  62. data/lib/devise/models/recoverable.rb +26 -9
  63. data/lib/devise/models/registerable.rb +2 -0
  64. data/lib/devise/models/rememberable.rb +4 -2
  65. data/lib/devise/models/timeoutable.rb +2 -0
  66. data/lib/devise/models/trackable.rb +7 -0
  67. data/lib/devise/models/validatable.rb +10 -3
  68. data/lib/devise/models.rb +3 -1
  69. data/lib/devise/modules.rb +2 -0
  70. data/lib/devise/omniauth/config.rb +2 -0
  71. data/lib/devise/omniauth/url_helpers.rb +2 -0
  72. data/lib/devise/omniauth.rb +2 -0
  73. data/lib/devise/orm/active_record.rb +2 -0
  74. data/lib/devise/orm/mongoid.rb +2 -0
  75. data/lib/devise/parameter_filter.rb +2 -0
  76. data/lib/devise/parameter_sanitizer.rb +2 -0
  77. data/lib/devise/rails/routes.rb +3 -1
  78. data/lib/devise/rails/warden_compat.rb +2 -0
  79. data/lib/devise/rails.rb +3 -5
  80. data/lib/devise/secret_key_finder.rb +25 -0
  81. data/lib/devise/strategies/authenticatable.rb +2 -0
  82. data/lib/devise/strategies/base.rb +2 -0
  83. data/lib/devise/strategies/database_authenticatable.rb +2 -0
  84. data/lib/devise/strategies/rememberable.rb +2 -0
  85. data/lib/devise/test/controller_helpers.rb +4 -1
  86. data/lib/devise/test/integration_helpers.rb +2 -0
  87. data/lib/devise/test_helpers.rb +3 -1
  88. data/lib/devise/time_inflector.rb +2 -0
  89. data/lib/devise/token_generator.rb +2 -0
  90. data/lib/devise/version.rb +3 -1
  91. data/lib/devise.rb +17 -2
  92. data/lib/generators/active_record/devise_generator.rb +15 -2
  93. data/lib/generators/active_record/templates/migration.rb +3 -1
  94. data/lib/generators/active_record/templates/migration_existing.rb +2 -0
  95. data/lib/generators/devise/controllers_generator.rb +2 -0
  96. data/lib/generators/devise/devise_generator.rb +4 -2
  97. data/lib/generators/devise/install_generator.rb +2 -0
  98. data/lib/generators/devise/orm_helpers.rb +7 -1
  99. data/lib/generators/devise/views_generator.rb +7 -8
  100. data/lib/generators/mongoid/devise_generator.rb +2 -0
  101. data/lib/generators/templates/controllers/confirmations_controller.rb +2 -0
  102. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +2 -0
  103. data/lib/generators/templates/controllers/passwords_controller.rb +2 -0
  104. data/lib/generators/templates/controllers/registrations_controller.rb +4 -2
  105. data/lib/generators/templates/controllers/sessions_controller.rb +3 -1
  106. data/lib/generators/templates/controllers/unlocks_controller.rb +2 -0
  107. data/lib/generators/templates/devise.rb +10 -1
  108. data/lib/generators/templates/markerb/email_changed.markerb +7 -0
  109. data/lib/generators/templates/markerb/password_change.markerb +2 -2
  110. data/test/controllers/custom_registrations_controller_test.rb +2 -0
  111. data/test/controllers/custom_strategy_test.rb +2 -0
  112. data/test/controllers/helper_methods_test.rb +2 -0
  113. data/test/controllers/helpers_test.rb +5 -3
  114. data/test/controllers/inherited_controller_i18n_messages_test.rb +2 -0
  115. data/test/controllers/internal_helpers_test.rb +2 -0
  116. data/test/controllers/load_hooks_controller_test.rb +2 -0
  117. data/test/controllers/passwords_controller_test.rb +2 -0
  118. data/test/controllers/sessions_controller_test.rb +2 -0
  119. data/test/controllers/url_helpers_test.rb +2 -0
  120. data/test/delegator_test.rb +2 -0
  121. data/test/devise_test.rb +2 -0
  122. data/test/failure_app_test.rb +2 -0
  123. data/test/generators/active_record_generator_test.rb +47 -0
  124. data/test/generators/controllers_generator_test.rb +2 -0
  125. data/test/generators/devise_generator_test.rb +2 -0
  126. data/test/generators/install_generator_test.rb +2 -0
  127. data/test/generators/mongoid_generator_test.rb +2 -0
  128. data/test/generators/views_generator_test.rb +2 -0
  129. data/test/helpers/devise_helper_test.rb +2 -0
  130. data/test/integration/authenticatable_test.rb +10 -2
  131. data/test/integration/confirmable_test.rb +2 -0
  132. data/test/integration/database_authenticatable_test.rb +2 -0
  133. data/test/integration/http_authenticatable_test.rb +8 -0
  134. data/test/integration/lockable_test.rb +5 -3
  135. data/test/integration/mounted_engine_test.rb +2 -0
  136. data/test/integration/omniauthable_test.rb +13 -0
  137. data/test/integration/recoverable_test.rb +2 -0
  138. data/test/integration/registerable_test.rb +2 -0
  139. data/test/integration/rememberable_test.rb +9 -1
  140. data/test/integration/timeoutable_test.rb +2 -0
  141. data/test/integration/trackable_test.rb +7 -0
  142. data/test/mailers/confirmation_instructions_test.rb +2 -0
  143. data/test/mailers/email_changed_test.rb +132 -0
  144. data/test/mailers/mailer_test.rb +20 -0
  145. data/test/mailers/reset_password_instructions_test.rb +2 -0
  146. data/test/mailers/unlock_instructions_test.rb +2 -0
  147. data/test/mapping_test.rb +2 -0
  148. data/test/models/authenticatable_test.rb +2 -0
  149. data/test/models/confirmable_test.rb +30 -0
  150. data/test/models/database_authenticatable_test.rb +15 -1
  151. data/test/models/lockable_test.rb +2 -0
  152. data/test/models/omniauthable_test.rb +2 -0
  153. data/test/models/recoverable_test.rb +13 -1
  154. data/test/models/registerable_test.rb +2 -0
  155. data/test/models/rememberable_test.rb +2 -0
  156. data/test/models/serializable_test.rb +6 -0
  157. data/test/models/timeoutable_test.rb +2 -0
  158. data/test/models/trackable_test.rb +21 -0
  159. data/test/models/validatable_test.rb +4 -2
  160. data/test/models_test.rb +2 -0
  161. data/test/omniauth/config_test.rb +11 -7
  162. data/test/omniauth/url_helpers_test.rb +2 -0
  163. data/test/orm/active_record.rb +9 -2
  164. data/test/orm/mongoid.rb +3 -1
  165. data/test/parameter_sanitizer_test.rb +2 -0
  166. data/test/rails_app/app/active_record/admin.rb +2 -0
  167. data/test/rails_app/app/active_record/shim.rb +2 -0
  168. data/test/rails_app/app/active_record/user.rb +14 -1
  169. data/test/rails_app/app/active_record/user_on_engine.rb +2 -0
  170. data/test/rails_app/app/active_record/user_on_main_app.rb +2 -0
  171. data/test/rails_app/app/active_record/user_with_validations.rb +12 -0
  172. data/test/rails_app/app/active_record/user_without_email.rb +2 -0
  173. data/test/rails_app/app/controllers/admins/sessions_controller.rb +2 -0
  174. data/test/rails_app/app/controllers/admins_controller.rb +2 -0
  175. data/test/rails_app/app/controllers/application_controller.rb +2 -0
  176. data/test/rails_app/app/controllers/application_with_fake_engine.rb +2 -0
  177. data/test/rails_app/app/controllers/custom/registrations_controller.rb +2 -0
  178. data/test/rails_app/app/controllers/home_controller.rb +3 -1
  179. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  180. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  181. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +3 -1
  182. data/test/rails_app/app/controllers/users_controller.rb +5 -3
  183. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  184. data/test/rails_app/app/mailers/users/from_proc_mailer.rb +2 -0
  185. data/test/rails_app/app/mailers/users/mailer.rb +2 -0
  186. data/test/rails_app/app/mailers/users/reply_to_mailer.rb +2 -0
  187. data/test/rails_app/app/mongoid/admin.rb +2 -0
  188. data/test/rails_app/app/mongoid/shim.rb +2 -0
  189. data/test/rails_app/app/mongoid/user.rb +11 -0
  190. data/test/rails_app/app/mongoid/user_on_engine.rb +2 -0
  191. data/test/rails_app/app/mongoid/user_on_main_app.rb +2 -0
  192. data/test/rails_app/app/mongoid/user_with_validations.rb +37 -0
  193. data/test/rails_app/app/mongoid/user_without_email.rb +2 -0
  194. data/test/rails_app/config/application.rb +6 -2
  195. data/test/rails_app/config/boot.rb +16 -3
  196. data/test/rails_app/config/environment.rb +2 -0
  197. data/test/rails_app/config/environments/development.rb +2 -0
  198. data/test/rails_app/config/environments/production.rb +2 -0
  199. data/test/rails_app/config/environments/test.rb +2 -0
  200. data/test/rails_app/config/initializers/backtrace_silencers.rb +2 -0
  201. data/test/rails_app/config/initializers/devise.rb +2 -0
  202. data/test/rails_app/config/initializers/inflections.rb +2 -0
  203. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  204. data/test/rails_app/config/initializers/session_store.rb +2 -0
  205. data/test/rails_app/config/routes.rb +2 -0
  206. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +2 -0
  207. data/test/rails_app/db/schema.rb +2 -0
  208. data/test/rails_app/lib/shared_admin.rb +7 -1
  209. data/test/rails_app/lib/shared_user.rb +2 -0
  210. data/test/rails_app/lib/shared_user_without_email.rb +2 -0
  211. data/test/rails_app/lib/shared_user_without_omniauth.rb +2 -0
  212. data/test/rails_test.rb +2 -0
  213. data/test/routes_test.rb +7 -5
  214. data/test/secret_key_finder_test.rb +97 -0
  215. data/test/support/action_controller/record_identifier.rb +2 -0
  216. data/test/support/assertions.rb +2 -0
  217. data/test/support/helpers.rb +6 -0
  218. data/test/support/http_method_compatibility.rb +2 -0
  219. data/test/support/integration.rb +3 -0
  220. data/test/support/webrat/integrations/rails.rb +2 -0
  221. data/test/test/controller_helpers_test.rb +16 -1
  222. data/test/test/integration_helpers_test.rb +2 -0
  223. data/test/test_helper.rb +2 -0
  224. data/test/test_models.rb +2 -0
  225. metadata +23 -5
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Controllers
3
5
  # Those helpers are convenience methods added to ApplicationController.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Controllers
3
5
  # A module that may be optionally included in a controller in order
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Controllers
3
5
  module ScopedViews
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Controllers
3
5
  # Provide sign in and sign out functionality.
4
6
  # Included by default in all controllers.
5
7
  module SignInOut
6
8
  # Return true if the given scope is signed in session. If no scope given, return
7
- # true if any scope is signed in. Does not run authentication hooks.
9
+ # true if any scope is signed in. This will run authentication hooks, which may
10
+ # cause exceptions to be thrown from this method; if you simply want to check
11
+ # if a scope has already previously been authenticated without running
12
+ # authentication hooks, you can directly call `warden.authenticated?(scope: scope)`
8
13
  def signed_in?(scope=nil)
9
14
  [scope || Devise.mappings.keys].flatten.any? do |_scope|
10
15
  warden.authenticate?(scope: _scope)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "uri"
2
4
 
3
5
  module Devise
@@ -29,16 +31,13 @@ module Devise
29
31
  # Example:
30
32
  #
31
33
  # store_location_for(:user, dashboard_path)
32
- # redirect_to user_omniauth_authorize_path(:facebook)
34
+ # redirect_to user_facebook_omniauth_authorize_path
33
35
  #
34
36
  def store_location_for(resource_or_scope, location)
35
37
  session_key = stored_location_key_for(resource_or_scope)
36
- uri = parse_uri(location)
37
- if uri
38
- path = [uri.path.sub(/\A\/+/, '/'), uri.query].compact.join('?')
39
- path = [path, uri.fragment].compact.join('#')
40
- session[session_key] = path
41
- end
38
+
39
+ path = extract_path_from_location(location)
40
+ session[session_key] = path if path
42
41
  end
43
42
 
44
43
  private
@@ -53,6 +52,25 @@ module Devise
53
52
  scope = Devise::Mapping.find_scope!(resource_or_scope)
54
53
  "#{scope}_return_to"
55
54
  end
55
+
56
+ def extract_path_from_location(location)
57
+ uri = parse_uri(location)
58
+
59
+ if uri
60
+ path = remove_domain_from_uri(uri)
61
+ path = add_fragment_back_to_path(uri, path)
62
+
63
+ path
64
+ end
65
+ end
66
+
67
+ def remove_domain_from_uri(uri)
68
+ [uri.path.sub(/\A\/+/, '/'), uri.query].compact.join('?')
69
+ end
70
+
71
+ def add_fragment_back_to_path(uri, path)
72
+ [path, uri.fragment].compact.join('#')
73
+ end
56
74
  end
57
75
  end
58
76
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Controllers
3
5
  # Create url helpers to be used with resource/scope configuration. Acts as
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  # Checks the scope in the given environment and returns the associated failure app.
3
5
  class Delegator
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bcrypt'
2
4
 
3
5
  module Devise
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_controller/metal"
2
4
 
3
5
  module Devise
4
6
  # Failure application that will be called every time :warden is thrown from
5
- # any strategy or hook. Responsible for redirect the user to the sign in
6
- # page based on current scope and mapping. If no scope is given, redirect
7
- # to the default_url.
7
+ # any strategy or hook. It is responsible for redirecting the user to the sign
8
+ # in page based on current scope and mapping. If no scope is given, it
9
+ # redirects to the default_url.
8
10
  class FailureApp < ActionController::Metal
9
11
  include ActionController::UrlFor
10
12
  include ActionController::Redirecting
@@ -160,12 +162,12 @@ module Devise
160
162
  %w(html */*).include? request_format.to_s
161
163
  end
162
164
 
163
- # Choose whether we should respond in a http authentication fashion,
165
+ # Choose whether we should respond in an HTTP authentication fashion,
164
166
  # including 401 and optional headers.
165
167
  #
166
- # This method allows the user to explicitly disable http authentication
167
- # on ajax requests in case they want to redirect on failures instead of
168
- # handling the errors on their own. This is useful in case your ajax API
168
+ # This method allows the user to explicitly disable HTTP authentication
169
+ # on AJAX requests in case they want to redirect on failures instead of
170
+ # handling the errors on their own. This is useful in case your AJAX API
169
171
  # is the same as your public API and uses a format like JSON (so you
170
172
  # cannot mark JSON as a navigational format).
171
173
  def http_auth?
@@ -176,7 +178,7 @@ module Devise
176
178
  end
177
179
  end
178
180
 
179
- # It does not make sense to send authenticate headers in ajax requests
181
+ # It doesn't make sense to send authenticate headers in AJAX requests
180
182
  # or if the user disabled them.
181
183
  def http_auth_header?
182
184
  scope_class.http_authenticatable && !request.xhr?
@@ -225,10 +227,10 @@ module Devise
225
227
  warden_options[:attempted_path]
226
228
  end
227
229
 
228
- # Stores requested uri to redirect the user after signing in. We cannot use
229
- # scoped session provided by warden here, since the user is not authenticated
230
- # yet, but we still need to store the uri based on scope, so different scopes
231
- # would never use the same uri to redirect.
230
+ # Stores requested URI to redirect the user after signing in. We can't use
231
+ # the scoped session provided by warden here, since the user is not
232
+ # authenticated yet, but we still need to store the URI based on scope, so
233
+ # different scopes would never use the same URI to redirect.
232
234
  def store_location!
233
235
  store_location_for(scope, attempted_path) if request.get? && !http_auth?
234
236
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Deny user access whenever their account is not active yet.
2
4
  # We need this as hook to validate the user activity on each request
3
5
  # and in case the user is using other strategies beside Devise ones.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Warden::Manager.after_authentication do |record, warden, options|
2
4
  clean_up_for_winning_strategy = !warden.winning_strategy.respond_to?(:clean_up_csrf?) ||
3
5
  warden.winning_strategy.clean_up_csrf?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Before logout hook to forget the user in the given scope, if it responds
2
4
  # to forget_me! Also clear remember token to ensure the user won't be
3
5
  # remembered again. Notice that we forget the user unless the record is not persisted.
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # After each sign in, if resource responds to failed_attempts, sets it to 0
2
4
  # This is only triggered when the user is explicitly set (with set_user)
3
5
  Warden::Manager.after_set_user except: :fetch do |record, warden, options|
4
6
  if record.respond_to?(:failed_attempts) && warden.authenticated?(options[:scope])
5
- record.update_attribute(:failed_attempts, 0) unless record.failed_attempts.to_i.zero?
7
+ unless record.failed_attempts.to_i.zero?
8
+ record.failed_attempts = 0
9
+ record.save(validate: false)
10
+ end
6
11
  end
7
12
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Hooks
3
5
  # A small warden proxy so we can remember, forget and
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Warden::Manager.after_set_user except: :fetch do |record, warden, options|
2
4
  scope = options[:scope]
3
5
  if record.respond_to?(:remember_me) && options[:store] != false &&
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Each time a record is set we check whether its session has already timed out
2
4
  # or not, based on last request time. If so, the record is logged out and
3
5
  # redirected to the sign in page. Also, each time the request comes and the
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # After each sign in, update sign in time, sign in count and sign in IP.
2
4
  # This is only triggered when the user is explicitly set (with set_user)
3
5
  # and on authentication. Retrieving the user from session (:fetch) does
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Mailers
3
5
  module Helpers
@@ -5,15 +7,16 @@ module Devise
5
7
 
6
8
  included do
7
9
  include Devise::Controllers::ScopedViews
8
- attr_reader :scope_name, :resource
9
10
  end
10
11
 
11
12
  protected
12
13
 
14
+ attr_reader :scope_name, :resource
15
+
13
16
  # Configure default email options
14
- def devise_mail(record, action, opts={})
17
+ def devise_mail(record, action, opts = {}, &block)
15
18
  initialize_from_record(record)
16
- mail headers_for(action, opts)
19
+ mail headers_for(action, opts), &block
17
20
  end
18
21
 
19
22
  def initialize_from_record(record)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  # Responsible for handling devise mappings and routes configuration. Each
3
5
  # resource configured by devise_for in routes is actually creating a mapping
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_model/version'
2
4
  require 'devise/hooks/activatable'
3
5
  require 'devise/hooks/csrf_cleaner'
@@ -102,7 +104,7 @@ module Devise
102
104
  # and passing a new list of attributes you want to exempt. All attributes
103
105
  # given to :except will simply add names to exempt to Devise internal list.
104
106
  def serializable_hash(options = nil)
105
- options ||= {}
107
+ options = options.try(:dup) || {}
106
108
  options[:except] = Array(options[:except])
107
109
 
108
110
  if options[:force_except]
@@ -256,7 +258,7 @@ module Devise
256
258
  # end
257
259
  #
258
260
  # Finally, notice that Devise also queries for users in other scenarios
259
- # besides authentication, for example when retrieving an user to send
261
+ # besides authentication, for example when retrieving a user to send
260
262
  # an e-mail for password reset. In such cases, find_for_authentication
261
263
  # is not called.
262
264
  def find_for_authentication(tainted_conditions)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Models
3
5
  # Confirmable is responsible to verify if an account is already confirmed to
@@ -26,7 +28,9 @@ module Devise
26
28
  # initial account confirmation) to be applied. Requires additional unconfirmed_email
27
29
  # db field to be set up (t.reconfirmable in migrations). Until confirmed, new email is
28
30
  # stored in unconfirmed email column, and copied to email column on successful
29
- # confirmation.
31
+ # confirmation. Also, when used in conjunction with `send_email_changed_notification`,
32
+ # the notification is sent to the original email when the change is requested,
33
+ # not when the unconfirmed email is confirmed.
30
34
  # * +confirm_within+: the time before a sent confirmation token becomes invalid.
31
35
  # You can use this to force the user to confirm within a set period of time.
32
36
  # Confirmable will not generate a new token if a repeat confirmation is requested
@@ -44,7 +48,7 @@ module Devise
44
48
  included do
45
49
  before_create :generate_confirmation_token, if: :confirmation_required?
46
50
  after_create :skip_reconfirmation_in_callback!, if: :send_confirmation_notification?
47
- if respond_to?(:after_commit) # ActiveRecord
51
+ if defined?(ActiveRecord) && self < ActiveRecord::Base # ActiveRecord
48
52
  after_commit :send_on_create_confirmation_instructions, on: :create, if: :send_confirmation_notification?
49
53
  after_commit :send_reconfirmation_instructions, on: :update, if: :reconfirmation_required?
50
54
  else # Mongoid
@@ -223,7 +227,7 @@ module Devise
223
227
  # confirmation_period_expired? # will always return false
224
228
  #
225
229
  def confirmation_period_expired?
226
- self.class.confirm_within && self.confirmation_sent_at && (Time.now > self.confirmation_sent_at + self.class.confirm_within)
230
+ self.class.confirm_within && self.confirmation_sent_at && (Time.now.utc > self.confirmation_sent_at.utc + self.class.confirm_within)
227
231
  end
228
232
 
229
233
  # Checks whether the record requires any confirmation.
@@ -251,22 +255,44 @@ module Devise
251
255
  generate_confirmation_token && save(validate: false)
252
256
  end
253
257
 
254
- def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
255
- @reconfirmation_required = true
256
- self.unconfirmed_email = self.email
257
- self.email = self.email_was
258
- self.confirmation_token = nil
259
- generate_confirmation_token
258
+ if Devise.activerecord51?
259
+ def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
260
+ @reconfirmation_required = true
261
+ self.unconfirmed_email = self.email
262
+ self.email = self.email_in_database
263
+ self.confirmation_token = nil
264
+ generate_confirmation_token
265
+ end
266
+ else
267
+ def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
268
+ @reconfirmation_required = true
269
+ self.unconfirmed_email = self.email
270
+ self.email = self.email_was
271
+ self.confirmation_token = nil
272
+ generate_confirmation_token
273
+ end
260
274
  end
261
275
 
262
- def postpone_email_change?
263
- postpone = self.class.reconfirmable &&
264
- email_changed? &&
265
- !@bypass_confirmation_postpone &&
266
- self.email.present? &&
267
- (!@skip_reconfirmation_in_callback || !self.email_was.nil?)
268
- @bypass_confirmation_postpone = false
269
- postpone
276
+ if Devise.activerecord51?
277
+ def postpone_email_change?
278
+ postpone = self.class.reconfirmable &&
279
+ will_save_change_to_email? &&
280
+ !@bypass_confirmation_postpone &&
281
+ self.email.present? &&
282
+ (!@skip_reconfirmation_in_callback || !self.email_in_database.nil?)
283
+ @bypass_confirmation_postpone = false
284
+ postpone
285
+ end
286
+ else
287
+ def postpone_email_change?
288
+ postpone = self.class.reconfirmable &&
289
+ email_changed? &&
290
+ !@bypass_confirmation_postpone &&
291
+ self.email.present? &&
292
+ (!@skip_reconfirmation_in_callback || !self.email_was.nil?)
293
+ @bypass_confirmation_postpone = false
294
+ postpone
295
+ end
270
296
  end
271
297
 
272
298
  def reconfirmation_required?
@@ -277,6 +303,16 @@ module Devise
277
303
  confirmation_required? && !@skip_confirmation_notification && self.email.present?
278
304
  end
279
305
 
306
+ # With reconfirmable, notify the original email when the user first
307
+ # requests the email change, instead of when the change is confirmed.
308
+ def send_email_changed_notification?
309
+ if self.class.reconfirmable
310
+ self.class.send_email_changed_notification && reconfirmation_required?
311
+ else
312
+ super
313
+ end
314
+ end
315
+
280
316
  # A callback initiated after successfully confirming. This can be
281
317
  # used to insert your own logic that is only run after the user successfully
282
318
  # confirms.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/strategies/database_authenticatable'
2
4
 
3
5
  module Devise
@@ -10,10 +12,14 @@ module Devise
10
12
  # DatabaseAuthenticatable adds the following options to devise_for:
11
13
  #
12
14
  # * +pepper+: a random string used to provide a more secure hash. Use
13
- # `rake secret` to generate new keys.
15
+ # `rails secret` to generate new keys.
14
16
  #
15
17
  # * +stretches+: the cost given to bcrypt.
16
18
  #
19
+ # * +send_email_changed_notification+: notify original email when it changes.
20
+ #
21
+ # * +send_password_change_notification+: notify email when password changes.
22
+ #
17
23
  # == Examples
18
24
  #
19
25
  # User.find(1).valid_password?('password123') # returns true/false
@@ -22,6 +28,7 @@ module Devise
22
28
  extend ActiveSupport::Concern
23
29
 
24
30
  included do
31
+ after_update :send_email_changed_notification, if: :send_email_changed_notification?
25
32
  after_update :send_password_change_notification, if: :send_password_change_notification?
26
33
 
27
34
  attr_reader :password, :current_password
@@ -132,6 +139,19 @@ module Devise
132
139
  encrypted_password[0,29] if encrypted_password
133
140
  end
134
141
 
142
+ if Devise.activerecord51?
143
+ # Send notification to user when email changes.
144
+ def send_email_changed_notification
145
+ send_devise_notification(:email_changed, to: email_before_last_save)
146
+ end
147
+ else
148
+ # Send notification to user when email changes.
149
+ def send_email_changed_notification
150
+ send_devise_notification(:email_changed, to: email_was)
151
+ end
152
+ end
153
+
154
+ # Send notification to user when password changes.
135
155
  def send_password_change_notification
136
156
  send_devise_notification(:password_change)
137
157
  end
@@ -147,12 +167,28 @@ module Devise
147
167
  Devise::Encryptor.digest(self.class, password)
148
168
  end
149
169
 
150
- def send_password_change_notification?
151
- self.class.send_password_change_notification && encrypted_password_changed?
170
+ if Devise.activerecord51?
171
+ def send_email_changed_notification?
172
+ self.class.send_email_changed_notification && saved_change_to_email?
173
+ end
174
+ else
175
+ def send_email_changed_notification?
176
+ self.class.send_email_changed_notification && email_changed?
177
+ end
178
+ end
179
+
180
+ if Devise.activerecord51?
181
+ def send_password_change_notification?
182
+ self.class.send_password_change_notification && saved_change_to_encrypted_password?
183
+ end
184
+ else
185
+ def send_password_change_notification?
186
+ self.class.send_password_change_notification && encrypted_password_changed?
187
+ end
152
188
  end
153
189
 
154
190
  module ClassMethods
155
- Devise::Models.config(self, :pepper, :stretches, :send_password_change_notification)
191
+ Devise::Models.config(self, :pepper, :stretches, :send_email_changed_notification, :send_password_change_notification)
156
192
 
157
193
  # We assume this method already gets the sanitized values from the
158
194
  # 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
@@ -99,8 +101,7 @@ module Devise
99
101
  if super && !access_locked?
100
102
  true
101
103
  else
102
- self.failed_attempts ||= 0
103
- self.failed_attempts += 1
104
+ increment_failed_attempts
104
105
  if attempts_exceeded?
105
106
  lock_access! unless access_locked?
106
107
  else
@@ -109,6 +110,11 @@ module Devise
109
110
  false
110
111
  end
111
112
  end
113
+
114
+ def increment_failed_attempts
115
+ self.failed_attempts ||= 0
116
+ self.failed_attempts += 1
117
+ end
112
118
 
113
119
  def unauthenticated_message
114
120
  # If set to paranoid mode, do not show the locked message because it
@@ -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
 
@@ -33,10 +35,14 @@ module Devise
33
35
  # Update password saving the record and clearing token. Returns true if
34
36
  # the passwords are valid and the record was saved, false otherwise.
35
37
  def reset_password(new_password, new_password_confirmation)
36
- self.password = new_password
37
- self.password_confirmation = new_password_confirmation
38
-
39
- save
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
45
+ end
40
46
  end
41
47
 
42
48
  # Resets reset password token and send reset password instructions by email.
@@ -93,13 +99,24 @@ module Devise
93
99
  send_devise_notification(:reset_password_instructions, token, {})
94
100
  end
95
101
 
96
- def clear_reset_password_token?
97
- encrypted_password_changed = respond_to?(:encrypted_password_changed?) && encrypted_password_changed?
98
- authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
99
- respond_to?("#{attribute}_changed?") && send("#{attribute}_changed?")
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
100
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
101
117
 
102
- authentication_keys_changed || encrypted_password_changed
118
+ authentication_keys_changed || encrypted_password_changed
119
+ end
103
120
  end
104
121
 
105
122
  module ClassMethods
@@ -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
@@ -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.
@@ -74,7 +76,7 @@ module Devise
74
76
  elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt.presence)
75
77
  salt
76
78
  else
77
- raise "authenticable_salt returned nil for the #{self.class.name} model. " \
79
+ raise "authenticatable_salt returned nil for the #{self.class.name} model. " \
78
80
  "In order to use rememberable, you must ensure a password is always set " \
79
81
  "or have a remember_token column in your model or implement your own " \
80
82
  "rememberable_value in the model with custom logic."
@@ -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
@@ -29,6 +31,11 @@ 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/plataformatec/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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Models
3
5
  # Validatable creates all needed validations for a user email and password.
@@ -10,7 +12,7 @@ module Devise
10
12
  # Validatable adds the following options to devise_for:
11
13
  #
12
14
  # * +email_regexp+: the regular expression used to validate e-mails;
13
- # * +password_length+: a range expressing password length. Defaults to 8..72.
15
+ # * +password_length+: a range expressing password length. Defaults to 6..128.
14
16
  #
15
17
  module Validatable
16
18
  # All validations used by this module.
@@ -27,8 +29,13 @@ module Devise
27
29
 
28
30
  base.class_eval do
29
31
  validates_presence_of :email, if: :email_required?
30
- validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
31
- validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
32
+ if Devise.activerecord51?
33
+ validates_uniqueness_of :email, allow_blank: true, if: :will_save_change_to_email?
34
+ validates_format_of :email, with: email_regexp, allow_blank: true, if: :will_save_change_to_email?
35
+ else
36
+ validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
37
+ validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
38
+ end
32
39
 
33
40
  validates_presence_of :password, if: :password_required?
34
41
  validates_confirmation_of :password, if: :password_required?