devise 3.2.4 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +259 -994
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +336 -99
  5. data/app/controllers/devise/confirmations_controller.rb +9 -3
  6. data/app/controllers/devise/omniauth_callbacks_controller.rb +12 -6
  7. data/app/controllers/devise/passwords_controller.rb +19 -6
  8. data/app/controllers/devise/registrations_controller.rb +55 -22
  9. data/app/controllers/devise/sessions_controller.rb +44 -14
  10. data/app/controllers/devise/unlocks_controller.rb +7 -2
  11. data/app/controllers/devise_controller.rb +65 -29
  12. data/app/helpers/devise_helper.rb +12 -19
  13. data/app/mailers/devise/mailer.rb +10 -0
  14. data/app/views/devise/confirmations/new.html.erb +8 -4
  15. data/app/views/devise/mailer/email_changed.html.erb +7 -0
  16. data/app/views/devise/mailer/password_change.html.erb +3 -0
  17. data/app/views/devise/passwords/edit.html.erb +15 -6
  18. data/app/views/devise/passwords/new.html.erb +8 -4
  19. data/app/views/devise/registrations/edit.html.erb +27 -13
  20. data/app/views/devise/registrations/new.html.erb +19 -8
  21. data/app/views/devise/sessions/new.html.erb +18 -9
  22. data/app/views/devise/shared/_error_messages.html.erb +15 -0
  23. data/app/views/devise/shared/{_links.erb → _links.html.erb} +9 -9
  24. data/app/views/devise/unlocks/new.html.erb +8 -4
  25. data/config/locales/en.yml +22 -16
  26. data/lib/devise/controllers/helpers.rb +109 -29
  27. data/lib/devise/controllers/rememberable.rb +12 -3
  28. data/lib/devise/controllers/scoped_views.rb +2 -0
  29. data/lib/devise/controllers/sign_in_out.rb +36 -20
  30. data/lib/devise/controllers/store_location.rb +31 -5
  31. data/lib/devise/controllers/url_helpers.rb +9 -7
  32. data/lib/devise/delegator.rb +2 -0
  33. data/lib/devise/encryptor.rb +24 -0
  34. data/lib/devise/failure_app.rb +116 -36
  35. data/lib/devise/hooks/activatable.rb +5 -4
  36. data/lib/devise/hooks/csrf_cleaner.rb +5 -1
  37. data/lib/devise/hooks/forgetable.rb +2 -0
  38. data/lib/devise/hooks/lockable.rb +6 -1
  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 +15 -8
  42. data/lib/devise/hooks/trackable.rb +2 -0
  43. data/lib/devise/mailers/helpers.rb +7 -4
  44. data/lib/devise/mapping.rb +8 -2
  45. data/lib/devise/models/authenticatable.rb +76 -51
  46. data/lib/devise/models/confirmable.rb +129 -34
  47. data/lib/devise/models/database_authenticatable.rb +107 -30
  48. data/lib/devise/models/lockable.rb +19 -9
  49. data/lib/devise/models/omniauthable.rb +2 -0
  50. data/lib/devise/models/recoverable.rb +62 -26
  51. data/lib/devise/models/registerable.rb +4 -0
  52. data/lib/devise/models/rememberable.rb +58 -29
  53. data/lib/devise/models/timeoutable.rb +2 -6
  54. data/lib/devise/models/trackable.rb +20 -4
  55. data/lib/devise/models/validatable.rb +12 -5
  56. data/lib/devise/models.rb +3 -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 +14 -5
  60. data/lib/devise/omniauth.rb +2 -0
  61. data/lib/devise/orm/active_record.rb +5 -1
  62. data/lib/devise/orm/mongoid.rb +6 -2
  63. data/lib/devise/parameter_filter.rb +4 -0
  64. data/lib/devise/parameter_sanitizer.rb +139 -65
  65. data/lib/devise/rails/routes.rb +80 -61
  66. data/lib/devise/rails/warden_compat.rb +3 -10
  67. data/lib/devise/rails.rb +8 -17
  68. data/lib/devise/secret_key_finder.rb +27 -0
  69. data/lib/devise/strategies/authenticatable.rb +18 -7
  70. data/lib/devise/strategies/base.rb +2 -0
  71. data/lib/devise/strategies/database_authenticatable.rb +13 -5
  72. data/lib/devise/strategies/rememberable.rb +15 -3
  73. data/lib/devise/test/controller_helpers.rb +165 -0
  74. data/lib/devise/test/integration_helpers.rb +63 -0
  75. data/lib/devise/test_helpers.rb +7 -124
  76. data/lib/devise/time_inflector.rb +2 -0
  77. data/lib/devise/token_generator.rb +3 -41
  78. data/lib/devise/version.rb +3 -1
  79. data/lib/devise.rb +106 -79
  80. data/lib/generators/active_record/devise_generator.rb +44 -7
  81. data/lib/generators/active_record/templates/migration.rb +5 -3
  82. data/lib/generators/active_record/templates/migration_existing.rb +5 -3
  83. data/lib/generators/devise/controllers_generator.rb +46 -0
  84. data/lib/generators/devise/devise_generator.rb +4 -2
  85. data/lib/generators/devise/install_generator.rb +17 -0
  86. data/lib/generators/devise/orm_helpers.rb +10 -21
  87. data/lib/generators/devise/views_generator.rb +21 -11
  88. data/lib/generators/mongoid/devise_generator.rb +7 -5
  89. data/lib/generators/templates/README +2 -9
  90. data/lib/generators/templates/controllers/README +14 -0
  91. data/lib/generators/templates/controllers/confirmations_controller.rb +30 -0
  92. data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +30 -0
  93. data/lib/generators/templates/controllers/passwords_controller.rb +34 -0
  94. data/lib/generators/templates/controllers/registrations_controller.rb +62 -0
  95. data/lib/generators/templates/controllers/sessions_controller.rb +27 -0
  96. data/lib/generators/templates/controllers/unlocks_controller.rb +30 -0
  97. data/lib/generators/templates/devise.rb +69 -30
  98. data/lib/generators/templates/markerb/confirmation_instructions.markerb +1 -1
  99. data/lib/generators/templates/markerb/email_changed.markerb +7 -0
  100. data/lib/generators/templates/markerb/password_change.markerb +3 -0
  101. data/lib/generators/templates/markerb/reset_password_instructions.markerb +1 -1
  102. data/lib/generators/templates/markerb/unlock_instructions.markerb +1 -1
  103. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +5 -1
  104. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +10 -2
  105. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +4 -1
  106. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +11 -3
  107. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +11 -3
  108. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +9 -4
  109. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +4 -1
  110. metadata +31 -259
  111. data/.gitignore +0 -11
  112. data/.travis.yml +0 -28
  113. data/.yardopts +0 -9
  114. data/CONTRIBUTING.md +0 -14
  115. data/Gemfile +0 -29
  116. data/Gemfile.lock +0 -160
  117. data/Rakefile +0 -35
  118. data/devise.gemspec +0 -27
  119. data/devise.png +0 -0
  120. data/gemfiles/Gemfile.rails-3.2-stable +0 -29
  121. data/gemfiles/Gemfile.rails-4.0-stable +0 -29
  122. data/gemfiles/Gemfile.rails-head +0 -29
  123. data/test/controllers/custom_strategy_test.rb +0 -62
  124. data/test/controllers/helpers_test.rb +0 -276
  125. data/test/controllers/internal_helpers_test.rb +0 -123
  126. data/test/controllers/passwords_controller_test.rb +0 -31
  127. data/test/controllers/sessions_controller_test.rb +0 -103
  128. data/test/controllers/url_helpers_test.rb +0 -59
  129. data/test/delegator_test.rb +0 -19
  130. data/test/devise_test.rb +0 -94
  131. data/test/failure_app_test.rb +0 -232
  132. data/test/generators/active_record_generator_test.rb +0 -103
  133. data/test/generators/devise_generator_test.rb +0 -39
  134. data/test/generators/install_generator_test.rb +0 -13
  135. data/test/generators/mongoid_generator_test.rb +0 -23
  136. data/test/generators/views_generator_test.rb +0 -96
  137. data/test/helpers/devise_helper_test.rb +0 -51
  138. data/test/integration/authenticatable_test.rb +0 -713
  139. data/test/integration/confirmable_test.rb +0 -284
  140. data/test/integration/database_authenticatable_test.rb +0 -84
  141. data/test/integration/http_authenticatable_test.rb +0 -105
  142. data/test/integration/lockable_test.rb +0 -239
  143. data/test/integration/omniauthable_test.rb +0 -133
  144. data/test/integration/recoverable_test.rb +0 -334
  145. data/test/integration/registerable_test.rb +0 -349
  146. data/test/integration/rememberable_test.rb +0 -167
  147. data/test/integration/timeoutable_test.rb +0 -183
  148. data/test/integration/trackable_test.rb +0 -92
  149. data/test/mailers/confirmation_instructions_test.rb +0 -115
  150. data/test/mailers/reset_password_instructions_test.rb +0 -96
  151. data/test/mailers/unlock_instructions_test.rb +0 -91
  152. data/test/mapping_test.rb +0 -127
  153. data/test/models/authenticatable_test.rb +0 -13
  154. data/test/models/confirmable_test.rb +0 -454
  155. data/test/models/database_authenticatable_test.rb +0 -249
  156. data/test/models/lockable_test.rb +0 -316
  157. data/test/models/omniauthable_test.rb +0 -7
  158. data/test/models/recoverable_test.rb +0 -184
  159. data/test/models/registerable_test.rb +0 -7
  160. data/test/models/rememberable_test.rb +0 -183
  161. data/test/models/serializable_test.rb +0 -49
  162. data/test/models/timeoutable_test.rb +0 -51
  163. data/test/models/trackable_test.rb +0 -13
  164. data/test/models/validatable_test.rb +0 -127
  165. data/test/models_test.rb +0 -144
  166. data/test/omniauth/config_test.rb +0 -57
  167. data/test/omniauth/url_helpers_test.rb +0 -54
  168. data/test/orm/active_record.rb +0 -10
  169. data/test/orm/mongoid.rb +0 -13
  170. data/test/parameter_sanitizer_test.rb +0 -81
  171. data/test/rails_app/Rakefile +0 -6
  172. data/test/rails_app/app/active_record/admin.rb +0 -6
  173. data/test/rails_app/app/active_record/shim.rb +0 -2
  174. data/test/rails_app/app/active_record/user.rb +0 -6
  175. data/test/rails_app/app/controllers/admins/sessions_controller.rb +0 -6
  176. data/test/rails_app/app/controllers/admins_controller.rb +0 -11
  177. data/test/rails_app/app/controllers/application_controller.rb +0 -9
  178. data/test/rails_app/app/controllers/home_controller.rb +0 -25
  179. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +0 -2
  180. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +0 -2
  181. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +0 -14
  182. data/test/rails_app/app/controllers/users_controller.rb +0 -31
  183. data/test/rails_app/app/helpers/application_helper.rb +0 -3
  184. data/test/rails_app/app/mailers/users/mailer.rb +0 -12
  185. data/test/rails_app/app/mongoid/admin.rb +0 -29
  186. data/test/rails_app/app/mongoid/shim.rb +0 -23
  187. data/test/rails_app/app/mongoid/user.rb +0 -39
  188. data/test/rails_app/app/views/admins/index.html.erb +0 -1
  189. data/test/rails_app/app/views/admins/sessions/new.html.erb +0 -2
  190. data/test/rails_app/app/views/home/admin_dashboard.html.erb +0 -1
  191. data/test/rails_app/app/views/home/index.html.erb +0 -1
  192. data/test/rails_app/app/views/home/join.html.erb +0 -1
  193. data/test/rails_app/app/views/home/private.html.erb +0 -1
  194. data/test/rails_app/app/views/home/user_dashboard.html.erb +0 -1
  195. data/test/rails_app/app/views/layouts/application.html.erb +0 -24
  196. data/test/rails_app/app/views/users/edit_form.html.erb +0 -1
  197. data/test/rails_app/app/views/users/index.html.erb +0 -1
  198. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +0 -1
  199. data/test/rails_app/app/views/users/sessions/new.html.erb +0 -1
  200. data/test/rails_app/bin/bundle +0 -3
  201. data/test/rails_app/bin/rails +0 -4
  202. data/test/rails_app/bin/rake +0 -4
  203. data/test/rails_app/config/application.rb +0 -40
  204. data/test/rails_app/config/boot.rb +0 -14
  205. data/test/rails_app/config/database.yml +0 -18
  206. data/test/rails_app/config/environment.rb +0 -5
  207. data/test/rails_app/config/environments/development.rb +0 -30
  208. data/test/rails_app/config/environments/production.rb +0 -80
  209. data/test/rails_app/config/environments/test.rb +0 -36
  210. data/test/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  211. data/test/rails_app/config/initializers/devise.rb +0 -181
  212. data/test/rails_app/config/initializers/inflections.rb +0 -2
  213. data/test/rails_app/config/initializers/secret_token.rb +0 -8
  214. data/test/rails_app/config/initializers/session_store.rb +0 -1
  215. data/test/rails_app/config/routes.rb +0 -105
  216. data/test/rails_app/config.ru +0 -4
  217. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +0 -71
  218. data/test/rails_app/db/schema.rb +0 -55
  219. data/test/rails_app/lib/shared_admin.rb +0 -17
  220. data/test/rails_app/lib/shared_user.rb +0 -29
  221. data/test/rails_app/public/404.html +0 -26
  222. data/test/rails_app/public/422.html +0 -26
  223. data/test/rails_app/public/500.html +0 -26
  224. data/test/rails_app/public/favicon.ico +0 -0
  225. data/test/routes_test.rb +0 -262
  226. data/test/support/action_controller/record_identifier.rb +0 -10
  227. data/test/support/assertions.rb +0 -40
  228. data/test/support/helpers.rb +0 -70
  229. data/test/support/integration.rb +0 -92
  230. data/test/support/locale/en.yml +0 -8
  231. data/test/support/mongoid.yml +0 -6
  232. data/test/support/webrat/integrations/rails.rb +0 -24
  233. data/test/test_helper.rb +0 -27
  234. data/test/test_helpers_test.rb +0 -173
  235. data/test/test_models.rb +0 -33
@@ -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.
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
- [ scope || Devise.mappings.keys ].flatten.any? do |_scope|
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/plataformatec/devise/blob/master/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!
@@ -35,6 +38,13 @@ module Devise
35
38
  expire_data_after_sign_in!
36
39
 
37
40
  if options[:bypass]
41
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc, caller)
42
+ [Devise] bypass option is deprecated and it will be removed in future version of Devise.
43
+ Please use bypass_sign_in method instead.
44
+ Example:
45
+
46
+ bypass_sign_in(user)
47
+ DEPRECATION
38
48
  warden.session_serializer.store(resource, scope)
39
49
  elsif warden.user(scope) == resource && !options.delete(:force)
40
50
  # Do nothing. User already signed in and we are not forcing it.
@@ -44,6 +54,20 @@ module Devise
44
54
  end
45
55
  end
46
56
 
57
+ # Sign in a user bypassing the warden callbacks and stores the user
58
+ # straight in session. This option is useful in cases the user is already
59
+ # signed in, but we want to refresh the credentials in session.
60
+ #
61
+ # Examples:
62
+ #
63
+ # bypass_sign_in @user, scope: :user
64
+ # bypass_sign_in @user
65
+ def bypass_sign_in(resource, scope: nil)
66
+ scope ||= Devise::Mapping.find_scope!(resource)
67
+ expire_data_after_sign_in!
68
+ warden.session_serializer.store(resource, scope)
69
+ end
70
+
47
71
  # Sign out a given user or scope. This helper is useful for signing out a user
48
72
  # after deleting accounts. Returns true if there was a logout and false if there
49
73
  # is no user logged in on the referred scope
@@ -58,7 +82,6 @@ module Devise
58
82
  scope = Devise::Mapping.find_scope!(resource_or_scope)
59
83
  user = warden.user(scope: scope, run_callbacks: false) # If there is no user
60
84
 
61
- warden.raw_session.inspect # Without this inspect here. The session does not clear.
62
85
  warden.logout(scope)
63
86
  warden.clear_strategies_cache!(scope: scope)
64
87
  instance_variable_set(:"@current_#{scope}", nil)
@@ -72,7 +95,6 @@ module Devise
72
95
  def sign_out_all_scopes(lock=true)
73
96
  users = Devise.mappings.keys.map { |s| warden.user(scope: s, run_callbacks: false) }
74
97
 
75
- warden.raw_session.inspect
76
98
  warden.logout
77
99
  expire_data_after_sign_out!
78
100
  warden.clear_strategies_cache!
@@ -91,13 +113,7 @@ module Devise
91
113
  session.keys.grep(/^devise\./).each { |k| session.delete(k) }
92
114
  end
93
115
 
94
- def expire_data_after_sign_out!
95
- # session.keys will return an empty array if the session is not yet loaded.
96
- # This is a bug in both Rack and Rails.
97
- # A call to #empty? forces the session to be loaded.
98
- session.empty?
99
- session.keys.grep(/^devise\./).each { |k| session.delete(k) }
100
- end
116
+ alias :expire_data_after_sign_out! :expire_data_after_sign_in!
101
117
  end
