actionpack 4.2.11.1 → 6.1.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,5 +1,7 @@
1
- require 'base64'
2
- require 'active_support/security_utils'
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "active_support/security_utils"
3
5
 
4
6
  module ActionController
5
7
  # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
@@ -28,14 +30,14 @@ module ActionController
28
30
  # class ApplicationController < ActionController::Base
29
31
  # before_action :set_account, :authenticate
30
32
  #
31
- # protected
33
+ # private
32
34
  # def set_account
33
35
  # @account = Account.find_by(url_name: request.subdomains.first)
34
36
  # end
35
37
  #
36
38
  # def authenticate
37
39
  # case request.format
38
- # when Mime::XML, Mime::ATOM
40
+ # when Mime[:xml], Mime[:atom]
39
41
  # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
40
42
  # @current_user = user
41
43
  # else
@@ -54,8 +56,9 @@ module ActionController
54
56
  # In your integration tests, you can do something like this:
55
57
  #
56
58
  # def test_access_granted_from_xml
57
- # @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
58
- # get "/notes/1.xml"
59
+ # authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
60
+ #
61
+ # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
59
62
  #
60
63
  # assert_equal 200, status
61
64
  # end
@@ -66,29 +69,30 @@ module ActionController
66
69
  extend ActiveSupport::Concern
67
70
 
68
71
  module ClassMethods
69
- def http_basic_authenticate_with(options = {})
70
- before_action(options.except(:name, :password, :realm)) do
71
- authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
72
- # This comparison uses & so that it doesn't short circuit and
73
- # uses `variable_size_secure_compare` so that length information
74
- # isn't leaked.
75
- ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
76
- ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
77
- end
78
- end
72
+ def http_basic_authenticate_with(name:, password:, realm: nil, **options)
73
+ before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
74
+ end
75
+ end
76
+
77
+ def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
78
+ authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
79
+ # This comparison uses & so that it doesn't short circuit and
80
+ # uses `secure_compare` so that length information isn't leaked.
81
+ ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
82
+ ActiveSupport::SecurityUtils.secure_compare(given_password, password)
79
83
  end
80
84
  end
81
85
 
82
- def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
83
- authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
86
+ def authenticate_or_request_with_http_basic(realm = nil, message = nil, &login_procedure)
87
+ authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message)
84
88
  end
85
89
 
86
90
  def authenticate_with_http_basic(&login_procedure)
87
91
  HttpAuthentication::Basic.authenticate(request, &login_procedure)
88
92
  end
89
93
 
90
- def request_http_basic_authentication(realm = "Application")
91
- HttpAuthentication::Basic.authentication_request(self, realm)
94
+ def request_http_basic_authentication(realm = "Application", message = nil)
95
+ HttpAuthentication::Basic.authentication_request(self, realm, message)
92
96
  end
93
97
  end
94
98
 
@@ -99,33 +103,34 @@ module ActionController
99
103
  end
100
104
 
101
105
  def has_basic_credentials?(request)
102
- request.authorization.present? && (auth_scheme(request) == 'Basic')
106
+ request.authorization.present? && (auth_scheme(request).downcase == "basic")
103
107
  end
104
108
 
105
109
  def user_name_and_password(request)
106
- decode_credentials(request).split(':', 2)
110
+ decode_credentials(request).split(":", 2)
107
111
  end
108
112
 
109
113
  def decode_credentials(request)
110
- ::Base64.decode64(auth_param(request) || '')
114
+ ::Base64.decode64(auth_param(request) || "")
111
115
  end
112
116
 
113
117
  def auth_scheme(request)
114
- request.authorization.split(' ', 2).first
118
+ request.authorization.to_s.split(" ", 2).first
115
119
  end
116
120
 
117
121
  def auth_param(request)
118
- request.authorization.split(' ', 2).second
122
+ request.authorization.to_s.split(" ", 2).second
119
123
  end
120
124
 
121
125
  def encode_credentials(user_name, password)
122
126
  "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
123
127
  end
124
128
 
125
- def authentication_request(controller, realm)
126
- controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
129
+ def authentication_request(controller, realm, message)
130
+ message ||= "HTTP Basic: Access denied.\n"
131
+ controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
127
132
  controller.status = 401
128
- controller.response_body = "HTTP Basic: Access denied.\n"
133
+ controller.response_body = message
129
134
  end
130
135
  end
131
136
 
@@ -133,7 +138,7 @@ module ActionController
133
138
  #
134
139
  # === Simple \Digest example
135
140
  #
136
- # require 'digest/md5'
141
+ # require "digest/md5"
137
142
  # class PostsController < ApplicationController
138
143
  # REALM = "SuperSecret"
139
144
  # USERS = {"dhh" => "secret", #plain text password
@@ -175,8 +180,8 @@ module ActionController
175
180
  extend self
176
181
 
177
182
  module ControllerMethods
178
- def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
179
- authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
183
+ def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure)
184
+ authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message)
180
185
  end
181
186
 
182
187
  # Authenticate with HTTP Digest, returns true or false
@@ -207,7 +212,7 @@ module ActionController
207
212
  password = password_procedure.call(credentials[:username])
208
213
  return false unless password
209
214
 
210
- method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
215
+ method = request.get_header("rack.methodoverride.original_method") || request.get_header("REQUEST_METHOD")
211
216
  uri = credentials[:uri]
212
217
 
213
218
  [true, false].any? do |trailing_question_mark|
@@ -223,19 +228,19 @@ module ActionController
223
228
  # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
224
229
  # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
225
230
  # of a plain-text password.
226
- def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
231
+ def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
227
232
  ha1 = password_is_ha1 ? password : ha1(credentials, password)
228
- ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
229
- ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
233
+ ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
234
+ ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
230
235
  end
231
236
 
232
237
  def ha1(credentials, password)
233
- ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
238
+ ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
234
239
  end
235
240
 
236
241
  def encode_credentials(http_method, credentials, password, password_is_ha1)
237
242
  credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
238
- "Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
243
+ "Digest " + credentials.sort_by { |x| x[0].to_s }.map { |v| "#{v[0]}='#{v[1]}'" }.join(", ")
239
244
  end
240
245
 
241
246
  def decode_credentials_header(request)
@@ -243,9 +248,9 @@ module ActionController
243
248
  end
244
249
 
245
250
  def decode_credentials(header)
246
- ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, '').split(',').map do |pair|
247
- key, value = pair.split('=', 2)
248
- [key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
251
+ ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair|
252
+ key, value = pair.split("=", 2)
253
+ [key.strip, value.to_s.gsub(/^"|"$/, "").delete("'")]
249
254
  end]
250
255
  end
251
256
 
@@ -264,8 +269,8 @@ module ActionController
264
269
  end
265
270
 
266
271
  def secret_token(request)
267
- key_generator = request.env["action_dispatch.key_generator"]
268
- http_auth_salt = request.env["action_dispatch.http_auth_salt"]
272
+ key_generator = request.key_generator
273
+ http_auth_salt = request.http_auth_salt
269
274
  key_generator.generate_key(http_auth_salt)
270
275
  end
271
276
 
@@ -309,21 +314,20 @@ module ActionController
309
314
  end
310
315
 
311
316
  # Might want a shorter timeout depending on whether the request
312
- # is a PATCH, PUT, or POST, and if client is browser or web service.
317
+ # is a PATCH, PUT, or POST, and if the client is a browser or web service.
313
318
  # Can be much shorter if the Stale directive is implemented. This would
314
- # allow a user to use new nonce without prompting user again for their
319
+ # allow a user to use new nonce without prompting the user again for their
315
320
  # username and password.
316
- def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
321
+ def validate_nonce(secret_key, request, value, seconds_to_timeout = 5 * 60)
317
322
  return false if value.nil?
318
323
  t = ::Base64.decode64(value).split(":").first.to_i
319
324
  nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
320
325
  end
321
326
 
322
- # Opaque based on random generation - but changing each request?
327
+ # Opaque based on digest of secret key
323
328
  def opaque(secret_key)
324
329
  ::Digest::MD5.hexdigest(secret_key)
325
330
  end
326
-
327
331
  end
328
332
 
329
333
  # Makes it dead easy to do HTTP Token authentication.
@@ -346,7 +350,9 @@ module ActionController
346
350
  # private
347
351
  # def authenticate
348
352
  # authenticate_or_request_with_http_token do |token, options|
349
- # token == TOKEN
353
+ # # Compare the tokens in a time-constant manner, to mitigate
354
+ # # timing attacks.
355
+ # ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
350
356
  # end
351
357
  # end
352
358
  # end
@@ -358,14 +364,14 @@ module ActionController
358
364
  # class ApplicationController < ActionController::Base
359
365
  # before_action :set_account, :authenticate
360
366
  #
361
- # protected
367
+ # private
362
368
  # def set_account
363
369
  # @account = Account.find_by(url_name: request.subdomains.first)
364
370
  # end
365
371
  #
366
372
  # def authenticate
367
373
  # case request.format
368
- # when Mime::XML, Mime::ATOM
374
+ # when Mime[:xml], Mime[:atom]
369
375
  # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
370
376
  # @current_user = user
371
377
  # else
@@ -385,10 +391,9 @@ module ActionController
385
391
  # In your integration tests, you can do something like this:
386
392
  #
387
393
  # def test_access_granted_from_xml
388
- # get(
389
- # "/notes/1.xml", nil,
390
- # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
391
- # )
394
+ # authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
395
+ #
396
+ # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
392
397
  #
393
398
  # assert_equal 200, status
394
399
  # end
@@ -400,22 +405,22 @@ module ActionController
400
405
  #
401
406
  # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
402
407
  module Token
403
- TOKEN_KEY = 'token='
404
- TOKEN_REGEX = /^Token /
405
- AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
408
+ TOKEN_KEY = "token="
409
+ TOKEN_REGEX = /^(Token|Bearer)\s+/
410
+ AUTHN_PAIR_DELIMITERS = /(?:,|;|\t)/
406
411
  extend self
407
412
 
408
413
  module ControllerMethods
409
- def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
410
- authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
414
+ def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
415
+ authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
411
416
  end
412
417
 
413
418
  def authenticate_with_http_token(&login_procedure)
414
419
  Token.authenticate(self, &login_procedure)
415
420
  end
416
421
 
417
- def request_http_token_authentication(realm = "Application")
418
- Token.authentication_request(self, realm)
422
+ def request_http_token_authentication(realm = "Application", message = nil)
423
+ Token.authentication_request(self, realm, message)
419
424
  end
420
425
  end
421
426
 
@@ -440,15 +445,17 @@ module ActionController
440
445
  end
441
446
  end
442
447
 
443
- # Parses the token and options out of the token authorization header. If
444
- # the header looks like this:
448
+ # Parses the token and options out of the token Authorization header.
449
+ # The value for the Authorization header is expected to have the prefix
450
+ # <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
445
451
  # Authorization: Token token="abc", nonce="def"
446
- # Then the returned token is "abc", and the options is {nonce: "def"}
452
+ # Then the returned token is <tt>"abc"</tt>, and the options are
453
+ # <tt>{nonce: "def"}</tt>
447
454
  #
448
455
  # request - ActionDispatch::Request instance with the current headers.
449
456
  #
450
- # Returns an Array of [String, Hash] if a token is present.
451
- # Returns nil if no token is found.
457
+ # Returns an +Array+ of <tt>[String, Hash]</tt> if a token is present.
458
+ # Returns +nil+ if no token is found.
452
459
  def token_and_options(request)
453
460
  authorization_request = request.authorization.to_s
454
461
  if authorization_request[TOKEN_REGEX]
@@ -468,16 +475,16 @@ module ActionController
468
475
 
469
476
  # This removes the <tt>"</tt> characters wrapping the value.
470
477
  def rewrite_param_values(array_params)
471
- array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' }
478
+ array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
472
479
  end
473
480
 
474
481
  # This method takes an authorization body and splits up the key-value
475
482
  # pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
476
483
  # delimiters defined in +AUTHN_PAIR_DELIMITERS+.
477
484
  def raw_params(auth)
478
- _raw_params = auth.sub(TOKEN_REGEX, '').split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
485
+ _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
479
486
 
480
- if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
487
+ if !_raw_params.first&.start_with?(TOKEN_KEY)
481
488
  _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
482
489
  end
483
490
 
@@ -497,15 +504,16 @@ module ActionController
497
504
  "Token #{values * ", "}"
498
505
  end
499
506
 
500
- # Sets a WWW-Authenticate to let the client know a token is desired.
507
+ # Sets a WWW-Authenticate header to let the client know a token is desired.
501
508
  #
502
509
  # controller - ActionController::Base instance for the outgoing response.
503
510
  # realm - String realm to use in the header.
504
511
  #
505
512
  # Returns nothing.
506
- def authentication_request(controller, realm)
507
- controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
508
- controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
513
+ def authentication_request(controller, realm, message = nil)
514
+ message ||= "HTTP Token: Access denied.\n"
515
+ controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
516
+ controller.__send__ :render, plain: message, status: :unauthorized
509
517
  end
510
518
  end
511
519
  end
@@ -1,19 +1,63 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionController
4
+ # Handles implicit rendering for a controller action that does not
5
+ # explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
6
+ #
7
+ # For API controllers, the implicit response is always <tt>204 No Content</tt>.
8
+ #
9
+ # For all other controllers, we use these heuristics to decide whether to
10
+ # render a template, raise an error for a missing template, or respond with
11
+ # <tt>204 No Content</tt>:
12
+ #
13
+ # First, if we DO find a template, it's rendered. Template lookup accounts
14
+ # for the action name, locales, format, variant, template handlers, and more
15
+ # (see +render+ for details).
16
+ #
17
+ # Second, if we DON'T find a template but the controller action does have
18
+ # templates for other formats, variants, etc., then we trust that you meant
19
+ # to provide a template for this response, too, and we raise
20
+ # <tt>ActionController::UnknownFormat</tt> with an explanation.
21
+ #
22
+ # Third, if we DON'T find a template AND the request is a page load in a web
23
+ # browser (technically, a non-XHR GET request for an HTML response) where
24
+ # you reasonably expect to have rendered a template, then we raise
25
+ # <tt>ActionController::MissingExactTemplate</tt> with an explanation.
26
+ #
27
+ # Finally, if we DON'T find a template AND the request isn't a browser page
28
+ # load, then we implicitly respond with <tt>204 No Content</tt>.
2
29
  module ImplicitRender
3
- def send_action(method, *args)
4
- ret = super
5
- default_render unless performed?
6
- ret
7
- end
30
+ # :stopdoc:
31
+ include BasicImplicitRender
8
32
 
9
- def default_render(*args)
10
- render(*args)
33
+ def default_render
34
+ if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
35
+ render
36
+ elsif any_templates?(action_name.to_s, _prefixes)
37
+ message = "#{self.class.name}\##{action_name} is missing a template " \
38
+ "for this request format and variant.\n" \
39
+ "\nrequest.formats: #{request.formats.map(&:to_s).inspect}" \
40
+ "\nrequest.variant: #{request.variant.inspect}"
41
+
42
+ raise ActionController::UnknownFormat, message
43
+ elsif interactive_browser_request?
44
+ message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
45
+ raise ActionController::MissingExactTemplate, message
46
+ else
47
+ logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
48
+ super
49
+ end
11
50
  end
12
51
 
13
52
  def method_for_action(action_name)
14
53
  super || if template_exists?(action_name.to_s, _prefixes)
15
- "default_render"
16
- end
54
+ "default_render"
55
+ end
17
56
  end
57
+
58
+ private
59
+ def interactive_browser_request?
60
+ request.get? && request.format == Mime[:html] && !request.xhr?
61
+ end
18
62
  end
19
63
  end
@@ -1,9 +1,11 @@
1
- require 'benchmark'
2
- require 'abstract_controller/logger'
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+ require "abstract_controller/logger"
3
5
 
4
6
  module ActionController
5
7
  # Adds instrumentation to several ends in ActionController::Base. It also provides
6
- # some hooks related with process_action, this allows an ORM like Active Record
8
+ # some hooks related with process_action. This allows an ORM like Active Record
7
9
  # and/or DataMapper to plug in ActionController and show related information.
8
10
  #
9
11
  # Check ActiveRecord::Railties::ControllerRuntime for an example.
@@ -11,34 +13,34 @@ module ActionController
11
13
  extend ActiveSupport::Concern
12
14
 
13
15
  include AbstractController::Logger
14
- include ActionController::RackDelegation
15
16
 
16
17
  attr_internal :view_runtime
17
18
 
18
- def process_action(*args)
19
+ def process_action(*)
19
20
  raw_payload = {
20
- :controller => self.class.name,
21
- :action => self.action_name,
22
- :params => request.filtered_parameters,
23
- :format => request.format.try(:ref),
24
- :method => request.request_method,
25
- :path => (request.fullpath rescue "unknown")
21
+ controller: self.class.name,
22
+ action: action_name,
23
+ request: request,
24
+ params: request.filtered_parameters,
25
+ headers: request.headers,
26
+ format: request.format.ref,
27
+ method: request.request_method,
28
+ path: request.fullpath
26
29
  }
27
30
 
28
- ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
31
+ ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
29
32
 
30
33
  ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
31
- begin
32
- result = super
33
- payload[:status] = response.status
34
- result
35
- ensure
36
- append_info_to_payload(payload)
37
- end
34
+ result = super
35
+ payload[:response] = response
36
+ payload[:status] = response.status
37
+ result
38
+ ensure
39
+ append_info_to_payload(payload)
38
40
  end
39
41
  end
40
42
 
41
- def render(*args)
43
+ def render(*)
42
44
  render_output = nil
43
45
  self.view_runtime = cleanup_view_runtime do
44
46
  Benchmark.ms { render_output = super }
@@ -46,9 +48,9 @@ module ActionController
46
48
  render_output
47
49
  end
48
50
 
49
- def send_file(path, options={})
51
+ def send_file(path, options = {})
50
52
  ActiveSupport::Notifications.instrument("send_file.action_controller",
51
- options.merge(:path => path)) do
53
+ options.merge(path: path)) do
52
54
  super
53
55
  end
54
56
  end
@@ -59,8 +61,8 @@ module ActionController
59
61
  end
60
62
  end
61
63
 
62
- def redirect_to(*args)
63
- ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
64
+ def redirect_to(*)
65
+ ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
64
66
  result = super
65
67
  payload[:status] = response.status
66
68
  payload[:location] = response.filtered_location
@@ -69,28 +71,24 @@ module ActionController
69
71
  end
70
72
 
71
73
  private
72
-
73
74
  # A hook invoked every time a before callback is halted.
74
- def halted_callback_hook(filter)
75
- ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
75
+ def halted_callback_hook(filter, _)
76
+ ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
76
77
  end
77
78
 
78
- # A hook which allows you to clean up any time taken into account in
79
- # views wrongly, like database querying time.
79
+ # A hook which allows you to clean up any time, wrongly taken into account in
80
+ # views, like database querying time.
80
81
  #
81
82
  # def cleanup_view_runtime
82
83
  # super - time_taken_in_something_expensive
83
84
  # end
84
- #
85
- # :api: plugin
86
- def cleanup_view_runtime #:nodoc:
85
+ def cleanup_view_runtime # :doc:
87
86
  yield
88
87
  end
89
88
 
90
89
  # Every time after an action is processed, this method is invoked
91
90
  # with the payload, so you can add more information.
92
- # :api: plugin
93
- def append_info_to_payload(payload) #:nodoc:
91
+ def append_info_to_payload(payload) # :doc:
94
92
  payload[:view_runtime] = view_runtime
95
93
  end
96
94
 
@@ -98,7 +96,6 @@ module ActionController
98
96
  # A hook which allows other frameworks to log what happened during
99
97
  # controller process action. This method should return an array
100
98
  # with the messages to be added.
101
- # :api: plugin
102
99
  def log_process_action(payload) #:nodoc:
103
100
  messages, view_runtime = [], payload[:view_runtime]
104
101
  messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime