devise 4.1.1 → 5.0.4

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 (255) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +68 -111
  3. data/MIT-LICENSE +2 -1
  4. data/README.md +315 -98
  5. data/app/controllers/devise/confirmations_controller.rb +3 -0
  6. data/app/controllers/devise/omniauth_callbacks_controller.rb +7 -5
  7. data/app/controllers/devise/passwords_controller.rb +10 -2
  8. data/app/controllers/devise/registrations_controller.rb +42 -20
  9. data/app/controllers/devise/sessions_controller.rb +9 -7
  10. data/app/controllers/devise/unlocks_controller.rb +3 -0
  11. data/app/controllers/devise_controller.rb +19 -3
  12. data/app/helpers/devise_helper.rb +3 -23
  13. data/app/mailers/devise/mailer.rb +10 -4
  14. data/app/views/devise/confirmations/new.html.erb +3 -3
  15. data/app/views/devise/mailer/email_changed.html.erb +7 -0
  16. data/app/views/devise/passwords/edit.html.erb +6 -6
  17. data/app/views/devise/passwords/new.html.erb +4 -4
  18. data/app/views/devise/registrations/edit.html.erb +13 -10
  19. data/app/views/devise/registrations/new.html.erb +9 -9
  20. data/app/views/devise/sessions/new.html.erb +8 -8
  21. data/app/views/devise/shared/_error_messages.html.erb +15 -0
  22. data/app/views/devise/shared/_links.html.erb +13 -13
  23. data/app/views/devise/unlocks/new.html.erb +3 -3
  24. data/config/locales/en.yml +5 -2
  25. data/lib/devise/controllers/helpers.rb +24 -9
  26. data/lib/devise/controllers/rememberable.rb +3 -1
  27. data/lib/devise/controllers/responder.rb +35 -0
  28. data/lib/devise/controllers/scoped_views.rb +2 -0
  29. data/lib/devise/controllers/sign_in_out.rb +31 -21
  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 +2 -0
  34. data/lib/devise/failure_app.rb +71 -38
  35. data/lib/devise/hooks/activatable.rb +3 -1
  36. data/lib/devise/hooks/csrf_cleaner.rb +8 -1
  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 +5 -3
  42. data/lib/devise/hooks/trackable.rb +2 -0
  43. data/lib/devise/mailers/helpers.rb +15 -18
  44. data/lib/devise/mapping.rb +4 -2
  45. data/lib/devise/models/authenticatable.rb +58 -44
  46. data/lib/devise/models/confirmable.rb +52 -14
  47. data/lib/devise/models/database_authenticatable.rb +52 -20
  48. data/lib/devise/models/lockable.rb +19 -5
  49. data/lib/devise/models/omniauthable.rb +4 -2
  50. data/lib/devise/models/recoverable.rb +22 -21
  51. data/lib/devise/models/registerable.rb +4 -0
  52. data/lib/devise/models/rememberable.rb +6 -4
  53. data/lib/devise/models/timeoutable.rb +3 -1
  54. data/lib/devise/models/trackable.rb +15 -1
  55. data/lib/devise/models/validatable.rb +10 -6
  56. data/lib/devise/models.rb +4 -1
  57. data/lib/devise/modules.rb +2 -0
  58. data/lib/devise/omniauth/config.rb +2 -0
  59. data/lib/devise/omniauth/url_helpers.rb +2 -51
  60. data/lib/devise/omniauth.rb +4 -5
  61. data/lib/devise/orm/active_record.rb +5 -1
  62. data/lib/devise/orm/mongoid.rb +6 -2
  63. data/lib/devise/orm.rb +80 -0
  64. data/lib/devise/parameter_filter.rb +4 -0
  65. data/lib/devise/parameter_sanitizer.rb +16 -58
  66. data/lib/devise/rails/routes.rb +12 -11
  67. data/lib/devise/rails/warden_compat.rb +2 -0
  68. data/lib/devise/rails.rb +16 -6
  69. data/lib/devise/strategies/authenticatable.rb +3 -1
  70. data/lib/devise/strategies/base.rb +2 -0
  71. data/lib/devise/strategies/database_authenticatable.rb +8 -1
  72. data/lib/devise/strategies/rememberable.rb +2 -0
  73. data/lib/devise/test/controller_helpers.rb +156 -0
  74. data/lib/devise/test/integration_helpers.rb +63 -0
  75. data/lib/devise/time_inflector.rb +2 -0
  76. data/lib/devise/token_generator.rb +2 -0
  77. data/lib/devise/version.rb +3 -1
  78. data/lib/devise.rb +69 -28
  79. data/lib/generators/active_record/devise_generator.rb +38 -16
  80. data/lib/generators/active_record/templates/migration.rb +3 -1
  81. data/lib/generators/active_record/templates/migration_existing.rb +2 -0
  82. data/lib/generators/devise/controllers_generator.rb +4 -2
  83. data/lib/generators/devise/devise_generator.rb +5 -3
  84. data/lib/generators/devise/install_generator.rb +3 -5
  85. data/lib/generators/devise/orm_helpers.rb +5 -3
  86. data/lib/generators/devise/views_generator.rb +8 -9
  87. data/lib/generators/mongoid/devise_generator.rb +7 -5
  88. data/lib/generators/templates/README +9 -8
  89. data/lib/generators/templates/controllers/confirmations_controller.rb +2 -0
  90. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +3 -1
  91. data/lib/generators/templates/controllers/passwords_controller.rb +2 -0
  92. data/lib/generators/templates/controllers/registrations_controller.rb +4 -2
  93. data/lib/generators/templates/controllers/sessions_controller.rb +3 -1
  94. data/lib/generators/templates/controllers/unlocks_controller.rb +2 -0
  95. data/lib/generators/templates/devise.rb +59 -11
  96. data/lib/generators/templates/markerb/email_changed.markerb +7 -0
  97. data/lib/generators/templates/markerb/password_change.markerb +2 -2
  98. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +5 -1
  99. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +10 -2
  100. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +5 -2
  101. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +12 -4
  102. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +11 -3
  103. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +7 -2
  104. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +4 -1
  105. metadata +23 -302
  106. data/.gitignore +0 -10
  107. data/.travis.yml +0 -44
  108. data/.yardopts +0 -9
  109. data/CODE_OF_CONDUCT.md +0 -22
  110. data/CONTRIBUTING.md +0 -16
  111. data/Gemfile +0 -30
  112. data/Gemfile.lock +0 -182
  113. data/Rakefile +0 -36
  114. data/bin/test +0 -13
  115. data/devise.gemspec +0 -26
  116. data/devise.png +0 -0
  117. data/gemfiles/Gemfile.rails-4.1-stable +0 -30
  118. data/gemfiles/Gemfile.rails-4.1-stable.lock +0 -170
  119. data/gemfiles/Gemfile.rails-4.2-stable +0 -30
  120. data/gemfiles/Gemfile.rails-4.2-stable.lock +0 -192
  121. data/gemfiles/Gemfile.rails-5.0-beta +0 -37
  122. data/gemfiles/Gemfile.rails-5.0-beta.lock +0 -199
  123. data/lib/devise/test_helpers.rb +0 -137
  124. data/test/controllers/custom_registrations_controller_test.rb +0 -40
  125. data/test/controllers/custom_strategy_test.rb +0 -64
  126. data/test/controllers/helper_methods_test.rb +0 -22
  127. data/test/controllers/helpers_test.rb +0 -316
  128. data/test/controllers/inherited_controller_i18n_messages_test.rb +0 -51
  129. data/test/controllers/internal_helpers_test.rb +0 -127
  130. data/test/controllers/load_hooks_controller_test.rb +0 -19
  131. data/test/controllers/passwords_controller_test.rb +0 -32
  132. data/test/controllers/sessions_controller_test.rb +0 -106
  133. data/test/controllers/url_helpers_test.rb +0 -65
  134. data/test/delegator_test.rb +0 -19
  135. data/test/devise_test.rb +0 -107
  136. data/test/failure_app_test.rb +0 -320
  137. data/test/generators/active_record_generator_test.rb +0 -83
  138. data/test/generators/controllers_generator_test.rb +0 -48
  139. data/test/generators/devise_generator_test.rb +0 -39
  140. data/test/generators/install_generator_test.rb +0 -24
  141. data/test/generators/mongoid_generator_test.rb +0 -23
  142. data/test/generators/views_generator_test.rb +0 -103
  143. data/test/helpers/devise_helper_test.rb +0 -49
  144. data/test/integration/authenticatable_test.rb +0 -698
  145. data/test/integration/confirmable_test.rb +0 -324
  146. data/test/integration/database_authenticatable_test.rb +0 -95
  147. data/test/integration/http_authenticatable_test.rb +0 -106
  148. data/test/integration/lockable_test.rb +0 -240
  149. data/test/integration/omniauthable_test.rb +0 -135
  150. data/test/integration/recoverable_test.rb +0 -347
  151. data/test/integration/registerable_test.rb +0 -357
  152. data/test/integration/rememberable_test.rb +0 -211
  153. data/test/integration/timeoutable_test.rb +0 -184
  154. data/test/integration/trackable_test.rb +0 -92
  155. data/test/mailers/confirmation_instructions_test.rb +0 -115
  156. data/test/mailers/reset_password_instructions_test.rb +0 -96
  157. data/test/mailers/unlock_instructions_test.rb +0 -91
  158. data/test/mapping_test.rb +0 -134
  159. data/test/models/authenticatable_test.rb +0 -23
  160. data/test/models/confirmable_test.rb +0 -511
  161. data/test/models/database_authenticatable_test.rb +0 -269
  162. data/test/models/lockable_test.rb +0 -350
  163. data/test/models/omniauthable_test.rb +0 -7
  164. data/test/models/recoverable_test.rb +0 -251
  165. data/test/models/registerable_test.rb +0 -7
  166. data/test/models/rememberable_test.rb +0 -169
  167. data/test/models/serializable_test.rb +0 -49
  168. data/test/models/timeoutable_test.rb +0 -51
  169. data/test/models/trackable_test.rb +0 -41
  170. data/test/models/validatable_test.rb +0 -119
  171. data/test/models_test.rb +0 -153
  172. data/test/omniauth/config_test.rb +0 -57
  173. data/test/omniauth/url_helpers_test.rb +0 -51
  174. data/test/orm/active_record.rb +0 -17
  175. data/test/orm/mongoid.rb +0 -13
  176. data/test/parameter_sanitizer_test.rb +0 -131
  177. data/test/rails_app/Rakefile +0 -6
  178. data/test/rails_app/app/active_record/admin.rb +0 -6
  179. data/test/rails_app/app/active_record/shim.rb +0 -2
  180. data/test/rails_app/app/active_record/user.rb +0 -7
  181. data/test/rails_app/app/active_record/user_on_engine.rb +0 -7
  182. data/test/rails_app/app/active_record/user_on_main_app.rb +0 -7
  183. data/test/rails_app/app/active_record/user_without_email.rb +0 -8
  184. data/test/rails_app/app/controllers/admins/sessions_controller.rb +0 -6
  185. data/test/rails_app/app/controllers/admins_controller.rb +0 -6
  186. data/test/rails_app/app/controllers/application_controller.rb +0 -11
  187. data/test/rails_app/app/controllers/application_with_fake_engine.rb +0 -30
  188. data/test/rails_app/app/controllers/custom/registrations_controller.rb +0 -31
  189. data/test/rails_app/app/controllers/home_controller.rb +0 -29
  190. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +0 -2
  191. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +0 -2
  192. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +0 -14
  193. data/test/rails_app/app/controllers/users_controller.rb +0 -31
  194. data/test/rails_app/app/helpers/application_helper.rb +0 -3
  195. data/test/rails_app/app/mailers/users/from_proc_mailer.rb +0 -3
  196. data/test/rails_app/app/mailers/users/mailer.rb +0 -3
  197. data/test/rails_app/app/mailers/users/reply_to_mailer.rb +0 -4
  198. data/test/rails_app/app/mongoid/admin.rb +0 -29
  199. data/test/rails_app/app/mongoid/shim.rb +0 -23
  200. data/test/rails_app/app/mongoid/user.rb +0 -39
  201. data/test/rails_app/app/mongoid/user_on_engine.rb +0 -39
  202. data/test/rails_app/app/mongoid/user_on_main_app.rb +0 -39
  203. data/test/rails_app/app/mongoid/user_without_email.rb +0 -33
  204. data/test/rails_app/app/views/admins/index.html.erb +0 -1
  205. data/test/rails_app/app/views/admins/sessions/new.html.erb +0 -2
  206. data/test/rails_app/app/views/home/admin_dashboard.html.erb +0 -1
  207. data/test/rails_app/app/views/home/index.html.erb +0 -1
  208. data/test/rails_app/app/views/home/join.html.erb +0 -1
  209. data/test/rails_app/app/views/home/private.html.erb +0 -1
  210. data/test/rails_app/app/views/home/user_dashboard.html.erb +0 -1
  211. data/test/rails_app/app/views/layouts/application.html.erb +0 -24
  212. data/test/rails_app/app/views/users/edit_form.html.erb +0 -1
  213. data/test/rails_app/app/views/users/index.html.erb +0 -1
  214. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +0 -1
  215. data/test/rails_app/app/views/users/sessions/new.html.erb +0 -1
  216. data/test/rails_app/bin/bundle +0 -3
  217. data/test/rails_app/bin/rails +0 -4
  218. data/test/rails_app/bin/rake +0 -4
  219. data/test/rails_app/config/application.rb +0 -44
  220. data/test/rails_app/config/boot.rb +0 -14
  221. data/test/rails_app/config/database.yml +0 -18
  222. data/test/rails_app/config/environment.rb +0 -5
  223. data/test/rails_app/config/environments/development.rb +0 -30
  224. data/test/rails_app/config/environments/production.rb +0 -84
  225. data/test/rails_app/config/environments/test.rb +0 -46
  226. data/test/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  227. data/test/rails_app/config/initializers/devise.rb +0 -180
  228. data/test/rails_app/config/initializers/inflections.rb +0 -2
  229. data/test/rails_app/config/initializers/secret_token.rb +0 -3
  230. data/test/rails_app/config/initializers/session_store.rb +0 -1
  231. data/test/rails_app/config/routes.rb +0 -126
  232. data/test/rails_app/config.ru +0 -4
  233. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -71
  234. data/test/rails_app/db/schema.rb +0 -55
  235. data/test/rails_app/lib/shared_admin.rb +0 -17
  236. data/test/rails_app/lib/shared_user.rb +0 -30
  237. data/test/rails_app/lib/shared_user_without_email.rb +0 -26
  238. data/test/rails_app/lib/shared_user_without_omniauth.rb +0 -13
  239. data/test/rails_app/public/404.html +0 -26
  240. data/test/rails_app/public/422.html +0 -26
  241. data/test/rails_app/public/500.html +0 -26
  242. data/test/rails_app/public/favicon.ico +0 -0
  243. data/test/rails_test.rb +0 -9
  244. data/test/routes_test.rb +0 -279
  245. data/test/support/action_controller/record_identifier.rb +0 -10
  246. data/test/support/assertions.rb +0 -39
  247. data/test/support/helpers.rb +0 -77
  248. data/test/support/http_method_compatibility.rb +0 -51
  249. data/test/support/integration.rb +0 -92
  250. data/test/support/locale/en.yml +0 -8
  251. data/test/support/mongoid.yml +0 -6
  252. data/test/support/webrat/integrations/rails.rb +0 -33
  253. data/test/test_helper.rb +0 -34
  254. data/test/test_helpers_test.rb +0 -178
  255. data/test/test_models.rb +0 -33
@@ -1,11 +1,11 @@
1
1
  <h2>Resend unlock instructions</h2>
2
2
 
3
3
  <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
4
- <%= devise_error_messages! %>
4
+ <%= render "devise/shared/error_messages", resource: resource %>
5
5
 
6
6
  <div class="field">
7
- <%= f.label :email %><br />
8
- <%= f.email_field :email, autofocus: true %>
7
+ <p><%= f.label :email %></p>
8
+ <p><%= f.email_field :email, autofocus: true, autocomplete: "email" %></p>
9
9
  </div>
10
10
 
11
11
  <div class="actions">
@@ -1,4 +1,4 @@
1
- # Additional translations at https://github.com/plataformatec/devise/wiki/I18n
1
+ # Additional translations at https://github.com/heartcombo/devise/wiki/I18n
2
2
 
3
3
  en:
4
4
  devise:
@@ -23,6 +23,8 @@ en:
23
23
  subject: "Reset password instructions"
24
24
  unlock_instructions:
25
25
  subject: "Unlock instructions"
26
+ email_changed:
27
+ subject: "Email Changed"
26
28
  password_change:
27
29
  subject: "Password Changed"
28
30
  omniauth_callbacks:
@@ -40,8 +42,9 @@ en:
40
42
  signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
41
43
  signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
42
44
  signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
43
- update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address."
45
+ update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
44
46
  updated: "Your account has been updated successfully."
47
+ updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again."
45
48
  sessions:
46
49
  signed_in: "Signed in successfully."
47
50
  signed_out: "Signed out successfully."
@@ -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.
@@ -34,16 +36,17 @@ module Devise
34
36
  # before_action ->{ authenticate_blogger! :admin } # Redirects to the admin login page
35
37
  # current_blogger :user # Preferably returns a User if one is signed in
36
38
  #
37
- def devise_group(group_name, opts={})
39
+ def devise_group(group_name, opts = {})
38
40
  mappings = "[#{ opts[:contains].map { |m| ":#{m}" }.join(',') }]"
39
41
 
40
42
  class_eval <<-METHODS, __FILE__, __LINE__ + 1
41
- def authenticate_#{group_name}!(favourite=nil, opts={})
43
+ def authenticate_#{group_name}!(favorite = nil, opts = {})
42
44
  unless #{group_name}_signed_in?
43
45
  mappings = #{mappings}
44
- mappings.unshift mappings.delete(favourite.to_sym) if favourite
46
+ mappings.unshift mappings.delete(favorite.to_sym) if favorite
45
47
  mappings.each do |mapping|
46
48
  opts[:scope] = mapping
49
+ opts[:locale] = I18n.locale
47
50
  warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
48
51
  end
49
52
  end
@@ -55,9 +58,9 @@ module Devise
55
58
  end
56
59
  end
57
60
 
58
- def current_#{group_name}(favourite=nil)
61
+ def current_#{group_name}(favorite = nil)
59
62
  mappings = #{mappings}
60
- mappings.unshift mappings.delete(favourite.to_sym) if favourite
63
+ mappings.unshift mappings.delete(favorite.to_sym) if favorite
61
64
  mappings.each do |mapping|
62
65
  current = warden.authenticate(scope: mapping)
63
66
  return current if current
@@ -111,8 +114,9 @@ module Devise
111
114
  mapping = mapping.name
112
115
 
113
116
  class_eval <<-METHODS, __FILE__, __LINE__ + 1
114
- def authenticate_#{mapping}!(opts={})
117
+ def authenticate_#{mapping}!(opts = {})
115
118
  opts[:scope] = :#{mapping}
119
+ opts[:locale] = I18n.locale
116
120
  warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
117
121
  end
118
122
 
@@ -138,7 +142,7 @@ module Devise
138
142
 
139
143
  # The main accessor for the warden proxy instance
140
144
  def warden
141
- request.env['warden']
145
+ request.env['warden'] or raise MissingWarden
142
146
  end
143
147
 
144
148
  # Return true if it's a devise_controller. false to all controllers unless
@@ -250,7 +254,7 @@ module Devise
250
254
  # Overwrite Rails' handle unverified request to sign out all scopes,
251
255
  # clear run strategies and remove cached variables.
252
256
  def handle_unverified_request
253
- super # call the default behaviour which resets/nullifies/raises
257
+ super # call the default behavior which resets/nullifies/raises
254
258
  request.env["devise.skip_storage"] = true
255
259
  sign_out_all_scopes(false)
256
260
  end
@@ -266,7 +270,7 @@ module Devise
266
270
  # Check if flash messages should be emitted. Default is to do it on
267
271
  # navigational formats
268
272
  def is_flashing_format?
269
- is_navigational_format?
273
+ request.respond_to?(:flash) && is_navigational_format?
270
274
  end
271
275
 
272
276
  private
@@ -277,4 +281,15 @@ module Devise
277
281
  end
278
282
  end
279
283
  end
284
+
285
+ class MissingWarden < StandardError
286
+ def initialize
287
+ super "Devise could not find the `Warden::Proxy` instance on your request environment.\n" + \
288
+ "Make sure that your application is loading Devise and Warden as expected and that " + \
289
+ "the `Warden::Manager` middleware is present in your middleware stack.\n" + \
290
+ "If you are seeing this on one of your tests, ensure that your tests are either " + \
291
+ "executing the Rails middleware stack or that your tests are using the `Devise::Test::ControllerHelpers` " + \
292
+ "module to inject the `request.env['warden']` object for you."
293
+ end
294
+ end
280
295
  end
@@ -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
@@ -18,7 +20,7 @@ module Devise
18
20
 
19
21
  # Remembers the given resource by setting up a cookie
20
22
  def remember_me(resource)
21
- return if env["devise.skip_storage"]
23
+ return if request.env["devise.skip_storage"]
22
24
  scope = Devise::Mapping.find_scope!(resource)
23
25
  resource.remember_me!
24
26
  cookies.signed[remember_key(resource, scope)] = remember_cookie_values(resource)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module Controllers
5
+ # Custom Responder to configure default statuses that only apply to Devise,
6
+ # and allow to integrate more easily with Hotwire/Turbo.
7
+ class Responder < ActionController::Responder
8
+ if respond_to?(:error_status=) && respond_to?(:redirect_status=)
9
+ self.error_status = :ok
10
+ self.redirect_status = :found
11
+ else
12
+ # TODO: remove this support for older Rails versions, which aren't supported by Turbo
13
+ # and/or responders. It won't allow configuring a custom response, but it allows Devise
14
+ # to use these methods and defaults across the implementation more easily.
15
+ def self.error_status
16
+ :ok
17
+ end
18
+
19
+ def self.redirect_status
20
+ :found
21
+ end
22
+
23
+ def self.error_status=(*)
24
+ warn "[DEVISE] Setting the error status on the Devise responder has no effect with this " \
25
+ "version of `responders`, please make sure you're using a newer version. Check the changelog for more info."
26
+ end
27
+
28
+ def self.redirect_status=(*)
29
+ warn "[DEVISE] Setting the redirect status on the Devise responder has no effect with this " \
30
+ "version of `responders`, please make sure you're using a newer version. Check the changelog for more info."
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Controllers
3
5
  module ScopedViews
@@ -1,31 +1,34 @@
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.
8
- def signed_in?(scope=nil)
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)`
13
+ def signed_in?(scope = nil)
9
14
  [scope || Devise.mappings.keys].flatten.any? do |_scope|
10
15
  warden.authenticate?(scope: _scope)
11
16
  end
12
17
  end
13
18
 
14
19
  # Sign in a user that already was authenticated. This helper is useful for logging
15
- # users in after sign up.
16
- #
17
- # All options given to sign_in is passed forward to the set_user method in warden.
18
- # The only exception is the :bypass option, which bypass warden callbacks and stores
19
- # the user straight in session. This option is useful in cases the user is already
20
- # signed in, but we want to refresh the credentials in session.
20
+ # users in after sign up. All options given to sign_in is passed forward
21
+ # to the set_user method in warden.
22
+ # If you are using a custom warden strategy and the timeoutable module, you have to
23
+ # set `env["devise.skip_timeout"] = true` in the request to use this method, like we do
24
+ # in the sessions controller: https://github.com/heartcombo/devise/blob/main/app/controllers/devise/sessions_controller.rb#L7
21
25
  #
22
26
  # Examples:
23
27
  #
24
28
  # sign_in :user, @user # sign_in(scope, resource)
25
29
  # sign_in @user # sign_in(resource)
26
- # sign_in @user, event: :authentication # sign_in(resource, options)
27
- # sign_in @user, store: false # sign_in(resource, options)
28
- # sign_in @user, bypass: true # sign_in(resource, options)
30
+ # sign_in @user, event: :authentication # sign_in(resource, options)
31
+ # sign_in @user, store: false # sign_in(resource, options)
29
32
  #
30
33
  def sign_in(resource_or_scope, *args)
31
34
  options = args.extract_options!
@@ -34,9 +37,7 @@ module Devise
34
37
 
35
38
  expire_data_after_sign_in!
36
39
 
37
- if options[:bypass]
38
- warden.session_serializer.store(resource, scope)
39
- elsif warden.user(scope) == resource && !options.delete(:force)
40
+ if warden.user(scope) == resource && !options.delete(:force)
40
41
  # Do nothing. User already signed in and we are not forcing it.
41
42
  true
42
43
  else
@@ -44,6 +45,20 @@ module Devise
44
45
  end
45
46
  end
46
47
 
48
+ # Sign in a user bypassing the warden callbacks and stores the user
49
+ # straight in session. This option is useful in cases the user is already
50
+ # signed in, but we want to refresh the credentials in session.
51
+ #
52
+ # Examples:
53
+ #
54
+ # bypass_sign_in @user, scope: :user
55
+ # bypass_sign_in @user
56
+ def bypass_sign_in(resource, scope: nil)
57
+ scope ||= Devise::Mapping.find_scope!(resource)
58
+ expire_data_after_sign_in!
59
+ warden.session_serializer.store(resource, scope)
60
+ end
61
+
47
62
  # Sign out a given user or scope. This helper is useful for signing out a user
48
63
  # after deleting accounts. Returns true if there was a logout and false if there
49
64
  # is no user logged in on the referred scope
@@ -53,12 +68,11 @@ module Devise
53
68
  # sign_out :user # sign_out(scope)
54
69
  # sign_out @user # sign_out(resource)
55
70
  #
56
- def sign_out(resource_or_scope=nil)
71
+ def sign_out(resource_or_scope = nil)
57
72
  return sign_out_all_scopes unless resource_or_scope
58
73
  scope = Devise::Mapping.find_scope!(resource_or_scope)
59
74
  user = warden.user(scope: scope, run_callbacks: false) # If there is no user
60
75
 
61
- warden.raw_session.inspect # Without this inspect here. The session does not clear.
62
76
  warden.logout(scope)
63
77
  warden.clear_strategies_cache!(scope: scope)
64
78
  instance_variable_set(:"@current_#{scope}", nil)
@@ -69,7 +83,7 @@ module Devise
69
83
  # Sign out all active users or scopes. This helper is useful for signing out all roles
70
84
  # in one click. This signs out ALL scopes in warden. Returns true if there was at least one logout
71
85
  # and false if there was no user logged in on all scopes.
72
- def sign_out_all_scopes(lock=true)
86
+ def sign_out_all_scopes(lock = true)
73
87
  users = Devise.mappings.keys.map { |s| warden.user(scope: s, run_callbacks: false) }
74
88
 
75
89
  warden.logout
@@ -83,10 +97,6 @@ module Devise
83
97
  private
84
98
 
85
99
  def expire_data_after_sign_in!
86
- # session.keys will return an empty array if the session is not yet loaded.
87
- # This is a bug in both Rack and Rails.
88
- # A call to #empty? forces the session to be loaded.
89
- session.empty?
90
100
  session.keys.grep(/^devise\./).each { |k| session.delete(k) }
91
101
  end
92
102
 
@@ -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 && uri.path
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
@@ -32,7 +34,7 @@ module Devise
32
34
  end
33
35
  end
34
36
 
35
- def self.generate_helpers!(routes=nil)
37
+ def self.generate_helpers!(routes = nil)
36
38
  routes ||= begin
37
39
  mappings = Devise.mappings.values.map(&:used_helpers).flatten.uniq
38
40
  Devise::URL_HELPERS.slice(*mappings)
@@ -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
@@ -16,6 +18,11 @@ module Devise
16
18
 
17
19
  delegate :flash, to: :request
18
20
 
21
+ include AbstractController::Callbacks
22
+ around_action do |failure_app, action|
23
+ I18n.with_locale(failure_app.i18n_locale, &action)
24
+ end
25
+
19
26
  def self.call(env)
20
27
  @respond ||= action(:respond)
21
28
  @respond.call(env)
@@ -50,13 +57,11 @@ module Devise
50
57
  end
51
58
 
52
59
  def recall
53
- config = Rails.application.config
54
-
55
- header_info = if config.try(:relative_url_root)
56
- base_path = Pathname.new(config.relative_url_root)
60
+ header_info = if relative_url_root?
61
+ base_path = Pathname.new(relative_url_root)
57
62
  full_path = Pathname.new(attempted_path)
58
63
 
59
- { "SCRIPT_NAME" => config.relative_url_root,
64
+ { "SCRIPT_NAME" => relative_url_root,
60
65
  "PATH_INFO" => '/' + full_path.relative_path_from(base_path).to_s }
61
66
  else
62
67
  { "PATH_INFO" => attempted_path }
@@ -66,13 +71,16 @@ module Devise
66
71
  if request.respond_to?(:set_header)
67
72
  request.set_header(var, value)
68
73
  else
69
- env[var] = value
74
+ request.env[var] = value
70
75
  end
71
76
  end
72
77
 
73
78
  flash.now[:alert] = i18n_message(:invalid) if is_flashing_format?
74
- # self.response = recall_app(warden_options[:recall]).call(env)
75
- self.response = recall_app(warden_options[:recall]).call(request.env)
79
+ self.response = recall_app(warden_options[:recall]).call(request.env).tap { |response|
80
+ status = response[0].in?(300..399) ? Devise.responder.redirect_status : Devise.responder.error_status
81
+ # Avoid warnings translating status to code using Rails if available (e.g. `unprocessable_entity` => `unprocessable_content`)
82
+ response[0] = ActionDispatch::Response.try(:rack_status_code, status) || Rack::Utils.status_code(status)
83
+ }
76
84
  end
77
85
 
78
86
  def redirect
@@ -103,16 +111,27 @@ module Devise
103
111
  options[:scope] = "devise.failure"
104
112
  options[:default] = [message]
105
113
  auth_keys = scope_class.authentication_keys
106
- keys = (auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys).map { |key| scope_class.human_attribute_name(key) }
107
- options[:authentication_keys] = keys.join(I18n.translate(:"support.array.words_connector"))
114
+ human_keys = (auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys).map { |key|
115
+ # TODO: Remove the fallback and just use `downcase_first` once we drop support for Rails 7.0.
116
+ human_key = scope_class.human_attribute_name(key)
117
+ human_key.respond_to?(:downcase_first) ? human_key.downcase_first : human_key[0].downcase + human_key[1..]
118
+ }
119
+ options[:authentication_keys] = human_keys.join(I18n.t(:"support.array.words_connector"))
108
120
  options = i18n_options(options)
109
121
 
110
- I18n.t(:"#{scope}.#{message}", options)
122
+ I18n.t(:"#{scope}.#{message}", **options).then { |msg|
123
+ # Ensure that auth keys at the start of the translated string are properly cased.
124
+ msg.start_with?(human_keys.first) ? msg.upcase_first : msg
125
+ }
111
126
  else
112
127
  message.to_s
113
128
  end
114
129
  end
115
130
 
131
+ def i18n_locale
132
+ warden_options[:locale]
133
+ end
134
+
116
135
  def redirect_url
117
136
  if warden_message == :timeout
118
137
  flash[:timedout] = true if is_flashing_format?
@@ -120,7 +139,7 @@ module Devise
120
139
  path = if request.get?
121
140
  attempted_path
122
141
  else
123
- request.referrer
142
+ extract_path_from_location(request.referrer)
124
143
  end
125
144
 
126
145
  path || scope_url
@@ -135,22 +154,22 @@ module Devise
135
154
 
136
155
  def scope_url
137
156
  opts = {}
138
- route = route(scope)
139
- opts[:format] = request_format unless skip_format?
140
157
 
141
- config = Rails.application.config
158
+ # Initialize script_name with nil to prevent infinite loops in
159
+ # authenticated mounted engines
160
+ opts[:script_name] = nil
142
161
 
143
- if config.respond_to?(:relative_url_root)
144
- # Rails 4.2 goes into an infinite loop if opts[:script_name] is unset
145
- rails_4_2 = (Rails::VERSION::MAJOR >= 4) && (Rails::VERSION::MINOR >= 2)
146
- if config.relative_url_root.present? || rails_4_2
147
- opts[:script_name] = config.relative_url_root
148
- end
149
- end
162
+ route = route(scope)
163
+
164
+ opts[:format] = request_format unless skip_format?
150
165
 
151
166
  router_name = Devise.mappings[scope].router_name || Devise.available_router_name
152
167
  context = send(router_name)
153
168
 
169
+ if relative_url_root?
170
+ opts[:script_name] = relative_url_root
171
+ end
172
+
154
173
  if context.respond_to?(route)
155
174
  context.send(route, opts)
156
175
  elsif respond_to?(:root_url)
@@ -161,15 +180,15 @@ module Devise
161
180
  end
162
181
 
163
182
  def skip_format?
164
- %w(html */*).include? request_format.to_s
183
+ %w(html */* turbo_stream).include? request_format.to_s
165
184
  end
166
185
 
167
- # Choose whether we should respond in a http authentication fashion,
186
+ # Choose whether we should respond in an HTTP authentication fashion,
168
187
  # including 401 and optional headers.
169
188
  #
170
- # This method allows the user to explicitly disable http authentication
171
- # on ajax requests in case they want to redirect on failures instead of
172
- # handling the errors on their own. This is useful in case your ajax API
189
+ # This method allows the user to explicitly disable HTTP authentication
190
+ # on AJAX requests in case they want to redirect on failures instead of
191
+ # handling the errors on their own. This is useful in case your AJAX API
173
192
  # is the same as your public API and uses a format like JSON (so you
174
193
  # cannot mark JSON as a navigational format).
175
194
  def http_auth?
@@ -180,7 +199,7 @@ module Devise
180
199
  end
181
200
  end
182
201
 
183
- # It does not make sense to send authenticate headers in ajax requests
202
+ # It doesn't make sense to send authenticate headers in AJAX requests
184
203
  # or if the user disabled them.
185
204
  def http_auth_header?
186
205
  scope_class.http_authenticatable && !request.xhr?
@@ -206,11 +225,11 @@ module Devise
206
225
  end
207
226
 
208
227
  def warden
209
- request.respond_to?(:get_header) ? request.get_header("warden") : env["warden"]
228
+ request.respond_to?(:get_header) ? request.get_header("warden") : request.env["warden"]
210
229
  end
211
230
 
212
231
  def warden_options
213
- request.respond_to?(:get_header) ? request.get_header("warden.options") : env["warden.options"]
232
+ request.respond_to?(:get_header) ? request.get_header("warden.options") : request.env["warden.options"]
214
233
  end
215
234
 
216
235
  def warden_message
@@ -229,10 +248,10 @@ module Devise
229
248
  warden_options[:attempted_path]
230
249
  end
231
250
 
232
- # Stores requested uri to redirect the user after signing in. We cannot use
233
- # scoped session provided by warden here, since the user is not authenticated
234
- # yet, but we still need to store the uri based on scope, so different scopes
235
- # would never use the same uri to redirect.
251
+ # Stores requested URI to redirect the user after signing in. We can't use
252
+ # the scoped session provided by warden here, since the user is not
253
+ # authenticated yet, but we still need to store the URI based on scope, so
254
+ # different scopes would never use the same URI to redirect.
236
255
  def store_location!
237
256
  store_location_for(scope, attempted_path) if request.get? && !http_auth?
238
257
  end
@@ -244,11 +263,25 @@ module Devise
244
263
  # Check if flash messages should be emitted. Default is to do it on
245
264
  # navigational formats
246
265
  def is_flashing_format?
247
- is_navigational_format?
266
+ request.respond_to?(:flash) && is_navigational_format?
248
267
  end
249
268
 
250
269
  def request_format
251
270
  @request_format ||= request.format.try(:ref)
252
271
  end
272
+
273
+ def relative_url_root
274
+ @relative_url_root ||= begin
275
+ config = Rails.application.config
276
+
277
+ config.try(:relative_url_root) || config.action_controller.try(:relative_url_root)
278
+ end
279
+ end
280
+
281
+ def relative_url_root?
282
+ relative_url_root.present?
283
+ end
284
+
285
+ ActiveSupport.run_load_hooks(:devise_failure_app, self)
253
286
  end
254
287
  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.
@@ -5,6 +7,6 @@ Warden::Manager.after_set_user do |record, warden, options|
5
7
  if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
6
8
  scope = options[:scope]
7
9
  warden.logout(scope)
8
- throw :warden, scope: scope, message: record.inactive_message
10
+ throw :warden, scope: scope, message: record.inactive_message, locale: options.fetch(:locale, I18n.locale)
9
11
  end
10
12
  end
@@ -1,7 +1,14 @@
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?
4
6
  if Devise.clean_up_csrf_token_on_authentication && clean_up_for_winning_strategy
5
- warden.request.session.try(:delete, :_csrf_token)
7
+ if warden.request.respond_to?(:reset_csrf_token)
8
+ # Rails 7.1+
9
+ warden.request.reset_csrf_token
10
+ else
11
+ warden.request.session.try(:delete, :_csrf_token)
12
+ end
6
13
  end
7
14
  end
@@ -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,9 @@
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
- if record.respond_to?(:failed_attempts) && warden.authenticated?(options[:scope])
5
- record.update_attribute(:failed_attempts, 0) unless record.failed_attempts.to_i.zero?
6
+ if record.respond_to?(:reset_failed_attempts!) && warden.authenticated?(options[:scope])
7
+ record.reset_failed_attempts!
6
8
  end
7
9
  end