102
118
  end
103
119
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "uri"
2
4
 
3
5
  module Devise
@@ -29,22 +31,46 @@ 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
- if location
37
- uri = URI.parse(location)
38
- session[session_key] = [uri.path.sub(/\A\/+/, '/'), uri.query].compact.join('?')
39
- end
38
+
39
+ path = extract_path_from_location(location)
40
+ session[session_key] = path if path
40
41
  end
41
42
 
42
43
  private
43
44
 
45
+ def parse_uri(location)
46
+ location && URI.parse(location)
47
+ rescue URI::InvalidURIError
48
+ nil
49
+ end
50
+
44
51
  def stored_location_key_for(resource_or_scope)
45
52
  scope = Devise::Mapping.find_scope!(resource_or_scope)
46
53
  "#{scope}_return_to"
47
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
48
74
  end
49
75
  end
50
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
@@ -42,14 +44,14 @@ module Devise
42
44
  [:path, :url].each do |path_or_url|
43
45
  actions.each do |action|
44
46
  action = action ? "#{action}_" : ""
45
- method = "#{action}#{module_name}_#{path_or_url}"
47
+ method = :"#{action}#{module_name}_#{path_or_url}"
46
48
 
47
- class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
48
- def #{method}(resource_or_scope, *args)
49
- scope = Devise::Mapping.find_scope!(resource_or_scope)
50
- _devise_route_context.send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
51
- end
52
- URL_HELPERS
49
+ define_method method do |resource_or_scope, *args|
50
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
51
+ router_name = Devise.mappings[scope].router_name
52
+ context = router_name ? send(router_name) : _devise_route_context
53
+ context.send("#{action}#{scope}_#{module_name}_#{path_or_url}", *args)
54
+ end
53
55
  end
54
56
  end
55
57
  end
@@ -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
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bcrypt'
4
+
5
+ module Devise
6
+ module Encryptor
7
+ def self.digest(klass, password)
8
+ if klass.pepper.present?
9
+ password = "#{password}#{klass.pepper}"
10
+ end
11
+ ::BCrypt::Password.create(password, cost: klass.stretches).to_s
12
+ end
13
+
14
+ def self.compare(klass, hashed_password, password)
15
+ return false if hashed_password.blank?
16
+ bcrypt = ::BCrypt::Password.new(hashed_password)
17
+ if klass.pepper.present?
18
+ password = "#{password}#{klass.pepper}"
19
+ end
20
+ password = ::BCrypt::Engine.hash_secret(password, bcrypt.salt)
21
+ Devise.secure_compare(password, hashed_password)
22
+ end
23
+ end
24
+ end
@@ -1,12 +1,13 @@
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
- include ActionController::RackDelegation
10
11
  include ActionController::UrlFor
11
12
  include ActionController::Redirecting
12
13
 
@@ -22,9 +23,12 @@ module Devise
22
23
  @respond.call(env)
23
24
  end
24
25
 
26
+ # Try retrieving the URL options from the parent controller (usually
27
+ # ApplicationController). Instance methods are not supported at the moment,
28
+ # so only the class-level attribute is used.
25
29
  def self.default_url_options(*args)
26
- if defined?(ApplicationController)
27
- ApplicationController.default_url_options(*args)
30
+ if defined?(Devise.parent_controller.constantize)
31
+ Devise.parent_controller.constantize.try(:default_url_options) || {}
28
32
  else
29
33
  {}
30
34
  end
@@ -48,18 +52,38 @@ module Devise
48
52
  end
49
53
 
50
54
  def recall
51
- env["PATH_INFO"] = attempted_path
52
- flash.now[:alert] = i18n_message(:invalid)
53
- self.response = recall_app(warden_options[:recall]).call(env)
55
+ header_info = if relative_url_root?
56
+ base_path = Pathname.new(relative_url_root)
57
+ full_path = Pathname.new(attempted_path)
58
+
59
+ { "SCRIPT_NAME" => relative_url_root,
60
+ "PATH_INFO" => '/' + full_path.relative_path_from(base_path).to_s }
61
+ else
62
+ { "PATH_INFO" => attempted_path }
63
+ end
64
+
65
+ header_info.each do | var, value|
66
+ if request.respond_to?(:set_header)
67
+ request.set_header(var, value)
68
+ else
69
+ request.env[var] = value
70
+ end
71
+ end
72
+
73
+ 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)
54
76
  end
55
77
 
56
78
  def redirect
57
79
  store_location!
58
- if flash[:timedout] && flash[:alert]
59
- flash.keep(:timedout)
60
- flash.keep(:alert)
61
- else
62
- flash[:alert] = i18n_message
80
+ if is_flashing_format?
81
+ if flash[:timedout] && flash[:alert]
82
+ flash.keep(:timedout)
83
+ flash.keep(:alert)
84
+ else
85
+ flash[:alert] = i18n_message
86
+ end
63
87
  end
64
88
  redirect_to redirect_url
65
89
  end
@@ -78,6 +102,9 @@ module Devise
78
102
  options[:resource_name] = scope
79
103
  options[:scope] = "devise.failure"
80
104
  options[:default] = [message]
105
+ 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"))
81
108
  options = i18n_options(options)
82
109
 
83
110
  I18n.t(:"#{scope}.#{message}", options)
@@ -88,7 +115,7 @@ module Devise
88
115
 
89
116
  def redirect_url
90
117
  if warden_message == :timeout
91
- flash[:timedout] = true
118
+ flash[:timedout] = true if is_flashing_format?
92
119
 
93
120
  path = if request.get?
94
121
  attempted_path
@@ -96,26 +123,45 @@ module Devise
96
123
  request.referrer
97
124
  end
98
125
 
99
- path || scope_path
126
+ path || scope_url
100
127
  else
101
- scope_path
128
+ scope_url
102
129
  end
103
130
  end
104
131
 
105
- def scope_path
132
+ def route(scope)
133
+ :"new_#{scope}_session_url"
134
+ end
135
+
136
+ def scope_url
106
137
  opts = {}
107
- route = :"new_#{scope}_session_path"
138
+
139
+ # Initialize script_name with nil to prevent infinite loops in
140
+ # authenticated mounted engines in rails 4.2 and 5.0
141
+ opts[:script_name] = nil
142
+
143
+ route = route(scope)
144
+
108
145
  opts[:format] = request_format unless skip_format?
109
146
 
110
- config = Rails.application.config
111
- opts[:script_name] = (config.relative_url_root if config.respond_to?(:relative_url_root))
147
+ router_name = Devise.mappings[scope].router_name || Devise.available_router_name
148
+ context = send(router_name)
112
149
 
113
- context = send(Devise.available_router_name)
150
+ if relative_url_root?
151
+ opts[:script_name] = relative_url_root
152
+
153
+ # We need to add the rootpath to `script_name` manually for applications that use a Rails
154
+ # version lower than 5.1. Otherwise, it is going to generate a wrong path for Engines
155
+ # that use Devise. Remove it when the support of Rails 5.0 is droped.
156
+ elsif root_path_defined?(context) && !rails_51_and_up?
157
+ rootpath = context.routes.url_helpers.root_path
158
+ opts[:script_name] = rootpath.chomp('/') if rootpath.length > 1
159
+ end
114
160
 
115
161
  if context.respond_to?(route)
116
162
  context.send(route, opts)
117
- elsif respond_to?(:root_path)
118
- root_path(opts)
163
+ elsif respond_to?(:root_url)
164
+ root_url(opts)
119
165
  else
120
166
  "/"
121
167
  end
@@ -125,12 +171,12 @@ module Devise
125
171
  %w(html */*).include? request_format.to_s
126
172
  end
127
173
 
128
- # Choose whether we should respond in a http authentication fashion,
174
+ # Choose whether we should respond in an HTTP authentication fashion,
129
175
  # including 401 and optional headers.
130
176
  #
131
- # This method allows the user to explicitly disable http authentication
132
- # on ajax requests in case they want to redirect on failures instead of
133
- # handling the errors on their own. This is useful in case your ajax API
177
+ # This method allows the user to explicitly disable HTTP authentication
178
+ # on AJAX requests in case they want to redirect on failures instead of
179
+ # handling the errors on their own. This is useful in case your AJAX API
134
180
  # is the same as your public API and uses a format like JSON (so you
135
181
  # cannot mark JSON as a navigational format).
136
182
  def http_auth?
@@ -141,10 +187,10 @@ module Devise
141
187
  end
142
188
  end
143
189
 
144
- # It does not make sense to send authenticate headers in ajax requests
190
+ # It doesn't make sense to send authenticate headers in AJAX requests
145
191
  # or if the user disabled them.
146
192
  def http_auth_header?
147
- Devise.mappings[scope].to.http_authenticatable && !request.xhr?
193
+ scope_class.http_authenticatable && !request.xhr?
148
194
  end
149
195
 
150
196
  def http_auth_body
@@ -167,11 +213,11 @@ module Devise
167
213
  end
168
214
 
169
215
  def warden
170
- env['warden']
216
+ request.respond_to?(:get_header) ? request.get_header("warden") : request.env["warden"]
171
217
  end
172
218
 
173
219
  def warden_options
174
- env['warden.options']
220
+ request.respond_to?(:get_header) ? request.get_header("warden.options") : request.env["warden.options"]
175
221
  end
176
222
 
177
223
  def warden_message
@@ -182,14 +228,18 @@ module Devise
182
228
  @scope ||= warden_options[:scope] || Devise.default_scope
183
229
  end
184
230
 
231
+ def scope_class
232
+ @scope_class ||= Devise.mappings[scope].to
233
+ end
234
+
185
235
  def attempted_path
186
236
  warden_options[:attempted_path]
187
237
  end
188
238
 
189
- # Stores requested uri to redirect the user after signing in. We cannot use
190
- # scoped session provided by warden here, since the user is not authenticated
191
- # yet, but we still need to store the uri based on scope, so different scopes
192
- # would never use the same uri to redirect.
239
+ # Stores requested URI to redirect the user after signing in. We can't use
240
+ # the scoped session provided by warden here, since the user is not
241
+ # authenticated yet, but we still need to store the URI based on scope, so
242
+ # different scopes would never use the same URI to redirect.
193
243
  def store_location!
194
244
  store_location_for(scope, attempted_path) if request.get? && !http_auth?
195
245
  end
@@ -198,8 +248,38 @@ module Devise
198
248
  Devise.navigational_formats.include?(request_format)
199
249
  end
200
250
 
251
+ # Check if flash messages should be emitted. Default is to do it on
252
+ # navigational formats
253
+ def is_flashing_format?
254
+ request.respond_to?(:flash) && is_navigational_format?
255
+ end
256
+
201
257
  def request_format
202
258
  @request_format ||= request.format.try(:ref)
203
259
  end
260
+
261
+ def relative_url_root
262
+ @relative_url_root ||= begin
263
+ config = Rails.application.config
264
+
265
+ config.try(:relative_url_root) || config.action_controller.try(:relative_url_root)
266
+ end
267
+ end
268
+
269
+ def relative_url_root?
270
+ relative_url_root.present?
271
+ end
272
+
273
+ ActiveSupport.run_load_hooks(:devise_failure_app, self)
274
+
275
+ private
276
+
277
+ def root_path_defined?(context)
278
+ defined?(context.routes) && context.routes.url_helpers.respond_to?(:root_path)
279
+ end
280
+
281
+ def rails_51_and_up?
282
+ Rails.gem_version >= Gem::Version.new("5.1")
283
+ end
204
284
  end
205
285
  end
@@ -1,7 +1,8 @@
1
- # Deny user access whenever their account is not active yet. All strategies that inherits from
2
- # Devise::Strategies::Authenticatable and uses the validate already check if the user is active_for_authentication?
3
- # before actively signing them in. However, we need this as hook to validate the user activity
4
- # in each request and in case the user is using other strategies beside Devise ones.
1
+ # frozen_string_literal: true
2
+
3
+ # Deny user access whenever their account is not active yet.
4
+ # We need this as hook to validate the user activity on each request
5
+ # and in case the user is using other strategies beside Devise ones.
5
6
  Warden::Manager.after_set_user do |record, warden, options|
6
7
  if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
7
8
  scope = options[:scope]
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Warden::Manager.after_authentication do |record, warden, options|
2
- if Devise.clean_up_csrf_token_on_authentication
4
+ clean_up_for_winning_strategy = !warden.winning_strategy.respond_to?(:clean_up_csrf?) ||
5
+ warden.winning_strategy.clean_up_csrf?
6
+ if Devise.clean_up_csrf_token_on_authentication && clean_up_for_winning_strategy
3
7
  warden.request.session.try(:delete, :_csrf_token)
4
8
  end
5
9
  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,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
@@ -7,7 +9,7 @@ module Devise
7
9
  include Devise::Controllers::SignInOut
8
10
 
9
11
  attr_reader :warden
10
- delegate :cookies, :env, to: :warden
12
+ delegate :cookies, :request, to: :warden
11
13
 
12
14
  def initialize(warden)
13
15
  @warden = warden
@@ -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
@@ -7,22 +9,27 @@ Warden::Manager.after_set_user do |record, warden, options|
7
9
  scope = options[:scope]
8
10
  env = warden.request.env
9
11
 
10
- if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) && options[:store] != false
12
+ if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) &&
13
+ options[:store] != false && !env['devise.skip_timeoutable']
11
14
  last_request_at = warden.session(scope)['last_request_at']
12
- proxy = Devise::Hooks::Proxy.new(warden)
13
15
 
14
- if record.timedout?(last_request_at) && !env['devise.skip_timeout']
15
- Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
16
+ if last_request_at.is_a? Integer
17
+ last_request_at = Time.at(last_request_at).utc
18
+ elsif last_request_at.is_a? String
19
+ last_request_at = Time.parse(last_request_at)
20
+ end
16
21
 
17
- if record.respond_to?(:expire_auth_token_on_timeout) && record.expire_auth_token_on_timeout
18
- record.reset_authentication_token!
19
- end
22
+ proxy = Devise::Hooks::Proxy.new(warden)
20
23
 
24
+ if record.timedout?(last_request_at) &&
25
+ !env['devise.skip_timeout'] &&
26
+ !proxy.remember_me_is_active?(record)
27
+ Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
21
28
  throw :warden, scope: scope, message: :timeout
22
29
  end
23
30
 
24
31
  unless env['devise.skip_trackable']
25
- warden.session(scope)['last_request_at'] = Time.now.utc
32
+ warden.session(scope)['last_request_at'] = Time.now.utc.to_i
26
33
  end
27
34
  end
28
35
  end
@@ -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)
@@ -64,7 +67,7 @@ module Devise
64
67
  template_path
65
68
  end
66
69
 
67
- # Setup a subject doing an I18n lookup. At first, it attempts to set a subject
70
+ # Set up a subject doing an I18n lookup. At first, it attempts to set a subject
68
71
  # based on the current mapping:
69
72
  #
70
73
  # en: