devise 3.0.0 → 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 (242) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +351 -0
  3. data/MIT-LICENSE +2 -1
  4. data/README.md +422 -130
  5. data/app/controllers/devise/confirmations_controller.rb +17 -6
  6. data/app/controllers/devise/omniauth_callbacks_controller.rb +12 -6
  7. data/app/controllers/devise/passwords_controller.rb +23 -8
  8. data/app/controllers/devise/registrations_controller.rb +70 -28
  9. data/app/controllers/devise/sessions_controller.rb +49 -17
  10. data/app/controllers/devise/unlocks_controller.rb +11 -4
  11. data/app/controllers/devise_controller.rb +74 -34
  12. data/app/helpers/devise_helper.rb +23 -18
  13. data/app/mailers/devise/mailer.rb +25 -10
  14. data/app/views/devise/confirmations/new.html.erb +9 -5
  15. data/app/views/devise/mailer/confirmation_instructions.html.erb +1 -1
  16. data/app/views/devise/mailer/email_changed.html.erb +7 -0
  17. data/app/views/devise/mailer/password_change.html.erb +3 -0
  18. data/app/views/devise/mailer/reset_password_instructions.html.erb +1 -1
  19. data/app/views/devise/mailer/unlock_instructions.html.erb +1 -1
  20. data/app/views/devise/passwords/edit.html.erb +16 -7
  21. data/app/views/devise/passwords/new.html.erb +9 -5
  22. data/app/views/devise/registrations/edit.html.erb +29 -15
  23. data/app/views/devise/registrations/new.html.erb +20 -9
  24. data/app/views/devise/sessions/new.html.erb +19 -10
  25. data/app/views/devise/shared/_error_messages.html.erb +15 -0
  26. data/app/views/devise/shared/{_links.erb → _links.html.erb} +10 -10
  27. data/app/views/devise/unlocks/new.html.erb +9 -5
  28. data/config/locales/en.yml +26 -20
  29. data/lib/devise/controllers/helpers.rb +122 -125
  30. data/lib/devise/controllers/rememberable.rb +14 -14
  31. data/lib/devise/controllers/scoped_views.rb +3 -1
  32. data/lib/devise/controllers/sign_in_out.rb +121 -0
  33. data/lib/devise/controllers/store_location.rb +76 -0
  34. data/lib/devise/controllers/url_helpers.rb +10 -8
  35. data/lib/devise/delegator.rb +2 -0
  36. data/lib/devise/encryptor.rb +24 -0
  37. data/lib/devise/failure_app.rb +132 -42
  38. data/lib/devise/hooks/activatable.rb +7 -6
  39. data/lib/devise/hooks/csrf_cleaner.rb +9 -0
  40. data/lib/devise/hooks/forgetable.rb +3 -1
  41. data/lib/devise/hooks/lockable.rb +5 -3
  42. data/lib/devise/hooks/proxy.rb +23 -0
  43. data/lib/devise/hooks/rememberable.rb +7 -4
  44. data/lib/devise/hooks/timeoutable.rb +18 -8
  45. data/lib/devise/hooks/trackable.rb +3 -1
  46. data/lib/devise/mailers/helpers.rb +15 -18
  47. data/lib/devise/mapping.rb +9 -3
  48. data/lib/devise/models/authenticatable.rb +102 -80
  49. data/lib/devise/models/confirmable.rb +154 -72
  50. data/lib/devise/models/database_authenticatable.rb +125 -25
  51. data/lib/devise/models/lockable.rb +50 -29
  52. data/lib/devise/models/omniauthable.rb +3 -1
  53. data/lib/devise/models/recoverable.rb +72 -50
  54. data/lib/devise/models/registerable.rb +4 -0
  55. data/lib/devise/models/rememberable.rb +65 -32
  56. data/lib/devise/models/timeoutable.rb +4 -8
  57. data/lib/devise/models/trackable.rb +20 -4
  58. data/lib/devise/models/validatable.rb +16 -9
  59. data/lib/devise/models.rb +6 -13
  60. data/lib/devise/modules.rb +12 -11
  61. data/lib/devise/omniauth/config.rb +2 -0
  62. data/lib/devise/omniauth/url_helpers.rb +14 -5
  63. data/lib/devise/omniauth.rb +4 -5
  64. data/lib/devise/orm/active_record.rb +5 -1
  65. data/lib/devise/orm/mongoid.rb +6 -2
  66. data/lib/devise/parameter_filter.rb +4 -0
  67. data/lib/devise/parameter_sanitizer.rb +144 -34
  68. data/lib/devise/rails/deprecated_constant_accessor.rb +39 -0
  69. data/lib/devise/rails/routes.rb +191 -127
  70. data/lib/devise/rails/warden_compat.rb +2 -1
  71. data/lib/devise/rails.rb +13 -20
  72. data/lib/devise/secret_key_finder.rb +27 -0
  73. data/lib/devise/strategies/authenticatable.rb +21 -22
  74. data/lib/devise/strategies/base.rb +3 -1
  75. data/lib/devise/strategies/database_authenticatable.rb +15 -4
  76. data/lib/devise/strategies/rememberable.rb +15 -3
  77. data/lib/devise/test/controller_helpers.rb +167 -0
  78. data/lib/devise/test/integration_helpers.rb +63 -0
  79. data/lib/devise/test_helpers.rb +7 -123
  80. data/lib/devise/time_inflector.rb +4 -2
  81. data/lib/devise/token_generator.rb +32 -0
  82. data/lib/devise/version.rb +3 -1
  83. data/lib/devise.rb +124 -78
  84. data/lib/generators/active_record/devise_generator.rb +64 -15
  85. data/lib/generators/active_record/templates/migration.rb +9 -8
  86. data/lib/generators/active_record/templates/migration_existing.rb +9 -8
  87. data/lib/generators/devise/controllers_generator.rb +46 -0
  88. data/lib/generators/devise/devise_generator.rb +10 -6
  89. data/lib/generators/devise/install_generator.rb +19 -1
  90. data/lib/generators/devise/orm_helpers.rb +17 -9
  91. data/lib/generators/devise/views_generator.rb +51 -28
  92. data/lib/generators/mongoid/devise_generator.rb +24 -24
  93. data/lib/generators/templates/README +13 -12
  94. data/lib/generators/templates/controllers/README +14 -0
  95. data/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
  96. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
  97. data/lib/generators/templates/controllers/passwords_controller.rb +34 -0
  98. data/lib/generators/templates/controllers/registrations_controller.rb +62 -0
  99. data/lib/generators/templates/controllers/sessions_controller.rb +27 -0
  100. data/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
  101. data/lib/generators/templates/devise.rb +118 -53
  102. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  103. data/lib/generators/templates/markerb/email_changed.markerb +7 -0
  104. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  105. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  106. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  107. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +6 -2
  108. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +12 -4
  109. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +5 -2
  110. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +14 -6
  111. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +12 -4
  112. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +11 -6
  113. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +5 -2
  114. metadata +73 -294
  115. data/.gitignore +0 -10
  116. data/.travis.yml +0 -20
  117. data/.yardopts +0 -9
  118. data/CHANGELOG.rdoc +0 -941
  119. data/CONTRIBUTING.md +0 -14
  120. data/Gemfile +0 -31
  121. data/Gemfile.lock +0 -159
  122. data/Rakefile +0 -35
  123. data/app/views/devise/_links.erb +0 -3
  124. data/devise.gemspec +0 -26
  125. data/devise.png +0 -0
  126. data/gemfiles/Gemfile.rails-3.2.x +0 -31
  127. data/gemfiles/Gemfile.rails-3.2.x.lock +0 -156
  128. data/lib/devise/models/token_authenticatable.rb +0 -89
  129. data/lib/devise/strategies/token_authenticatable.rb +0 -91
  130. data/test/controllers/custom_strategy_test.rb +0 -62
  131. data/test/controllers/helpers_test.rb +0 -253
  132. data/test/controllers/internal_helpers_test.rb +0 -120
  133. data/test/controllers/passwords_controller_test.rb +0 -32
  134. data/test/controllers/sessions_controller_test.rb +0 -99
  135. data/test/controllers/url_helpers_test.rb +0 -59
  136. data/test/delegator_test.rb +0 -19
  137. data/test/devise_test.rb +0 -83
  138. data/test/failure_app_test.rb +0 -221
  139. data/test/generators/active_record_generator_test.rb +0 -73
  140. data/test/generators/devise_generator_test.rb +0 -39
  141. data/test/generators/install_generator_test.rb +0 -13
  142. data/test/generators/mongoid_generator_test.rb +0 -23
  143. data/test/generators/views_generator_test.rb +0 -67
  144. data/test/helpers/devise_helper_test.rb +0 -51
  145. data/test/integration/authenticatable_test.rb +0 -699
  146. data/test/integration/confirmable_test.rb +0 -299
  147. data/test/integration/database_authenticatable_test.rb +0 -84
  148. data/test/integration/http_authenticatable_test.rb +0 -115
  149. data/test/integration/lockable_test.rb +0 -242
  150. data/test/integration/omniauthable_test.rb +0 -133
  151. data/test/integration/recoverable_test.rb +0 -335
  152. data/test/integration/registerable_test.rb +0 -349
  153. data/test/integration/rememberable_test.rb +0 -165
  154. data/test/integration/timeoutable_test.rb +0 -150
  155. data/test/integration/token_authenticatable_test.rb +0 -205
  156. data/test/integration/trackable_test.rb +0 -92
  157. data/test/mailers/confirmation_instructions_test.rb +0 -111
  158. data/test/mailers/reset_password_instructions_test.rb +0 -92
  159. data/test/mailers/unlock_instructions_test.rb +0 -87
  160. data/test/mapping_test.rb +0 -127
  161. data/test/models/authenticatable_test.rb +0 -13
  162. data/test/models/confirmable_test.rb +0 -452
  163. data/test/models/database_authenticatable_test.rb +0 -226
  164. data/test/models/lockable_test.rb +0 -282
  165. data/test/models/omniauthable_test.rb +0 -7
  166. data/test/models/recoverable_test.rb +0 -222
  167. data/test/models/registerable_test.rb +0 -7
  168. data/test/models/rememberable_test.rb +0 -175
  169. data/test/models/serializable_test.rb +0 -49
  170. data/test/models/timeoutable_test.rb +0 -46
  171. data/test/models/token_authenticatable_test.rb +0 -55
  172. data/test/models/trackable_test.rb +0 -13
  173. data/test/models/validatable_test.rb +0 -127
  174. data/test/models_test.rb +0 -163
  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 -58
  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/controllers/admins/sessions_controller.rb +0 -6
  185. data/test/rails_app/app/controllers/admins_controller.rb +0 -11
  186. data/test/rails_app/app/controllers/application_controller.rb +0 -9
  187. data/test/rails_app/app/controllers/home_controller.rb +0 -25
  188. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +0 -2
  189. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +0 -2
  190. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +0 -14
  191. data/test/rails_app/app/controllers/users_controller.rb +0 -31
  192. data/test/rails_app/app/helpers/application_helper.rb +0 -3
  193. data/test/rails_app/app/mailers/users/mailer.rb +0 -12
  194. data/test/rails_app/app/mongoid/admin.rb +0 -29
  195. data/test/rails_app/app/mongoid/shim.rb +0 -23
  196. data/test/rails_app/app/mongoid/user.rb +0 -42
  197. data/test/rails_app/app/views/admins/index.html.erb +0 -1
  198. data/test/rails_app/app/views/admins/sessions/new.html.erb +0 -2
  199. data/test/rails_app/app/views/home/admin_dashboard.html.erb +0 -1
  200. data/test/rails_app/app/views/home/index.html.erb +0 -1
  201. data/test/rails_app/app/views/home/join.html.erb +0 -1
  202. data/test/rails_app/app/views/home/private.html.erb +0 -1
  203. data/test/rails_app/app/views/home/user_dashboard.html.erb +0 -1
  204. data/test/rails_app/app/views/layouts/application.html.erb +0 -24
  205. data/test/rails_app/app/views/users/edit_form.html.erb +0 -1
  206. data/test/rails_app/app/views/users/index.html.erb +0 -1
  207. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +0 -1
  208. data/test/rails_app/app/views/users/sessions/new.html.erb +0 -1
  209. data/test/rails_app/bin/bundle +0 -3
  210. data/test/rails_app/bin/rails +0 -4
  211. data/test/rails_app/bin/rake +0 -4
  212. data/test/rails_app/config/application.rb +0 -40
  213. data/test/rails_app/config/boot.rb +0 -8
  214. data/test/rails_app/config/database.yml +0 -18
  215. data/test/rails_app/config/environment.rb +0 -5
  216. data/test/rails_app/config/environments/development.rb +0 -34
  217. data/test/rails_app/config/environments/production.rb +0 -84
  218. data/test/rails_app/config/environments/test.rb +0 -36
  219. data/test/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  220. data/test/rails_app/config/initializers/devise.rb +0 -178
  221. data/test/rails_app/config/initializers/inflections.rb +0 -2
  222. data/test/rails_app/config/initializers/secret_token.rb +0 -8
  223. data/test/rails_app/config/initializers/session_store.rb +0 -1
  224. data/test/rails_app/config/routes.rb +0 -104
  225. data/test/rails_app/config.ru +0 -4
  226. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -74
  227. data/test/rails_app/db/schema.rb +0 -52
  228. data/test/rails_app/lib/shared_admin.rb +0 -14
  229. data/test/rails_app/lib/shared_user.rb +0 -25
  230. data/test/rails_app/public/404.html +0 -26
  231. data/test/rails_app/public/422.html +0 -26
  232. data/test/rails_app/public/500.html +0 -26
  233. data/test/rails_app/public/favicon.ico +0 -0
  234. data/test/routes_test.rb +0 -250
  235. data/test/support/assertions.rb +0 -40
  236. data/test/support/helpers.rb +0 -91
  237. data/test/support/integration.rb +0 -92
  238. data/test/support/locale/en.yml +0 -4
  239. data/test/support/webrat/integrations/rails.rb +0 -24
  240. data/test/test_helper.rb +0 -34
  241. data/test/test_helpers_test.rb +0 -151
  242. data/test/test_models.rb +0 -26
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/strategies/base'
2
4
 
3
5
  module Devise
@@ -16,32 +18,26 @@ module Devise
16
18
  valid_for_params_auth? || valid_for_http_auth?
17
19
  end
18
20
 
21
+ # Override and set to false for things like OmniAuth that technically
22
+ # run through Authentication (user_set) very often, which would normally
23
+ # reset CSRF data in the session
24
+ def clean_up_csrf?
25
+ true
26
+ end
27
+
19
28
  private
20
29
 
21
30
  # Receives a resource and check if it is valid by calling valid_for_authentication?
22
- # An optional block that will be triggered while validating can be optionally
23
- # given as parameter. Check Devise::Models::Authenticable.valid_for_authentication?
31
+ # A block that will be triggered while validating can be optionally
32
+ # given as parameter. Check Devise::Models::Authenticatable.valid_for_authentication?
24
33
  # for more information.
25
34
  #
26
35
  # In case the resource can't be validated, it will fail with the given
27
36
  # unauthenticated_message.
28
37
  def validate(resource, &block)
29
- unless resource
30
- ActiveSupport::Deprecation.warn "an empty resource was given to #{self.class.name}#validate. " \
31
- "Please ensure the resource is not nil", caller
32
- end
33
-
34
38
  result = resource && resource.valid_for_authentication?(&block)
35
39
 
36
- case result
37
- when Symbol, String
38
- ActiveSupport::Deprecation.warn "valid_for_authentication? should return a boolean value"
39
- fail!(result)
40
- return false
41
- end
42
-
43
40
  if result
44
- decorate(resource)
45
41
  true
46
42
  else
47
43
  if resource
@@ -52,7 +48,7 @@ module Devise
52
48
  end
53
49
 
54
50
  # Get values from params and set in the resource.
55
- def decorate(resource)
51
+ def remember_me(resource)
56
52
  resource.remember_me = remember_me? if resource.respond_to?(:remember_me=)
57
53
  end
58
54
 
@@ -61,9 +57,9 @@ module Devise
61
57
  valid_params? && Devise::TRUE_VALUES.include?(params_auth_hash[:remember_me])
62
58
  end
63
59
 
64
- # Check if this is strategy is valid for http authentication by:
60
+ # Check if this is a valid strategy for http authentication by:
65
61
  #
66
- # * Validating if the model allows params authentication;
62
+ # * Validating if the model allows http authentication;
67
63
  # * If any of the authorization headers were sent;
68
64
  # * If all authentication keys are present;
69
65
  #
@@ -71,7 +67,7 @@ module Devise
71
67
  http_authenticatable? && request.authorization && with_authentication_hash(:http_auth, http_auth_hash)
72
68
  end
73
69
 
74
- # Check if this is strategy is valid for params authentication by:
70
+ # Check if this is a valid strategy for params authentication by:
75
71
  #
76
72
  # * Validating if the model allows params authentication;
77
73
  # * If the request hits the sessions controller through POST;
@@ -114,14 +110,17 @@ module Devise
114
110
  params_auth_hash.is_a?(Hash)
115
111
  end
116
112
 
117
- # Check if password is present and is not equal to "X" (default value for token).
113
+ # Note: unlike `Model.valid_password?`, this method does not actually
114
+ # ensure that the password in the params matches the password stored in
115
+ # the database. It only checks if the password is *present*. Do not rely
116
+ # on this method for validating that a given password is correct.
118
117
  def valid_password?
119
- password.present? && password != "X"
118
+ password.present?
120
119
  end
121
120
 
122
121
  # Helper to decode credentials from HTTP.
123
122
  def decode_credentials
124
- return [] unless request.authorization && request.authorization =~ /^Basic (.*)/m
123
+ return [] unless request.authorization && request.authorization =~ /^Basic (.*)/mi
125
124
  Base64.decode64($1).split(/:/, 2)
126
125
  end
127
126
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
4
  module Strategies
3
5
  # Base strategy for Devise. Responsible for verifying correct scope and mapping.
@@ -17,4 +19,4 @@ module Devise
17
19
  end
18
20
  end
19
21
  end
20
- end
22
+ end
@@ -1,17 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/strategies/authenticatable'
2
4
 
3
5
  module Devise
4
6
  module Strategies
5
- # Default strategy for signing in a user, based on his email and password in the database.
7
+ # Default strategy for signing in a user, based on their email and password in the database.
6
8
  class DatabaseAuthenticatable < Authenticatable
7
9
  def authenticate!
8
- resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
9
- return fail(:not_found_in_database) unless resource
10
+ resource = password.present? && mapping.to.find_for_database_authentication(authentication_hash)
11
+ hashed = false
10
12
 
11
- if validate(resource){ resource.valid_password?(password) }
13
+ if validate(resource){ hashed = true; resource.valid_password?(password) }
14
+ remember_me(resource)
12
15
  resource.after_database_authentication
13
16
  success!(resource)
14
17
  end
18
+
19
+ # In paranoid mode, hash the password even when a resource doesn't exist for the given authentication key.
20
+ # This is necessary to prevent enumeration attacks - e.g. the request is faster when a resource doesn't
21
+ # exist in the database if the password hashing algorithm is not called.
22
+ mapping.to.new.password = password if !hashed && Devise.paranoid
23
+ unless resource
24
+ Devise.paranoid ? fail(:invalid) : fail(:not_found_in_database)
25
+ end
15
26
  end
16
27
  end
17
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'devise/strategies/authenticatable'
2
4
 
3
5
  module Devise
@@ -25,15 +27,25 @@ module Devise
25
27
  end
26
28
 
27
29
  if validate(resource)
30
+ remember_me(resource) if extend_remember_me?(resource)
31
+ resource.after_remembered
28
32
  success!(resource)
29
33
  end
30
34
  end
31
35
 
36
+ # No need to clean up the CSRF when using rememberable.
37
+ # In fact, cleaning it up here would be a bug because
38
+ # rememberable is triggered on GET requests which means
39
+ # we would render a page on first access with all csrf
40
+ # tokens expired.
41
+ def clean_up_csrf?
42
+ false
43
+ end
44
+
32
45
  private
33
46
 
34
- def decorate(resource)
35
- super
36
- resource.extend_remember_period = mapping.to.extend_remember_period if resource.respond_to?(:extend_remember_period=)
47
+ def extend_remember_me?(resource)
48
+ resource.respond_to?(:extend_remember_period) && resource.extend_remember_period
37
49
  end
38
50
 
39
51
  def remember_me?
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module Test
5
+ # `Devise::Test::ControllerHelpers` provides a facility to test controllers
6
+ # in isolation when using `ActionController::TestCase` allowing you to
7
+ # quickly sign_in or sign_out a user. Do not use
8
+ # `Devise::Test::ControllerHelpers` in integration tests.
9
+ #
10
+ # Examples
11
+ #
12
+ # class PostsTest < ActionController::TestCase
13
+ # include Devise::Test::ControllerHelpers
14
+ #
15
+ # test 'authenticated users can GET index' do
16
+ # sign_in users(:bob)
17
+ #
18
+ # get :index
19
+ # assert_response :success
20
+ # end
21
+ # end
22
+ #
23
+ # Important: you should not test Warden specific behavior (like callbacks)
24
+ # using `Devise::Test::ControllerHelpers` since it is a stub of the actual
25
+ # behavior. Such callbacks should be tested in your integration suite instead.
26
+ module ControllerHelpers
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ setup :setup_controller_for_warden, :warden
31
+ end
32
+
33
+ # Override process to consider warden.
34
+ def process(*)
35
+ _catch_warden { super }
36
+
37
+ @response
38
+ end
39
+
40
+ ruby2_keywords(:process) if respond_to?(:ruby2_keywords, true)
41
+
42
+ # We need to set up the environment variables and the response in the controller.
43
+ def setup_controller_for_warden #:nodoc:
44
+ @request.env['action_controller.instance'] = @controller
45
+ end
46
+
47
+ # Quick access to Warden::Proxy.
48
+ def warden #:nodoc:
49
+ @request.env['warden'] ||= begin
50
+ manager = Warden::Manager.new(nil) do |config|
51
+ config.merge! Devise.warden_config
52
+ end
53
+ Warden::Proxy.new(@request.env, manager)
54
+ end
55
+ end
56
+
57
+ # sign_in a given resource by storing its keys in the session.
58
+ # This method bypass any warden authentication callback.
59
+ #
60
+ # * +resource+ - The resource that should be authenticated
61
+ # * +scope+ - An optional +Symbol+ with the scope where the resource
62
+ # should be signed in with.
63
+ # Examples:
64
+ #
65
+ # sign_in users(:alice)
66
+ # sign_in users(:alice), scope: :admin
67
+ def sign_in(resource, deprecated = nil, scope: nil)
68
+ if deprecated.present?
69
+ scope = resource
70
+ resource = deprecated
71
+
72
+ ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
73
+ [Devise] sign_in(:#{scope}, resource) on controller tests is deprecated and will be removed from Devise.
74
+ Please use sign_in(resource, scope: :#{scope}) instead.
75
+ DEPRECATION
76
+ end
77
+
78
+ scope ||= Devise::Mapping.find_scope!(resource)
79
+
80
+ warden.instance_variable_get(:@users).delete(scope)
81
+ warden.session_serializer.store(resource, scope)
82
+ end
83
+
84
+ # Sign out a given resource or scope by calling logout on Warden.
85
+ # This method bypass any warden logout callback.
86
+ #
87
+ # Examples:
88
+ #
89
+ # sign_out :user # sign_out(scope)
90
+ # sign_out @user # sign_out(resource)
91
+ #
92
+ def sign_out(resource_or_scope)
93
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
94
+ @controller.instance_variable_set(:"@current_#{scope}", nil)
95
+ user = warden.instance_variable_get(:@users).delete(scope)
96
+ warden.session_serializer.delete(scope, user)
97
+ end
98
+
99
+ protected
100
+
101
+ # Catch warden continuations and handle like the middleware would.
102
+ # Returns nil when interrupted, otherwise the normal result of the block.
103
+ def _catch_warden(&block)
104
+ result = catch(:warden, &block)
105
+
106
+ env = @controller.request.env
107
+
108
+ result ||= {}
109
+
110
+ # Set the response. In production, the rack result is returned
111
+ # from Warden::Manager#call, which the following is modelled on.
112
+ case result
113
+ when Array
114
+ if result.first == 401 && intercept_401?(env) # does this happen during testing?
115
+ _process_unauthenticated(env)
116
+ else
117
+ result
118
+ end
119
+ when Hash
120
+ _process_unauthenticated(env, result)
121
+ else
122
+ result
123
+ end
124
+ end
125
+
126
+ def _process_unauthenticated(env, options = {})
127
+ options[:action] ||= :unauthenticated
128
+ proxy = request.env['warden']
129
+ result = options[:result] || proxy.result
130
+
131
+ ret = case result
132
+ when :redirect
133
+ body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
134
+ [proxy.status, proxy.headers, [body]]
135
+ when :custom
136
+ proxy.custom_response
137
+ else
138
+ request.env["PATH_INFO"] = "/#{options[:action]}"
139
+ request.env["warden.options"] = options
140
+ Warden::Manager._run_callbacks(:before_failure, env, options)
141
+
142
+ status, headers, response = Devise.warden_config[:failure_app].call(env).to_a
143
+ @controller.response.headers.merge!(headers)
144
+ @controller.response.content_type = headers["Content-Type"] unless Rails::VERSION::MAJOR >= 5
145
+ @controller.status = status
146
+ @controller.response_body = response.body
147
+ nil # causes process return @response
148
+ end
149
+
150
+ # ensure that the controller response is set up. In production, this is
151
+ # not necessary since warden returns the results to rack. However, at
152
+ # testing time, we want the response to be available to the testing
153
+ # framework to verify what would be returned to rack.
154
+ if ret.is_a?(Array)
155
+ status, headers, body = *ret
156
+ # ensure the controller response is set to our response.
157
+ @controller.response ||= @response
158
+ @response.status = status
159
+ @response.headers.merge!(headers)
160
+ @response.body = body
161
+ end
162
+
163
+ ret
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ # Devise::Test::IntegrationHelpers is a helper module for facilitating
5
+ # authentication on Rails integration tests to bypass the required steps for
6
+ # signin in or signin out a record.
7
+ #
8
+ # Examples
9
+ #
10
+ # class PostsTest < ActionDispatch::IntegrationTest
11
+ # include Devise::Test::IntegrationHelpers
12
+ #
13
+ # test 'authenticated users can see posts' do
14
+ # sign_in users(:bob)
15
+ #
16
+ # get '/posts'
17
+ # assert_response :success
18
+ # end
19
+ # end
20
+ module Test
21
+ module IntegrationHelpers
22
+ def self.included(base)
23
+ base.class_eval do
24
+ include Warden::Test::Helpers
25
+
26
+ setup :setup_integration_for_devise
27
+ teardown :teardown_integration_for_devise
28
+ end
29
+ end
30
+
31
+ # Signs in a specific resource, mimicking a successful sign in
32
+ # operation through +Devise::SessionsController#create+.
33
+ #
34
+ # * +resource+ - The resource that should be authenticated
35
+ # * +scope+ - An optional +Symbol+ with the scope where the resource
36
+ # should be signed in with.
37
+ def sign_in(resource, scope: nil)
38
+ scope ||= Devise::Mapping.find_scope!(resource)
39
+
40
+ login_as(resource, scope: scope)
41
+ end
42
+
43
+ # Signs out a specific scope from the session.
44
+ #
45
+ # * +resource_or_scope+ - The resource or scope that should be signed out.
46
+ def sign_out(resource_or_scope)
47
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
48
+
49
+ logout scope
50
+ end
51
+
52
+ protected
53
+
54
+ def setup_integration_for_devise
55
+ Warden.test_mode!
56
+ end
57
+
58
+ def teardown_integration_for_devise
59
+ Warden.test_reset!
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,131 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
- # Devise::TestHelpers provides a facility to test controllers in isolation
3
- # when using ActionController::TestCase allowing you to quickly sign_in or
4
- # sign_out a user. Do not use Devise::TestHelpers in integration tests.
5
- #
6
- # Notice you should not test Warden specific behavior (like Warden callbacks)
7
- # using Devise::TestHelpers since it is a stub of the actual behavior. Such
8
- # callbacks should be tested in your integration suite instead.
9
4
  module TestHelpers
10
5
  def self.included(base)
11
6
  base.class_eval do
12
- setup :setup_controller_for_warden, :warden if respond_to?(:setup)
13
- end
14
- end
15
-
16
- # Override process to consider warden.
17
- def process(*)
18
- # Make sure we always return @response, a la ActionController::TestCase::Behaviour#process, even if warden interrupts
19
- _catch_warden { super } || @response
20
- end
21
-
22
- # We need to setup the environment variables and the response in the controller.
23
- def setup_controller_for_warden #:nodoc:
24
- @request.env['action_controller.instance'] = @controller
25
- end
26
-
27
- # Quick access to Warden::Proxy.
28
- def warden #:nodoc:
29
- @warden ||= begin
30
- manager = Warden::Manager.new(nil) do |config|
31
- config.merge! Devise.warden_config
32
- end
33
- @request.env['warden'] = Warden::Proxy.new(@request.env, manager)
7
+ ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
8
+ [Devise] including `Devise::TestHelpers` is deprecated and will be removed from Devise.
9
+ For controller tests, please include `Devise::Test::ControllerHelpers` instead.
10
+ DEPRECATION
11
+ include Devise::Test::ControllerHelpers
34
12
  end
35
13
  end
36
-
37
- # sign_in a given resource by storing its keys in the session.
38
- # This method bypass any warden authentication callback.
39
- #
40
- # Examples:
41
- #
42
- # sign_in :user, @user # sign_in(scope, resource)
43
- # sign_in @user # sign_in(resource)
44
- #
45
- def sign_in(resource_or_scope, resource=nil)
46
- scope ||= Devise::Mapping.find_scope!(resource_or_scope)
47
- resource ||= resource_or_scope
48
- warden.instance_variable_get(:@users).delete(scope)
49
- warden.session_serializer.store(resource, scope)
50
- end
51
-
52
- # Sign out a given resource or scope by calling logout on Warden.
53
- # This method bypass any warden logout callback.
54
- #
55
- # Examples:
56
- #
57
- # sign_out :user # sign_out(scope)
58
- # sign_out @user # sign_out(resource)
59
- #
60
- def sign_out(resource_or_scope)
61
- scope = Devise::Mapping.find_scope!(resource_or_scope)
62
- @controller.instance_variable_set(:"@current_#{scope}", nil)
63
- user = warden.instance_variable_get(:@users).delete(scope)
64
- warden.session_serializer.delete(scope, user)
65
- end
66
-
67
- protected
68
-
69
- # Catch warden continuations and handle like the middleware would.
70
- # Returns nil when interrupted, otherwise the normal result of the block.
71
- def _catch_warden(&block)
72
- result = catch(:warden, &block)
73
-
74
- env = @controller.request.env
75
-
76
- result ||= {}
77
-
78
- # Set the response. In production, the rack result is returned
79
- # from Warden::Manager#call, which the following is modelled on.
80
- case result
81
- when Array
82
- if result.first == 401 && intercept_401?(env) # does this happen during testing?
83
- _process_unauthenticated(env)
84
- else
85
- result
86
- end
87
- when Hash
88
- _process_unauthenticated(env, result)
89
- else
90
- result
91
- end
92
- end
93
-
94
- def _process_unauthenticated(env, options = {})
95
- options[:action] ||= :unauthenticated
96
- proxy = env['warden']
97
- result = options[:result] || proxy.result
98
-
99
- ret = case result
100
- when :redirect
101
- body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
102
- [proxy.status, proxy.headers, [body]]
103
- when :custom
104
- proxy.custom_response
105
- else
106
- env["PATH_INFO"] = "/#{options[:action]}"
107
- env["warden.options"] = options
108
- Warden::Manager._run_callbacks(:before_failure, env, options)
109
-
110
- status, headers, response = Devise.warden_config[:failure_app].call(env).to_a
111
- @controller.send :render, :status => status, :text => response.body,
112
- :content_type => headers["Content-Type"], :location => headers["Location"]
113
- nil # causes process return @response
114
- end
115
-
116
- # ensure that the controller response is set up. In production, this is
117
- # not necessary since warden returns the results to rack. However, at
118
- # testing time, we want the response to be available to the testing
119
- # framework to verify what would be returned to rack.
120
- if ret.is_a?(Array)
121
- # ensure the controller response is set to our response.
122
- @controller.response ||= @response
123
- @response.status = ret.first
124
- @response.headers = ret.second
125
- @response.body = ret.third
126
- end
127
-
128
- ret
129
- end
130
14
  end
131
15
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/delegation"
2
4
 
3
5
  module Devise
@@ -6,9 +8,9 @@ module Devise
6
8
 
7
9
  class << self
8
10
  attr_reader :instance
9
- delegate :time_ago_in_words, :to => :instance
11
+ delegate :time_ago_in_words, to: :instance
10
12
  end
11
13
 
12
14
  @instance = new
13
15
  end
14
- end
16
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module Devise
6
+ class TokenGenerator
7
+ def initialize(key_generator, digest = "SHA256")
8
+ @key_generator = key_generator
9
+ @digest = digest
10
+ end
11
+
12
+ def digest(klass, column, value)
13
+ value.present? && OpenSSL::HMAC.hexdigest(@digest, key_for(column), value.to_s)
14
+ end
15
+
16
+ def generate(klass, column)
17
+ key = key_for(column)
18
+
19
+ loop do
20
+ raw = Devise.friendly_token
21
+ enc = OpenSSL::HMAC.hexdigest(@digest, key, raw)
22
+ break [raw, enc] unless klass.to_adapter.find_first({ column => enc })
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def key_for(column)
29
+ @key_generator.generate_key("Devise #{column}")
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Devise
2
- VERSION = "3.0.0".freeze
4
+ VERSION = "4.8.0".freeze
3
5
  end