actionpack 5.2.8.1 → 6.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +270 -347
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +4 -3
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +12 -0
- data/lib/abstract_controller/collector.rb +1 -2
- data/lib/abstract_controller/helpers.rb +7 -6
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/translation.rb +4 -4
- data/lib/action_controller/api.rb +2 -1
- data/lib/action_controller/base.rb +2 -7
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/log_subscriber.rb +8 -5
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +9 -3
- data/lib/action_controller/metal/content_security_policy.rb +0 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -6
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +23 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/force_ssl.rb +15 -56
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +3 -4
- data/lib/action_controller/metal/http_authentication.rb +20 -21
- data/lib/action_controller/metal/implicit_render.rb +4 -14
- data/lib/action_controller/metal/instrumentation.rb +3 -6
- data/lib/action_controller/metal/live.rb +29 -31
- data/lib/action_controller/metal/mime_responds.rb +13 -2
- data/lib/action_controller/metal/params_wrapper.rb +18 -14
- data/lib/action_controller/metal/redirecting.rb +5 -5
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +2 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +25 -48
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +65 -44
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +8 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +17 -3
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +7 -8
- data/lib/action_controller.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +14 -11
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +28 -17
- data/lib/action_dispatch/http/filter_parameters.rb +8 -7
- data/lib/action_dispatch/http/filter_redirect.rb +1 -2
- data/lib/action_dispatch/http/headers.rb +1 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -6
- data/lib/action_dispatch/http/mime_type.rb +14 -8
- data/lib/action_dispatch/http/parameter_filter.rb +5 -79
- data/lib/action_dispatch/http/parameters.rb +15 -6
- data/lib/action_dispatch/http/request.rb +21 -14
- data/lib/action_dispatch/http/response.rb +40 -21
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +81 -82
- data/lib/action_dispatch/journey/formatter.rb +2 -3
- data/lib/action_dispatch/journey/gtg/builder.rb +0 -1
- data/lib/action_dispatch/journey/gtg/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -1
- data/lib/action_dispatch/journey/nodes/node.rb +9 -8
- data/lib/action_dispatch/journey/path/pattern.rb +6 -3
- data/lib/action_dispatch/journey/route.rb +5 -4
- data/lib/action_dispatch/journey/router/utils.rb +10 -10
- data/lib/action_dispatch/journey/router.rb +0 -4
- data/lib/action_dispatch/journey/routes.rb +0 -2
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +62 -78
- data/lib/action_dispatch/middleware/debug_exceptions.rb +45 -61
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -16
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +121 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +9 -12
- data/lib/action_dispatch/middleware/request_id.rb +2 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +0 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -7
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -2
- data/lib/action_dispatch/middleware/ssl.rb +8 -8
- data/lib/action_dispatch/middleware/stack.rb +38 -2
- data/lib/action_dispatch/middleware/static.rb +6 -7
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +5 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
- data/lib/action_dispatch/railtie.rb +7 -2
- data/lib/action_dispatch/request/session.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +97 -50
- data/lib/action_dispatch/routing/mapper.rb +63 -42
- data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -6
- data/lib/action_dispatch/routing/route_set.rb +25 -31
- data/lib/action_dispatch/routing/url_for.rb +2 -2
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/system_test_case.rb +44 -6
- data/lib/action_dispatch/system_testing/browser.rb +38 -7
- data/lib/action_dispatch/system_testing/driver.rb +11 -2
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +2 -3
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +33 -12
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +2 -2
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +7 -2
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +29 -15
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -34,7 +34,7 @@ module ActionController
|
|
34
34
|
# end
|
35
35
|
# end
|
36
36
|
#
|
37
|
-
# Then, in any view rendered by <tt>
|
37
|
+
# Then, in any view rendered by <tt>EventsController</tt>, the <tt>format_time</tt> method can be called:
|
38
38
|
#
|
39
39
|
# <% @events.each do |event| -%>
|
40
40
|
# <p>
|
@@ -75,7 +75,7 @@ module ActionController
|
|
75
75
|
# Provides a proxy to access helper methods from outside the view.
|
76
76
|
def helpers
|
77
77
|
@helper_proxy ||= begin
|
78
|
-
proxy = ActionView::Base.
|
78
|
+
proxy = ActionView::Base.empty
|
79
79
|
proxy.config = config.inheritable_copy
|
80
80
|
proxy.extend(_helpers)
|
81
81
|
end
|
@@ -100,8 +100,7 @@ module ActionController
|
|
100
100
|
# # => ["application", "chart", "rubygems"]
|
101
101
|
def all_helpers_from_path(path)
|
102
102
|
helpers = Array(path).flat_map do |_path|
|
103
|
-
|
104
|
-
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
|
103
|
+
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
|
105
104
|
names.sort!
|
106
105
|
end
|
107
106
|
helpers.uniq!
|
@@ -56,8 +56,9 @@ module ActionController
|
|
56
56
|
# In your integration tests, you can do something like this:
|
57
57
|
#
|
58
58
|
# def test_access_granted_from_xml
|
59
|
-
#
|
60
|
-
#
|
59
|
+
# authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
60
|
+
#
|
61
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
61
62
|
#
|
62
63
|
# assert_equal 200, status
|
63
64
|
# end
|
@@ -68,21 +69,20 @@ module ActionController
|
|
68
69
|
extend ActiveSupport::Concern
|
69
70
|
|
70
71
|
module ClassMethods
|
71
|
-
def http_basic_authenticate_with(
|
72
|
-
before_action(options
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
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
|
+
ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
|
80
|
+
ActiveSupport::SecurityUtils.secure_compare(given_password, password)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
def authenticate_or_request_with_http_basic(realm =
|
85
|
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
|
84
|
+
def authenticate_or_request_with_http_basic(realm = nil, message = nil, &login_procedure)
|
85
|
+
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message)
|
86
86
|
end
|
87
87
|
|
88
88
|
def authenticate_with_http_basic(&login_procedure)
|
@@ -126,7 +126,7 @@ module ActionController
|
|
126
126
|
|
127
127
|
def authentication_request(controller, realm, message)
|
128
128
|
message ||= "HTTP Basic: Access denied.\n"
|
129
|
-
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'
|
129
|
+
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
|
130
130
|
controller.status = 401
|
131
131
|
controller.response_body = message
|
132
132
|
end
|
@@ -389,10 +389,9 @@ module ActionController
|
|
389
389
|
# In your integration tests, you can do something like this:
|
390
390
|
#
|
391
391
|
# def test_access_granted_from_xml
|
392
|
-
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
# )
|
392
|
+
# authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
|
393
|
+
#
|
394
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
396
395
|
#
|
397
396
|
# assert_equal 200, status
|
398
397
|
# end
|
@@ -474,7 +473,7 @@ module ActionController
|
|
474
473
|
|
475
474
|
# This removes the <tt>"</tt> characters wrapping the value.
|
476
475
|
def rewrite_param_values(array_params)
|
477
|
-
array_params.each { |param| (param[1] || ""
|
476
|
+
array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
|
478
477
|
end
|
479
478
|
|
480
479
|
# This method takes an authorization body and splits up the key-value
|
@@ -511,7 +510,7 @@ module ActionController
|
|
511
510
|
# Returns nothing.
|
512
511
|
def authentication_request(controller, realm, message = nil)
|
513
512
|
message ||= "HTTP Token: Access denied.\n"
|
514
|
-
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'
|
513
|
+
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
|
515
514
|
controller.__send__ :render, plain: message, status: :unauthorized
|
516
515
|
end
|
517
516
|
end
|
@@ -30,9 +30,9 @@ module ActionController
|
|
30
30
|
# :stopdoc:
|
31
31
|
include BasicImplicitRender
|
32
32
|
|
33
|
-
def default_render
|
33
|
+
def default_render
|
34
34
|
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
|
35
|
-
render
|
35
|
+
render
|
36
36
|
elsif any_templates?(action_name.to_s, _prefixes)
|
37
37
|
message = "#{self.class.name}\##{action_name} is missing a template " \
|
38
38
|
"for this request format and variant.\n" \
|
@@ -41,18 +41,8 @@ module ActionController
|
|
41
41
|
|
42
42
|
raise ActionController::UnknownFormat, message
|
43
43
|
elsif interactive_browser_request?
|
44
|
-
message = "#{self.class.name}\##{action_name} is missing a template "
|
45
|
-
|
46
|
-
"request.formats: #{request.formats.map(&:to_s).inspect}\n" \
|
47
|
-
"request.variant: #{request.variant.inspect}\n\n" \
|
48
|
-
"NOTE! For XHR/Ajax or API requests, this action would normally " \
|
49
|
-
"respond with 204 No Content: an empty white screen. Since you're " \
|
50
|
-
"loading it in a web browser, we assume that you expected to " \
|
51
|
-
"actually render a template, not nothing, so we're showing an " \
|
52
|
-
"error to be extra-clear. If you expect 204 No Content, carry on. " \
|
53
|
-
"That's what you'll get from an XHR or API request. Give it a shot."
|
54
|
-
|
55
|
-
raise ActionController::UnknownFormat, message
|
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
|
56
46
|
else
|
57
47
|
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
|
58
48
|
super
|
@@ -30,13 +30,11 @@ module ActionController
|
|
30
30
|
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
31
31
|
|
32
32
|
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
33
|
-
|
34
|
-
result = super
|
33
|
+
super.tap do
|
35
34
|
payload[:status] = response.status
|
36
|
-
result
|
37
|
-
ensure
|
38
|
-
append_info_to_payload(payload)
|
39
35
|
end
|
36
|
+
ensure
|
37
|
+
append_info_to_payload(payload)
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
@@ -71,7 +69,6 @@ module ActionController
|
|
71
69
|
end
|
72
70
|
|
73
71
|
private
|
74
|
-
|
75
72
|
# A hook invoked every time a before callback is halted.
|
76
73
|
def halted_callback_hook(filter)
|
77
74
|
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
@@ -86,7 +86,7 @@ module ActionController
|
|
86
86
|
# Note: SSEs are not currently supported by IE. However, they are supported
|
87
87
|
# by Chrome, Firefox, Opera, and Safari.
|
88
88
|
class SSE
|
89
|
-
|
89
|
+
PERMITTED_OPTIONS = %w( retry event id )
|
90
90
|
|
91
91
|
def initialize(stream, options = {})
|
92
92
|
@stream = stream
|
@@ -107,17 +107,16 @@ module ActionController
|
|
107
107
|
end
|
108
108
|
|
109
109
|
private
|
110
|
-
|
111
110
|
def perform_write(json, options)
|
112
111
|
current_options = @options.merge(options).stringify_keys
|
113
112
|
|
114
|
-
|
113
|
+
PERMITTED_OPTIONS.each do |option_name|
|
115
114
|
if (option_value = current_options[option_name])
|
116
115
|
@stream.write "#{option_name}: #{option_value}\n"
|
117
116
|
end
|
118
117
|
end
|
119
118
|
|
120
|
-
message = json.gsub("\n"
|
119
|
+
message = json.gsub("\n", "\ndata: ")
|
121
120
|
@stream.write "data: #{message}\n\n"
|
122
121
|
end
|
123
122
|
end
|
@@ -146,7 +145,7 @@ module ActionController
|
|
146
145
|
|
147
146
|
def write(string)
|
148
147
|
unless @response.committed?
|
149
|
-
@response.
|
148
|
+
@response.headers["Cache-Control"] ||= "no-cache"
|
150
149
|
@response.delete_header "Content-Length"
|
151
150
|
end
|
152
151
|
|
@@ -205,7 +204,6 @@ module ActionController
|
|
205
204
|
end
|
206
205
|
|
207
206
|
private
|
208
|
-
|
209
207
|
def each_chunk(&block)
|
210
208
|
loop do
|
211
209
|
str = nil
|
@@ -220,7 +218,6 @@ module ActionController
|
|
220
218
|
|
221
219
|
class Response < ActionDispatch::Response #:nodoc: all
|
222
220
|
private
|
223
|
-
|
224
221
|
def before_committed
|
225
222
|
super
|
226
223
|
jar = request.cookie_jar
|
@@ -280,33 +277,34 @@ module ActionController
|
|
280
277
|
raise error if error
|
281
278
|
end
|
282
279
|
|
283
|
-
# Spawn a new thread to serve up the controller in. This is to get
|
284
|
-
# around the fact that Rack isn't based around IOs and we need to use
|
285
|
-
# a thread to stream data from the response bodies. Nobody should call
|
286
|
-
# this method except in Rails internals. Seriously!
|
287
|
-
def new_controller_thread # :nodoc:
|
288
|
-
Thread.new {
|
289
|
-
t2 = Thread.current
|
290
|
-
t2.abort_on_exception = true
|
291
|
-
yield
|
292
|
-
}
|
293
|
-
end
|
294
|
-
|
295
|
-
def log_error(exception)
|
296
|
-
logger = ActionController::Base.logger
|
297
|
-
return unless logger
|
298
|
-
|
299
|
-
logger.fatal do
|
300
|
-
message = "\n#{exception.class} (#{exception.message}):\n".dup
|
301
|
-
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
|
302
|
-
message << " " << exception.backtrace.join("\n ")
|
303
|
-
"#{message}\n\n"
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
280
|
def response_body=(body)
|
308
281
|
super
|
309
282
|
response.close if response
|
310
283
|
end
|
284
|
+
|
285
|
+
private
|
286
|
+
# Spawn a new thread to serve up the controller in. This is to get
|
287
|
+
# around the fact that Rack isn't based around IOs and we need to use
|
288
|
+
# a thread to stream data from the response bodies. Nobody should call
|
289
|
+
# this method except in Rails internals. Seriously!
|
290
|
+
def new_controller_thread # :nodoc:
|
291
|
+
Thread.new {
|
292
|
+
t2 = Thread.current
|
293
|
+
t2.abort_on_exception = true
|
294
|
+
yield
|
295
|
+
}
|
296
|
+
end
|
297
|
+
|
298
|
+
def log_error(exception)
|
299
|
+
logger = ActionController::Base.logger
|
300
|
+
return unless logger
|
301
|
+
|
302
|
+
logger.fatal do
|
303
|
+
message = +"\n#{exception.class} (#{exception.message}):\n"
|
304
|
+
message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
|
305
|
+
message << " " << exception.backtrace.join("\n ")
|
306
|
+
"#{message}\n\n"
|
307
|
+
end
|
308
|
+
end
|
311
309
|
end
|
312
310
|
end
|
@@ -11,7 +11,7 @@ module ActionController #:nodoc:
|
|
11
11
|
# @people = Person.all
|
12
12
|
# end
|
13
13
|
#
|
14
|
-
# That action implicitly responds to all formats, but formats can also be
|
14
|
+
# That action implicitly responds to all formats, but formats can also be explicitly enumerated:
|
15
15
|
#
|
16
16
|
# def index
|
17
17
|
# @people = Person.all
|
@@ -105,7 +105,7 @@ module ActionController #:nodoc:
|
|
105
105
|
#
|
106
106
|
# Mime::Type.register "image/jpg", :jpg
|
107
107
|
#
|
108
|
-
#
|
108
|
+
# +respond_to+ also allows you to specify a common block for different formats by using +any+:
|
109
109
|
#
|
110
110
|
# def index
|
111
111
|
# @people = Person.all
|
@@ -124,6 +124,14 @@ module ActionController #:nodoc:
|
|
124
124
|
#
|
125
125
|
# render json: @people
|
126
126
|
#
|
127
|
+
# +any+ can also be used with no arguments, in which case it will be used for any format requested by
|
128
|
+
# the user:
|
129
|
+
#
|
130
|
+
# respond_to do |format|
|
131
|
+
# format.html
|
132
|
+
# format.any { redirect_to support_path }
|
133
|
+
# end
|
134
|
+
#
|
127
135
|
# Formats can have different variants.
|
128
136
|
#
|
129
137
|
# The request variant is a specialization of the request format, like <tt>:tablet</tt>,
|
@@ -197,6 +205,9 @@ module ActionController #:nodoc:
|
|
197
205
|
yield collector if block_given?
|
198
206
|
|
199
207
|
if format = collector.negotiate_format(request)
|
208
|
+
if media_type && media_type != format
|
209
|
+
raise ActionController::RespondToMismatchError
|
210
|
+
end
|
200
211
|
_process_format(format)
|
201
212
|
_set_rendered_content_type format
|
202
213
|
response = collector.response
|
@@ -115,7 +115,7 @@ module ActionController
|
|
115
115
|
|
116
116
|
if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
|
117
117
|
self.include += m.nested_attributes_options.keys.map do |key|
|
118
|
-
key.to_s.
|
118
|
+
(+key.to_s).concat("_attributes")
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
@@ -241,23 +241,11 @@ module ActionController
|
|
241
241
|
# Performs parameters wrapping upon the request. Called automatically
|
242
242
|
# by the metal call stack.
|
243
243
|
def process_action(*args)
|
244
|
-
if _wrapper_enabled?
|
245
|
-
wrapped_hash = _wrap_parameters request.request_parameters
|
246
|
-
wrapped_keys = request.request_parameters.keys
|
247
|
-
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
|
248
|
-
|
249
|
-
# This will make the wrapped hash accessible from controller and view.
|
250
|
-
request.parameters.merge! wrapped_hash
|
251
|
-
request.request_parameters.merge! wrapped_hash
|
252
|
-
|
253
|
-
# This will display the wrapped hash in the log file.
|
254
|
-
request.filtered_parameters.merge! wrapped_filtered_hash
|
255
|
-
end
|
244
|
+
_perform_parameter_wrapping if _wrapper_enabled?
|
256
245
|
super
|
257
246
|
end
|
258
247
|
|
259
248
|
private
|
260
|
-
|
261
249
|
# Returns the wrapper key which will be used to store wrapped parameters.
|
262
250
|
def _wrapper_key
|
263
251
|
_wrapper_options.name
|
@@ -287,7 +275,23 @@ module ActionController
|
|
287
275
|
return false unless request.has_content_type?
|
288
276
|
|
289
277
|
ref = request.content_mime_type.ref
|
278
|
+
|
290
279
|
_wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
|
280
|
+
rescue ActionDispatch::Http::Parameters::ParseError
|
281
|
+
false
|
282
|
+
end
|
283
|
+
|
284
|
+
def _perform_parameter_wrapping
|
285
|
+
wrapped_hash = _wrap_parameters request.request_parameters
|
286
|
+
wrapped_keys = request.request_parameters.keys
|
287
|
+
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
|
288
|
+
|
289
|
+
# This will make the wrapped hash accessible from controller and view.
|
290
|
+
request.parameters.merge! wrapped_hash
|
291
|
+
request.request_parameters.merge! wrapped_hash
|
292
|
+
|
293
|
+
# This will display the wrapped hash in the log file.
|
294
|
+
request.filtered_parameters.merge! wrapped_filtered_hash
|
291
295
|
end
|
292
296
|
end
|
293
297
|
end
|
@@ -55,11 +55,11 @@ module ActionController
|
|
55
55
|
# Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
|
56
56
|
# To terminate the execution of the function immediately after the +redirect_to+, use return.
|
57
57
|
# redirect_to post_url(@post) and return
|
58
|
-
def redirect_to(options = {},
|
58
|
+
def redirect_to(options = {}, response_options = {})
|
59
59
|
raise ActionControllerError.new("Cannot redirect to nil!") unless options
|
60
60
|
raise AbstractController::DoubleRenderError if response_body
|
61
61
|
|
62
|
-
self.status = _extract_redirect_to_status(options,
|
62
|
+
self.status = _extract_redirect_to_status(options, response_options)
|
63
63
|
self.location = _compute_redirect_to_location(request, options)
|
64
64
|
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
|
65
65
|
end
|
@@ -114,11 +114,11 @@ module ActionController
|
|
114
114
|
public :_compute_redirect_to_location
|
115
115
|
|
116
116
|
private
|
117
|
-
def _extract_redirect_to_status(options,
|
117
|
+
def _extract_redirect_to_status(options, response_options)
|
118
118
|
if options.is_a?(Hash) && options.key?(:status)
|
119
119
|
Rack::Utils.status_code(options.delete(:status))
|
120
|
-
elsif
|
121
|
-
Rack::Utils.status_code(
|
120
|
+
elsif response_options.key?(:status)
|
121
|
+
Rack::Utils.status_code(response_options[:status])
|
122
122
|
else
|
123
123
|
302
|
124
124
|
end
|
@@ -157,24 +157,24 @@ module ActionController
|
|
157
157
|
json = json.to_json(options) unless json.kind_of?(String)
|
158
158
|
|
159
159
|
if options[:callback].present?
|
160
|
-
if
|
160
|
+
if media_type.nil? || media_type == Mime[:json]
|
161
161
|
self.content_type = Mime[:js]
|
162
162
|
end
|
163
163
|
|
164
164
|
"/**/#{options[:callback]}(#{json})"
|
165
165
|
else
|
166
|
-
self.content_type
|
166
|
+
self.content_type = Mime[:json] if media_type.nil?
|
167
167
|
json
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
171
|
add :js do |js, options|
|
172
|
-
self.content_type
|
172
|
+
self.content_type = Mime[:js] if media_type.nil?
|
173
173
|
js.respond_to?(:to_js) ? js.to_js(options) : js
|
174
174
|
end
|
175
175
|
|
176
176
|
add :xml do |xml, options|
|
177
|
-
self.content_type
|
177
|
+
self.content_type = Mime[:xml] if media_type.nil?
|
178
178
|
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
|
179
179
|
end
|
180
180
|
end
|
@@ -40,7 +40,7 @@ module ActionController
|
|
40
40
|
def render_to_string(*)
|
41
41
|
result = super
|
42
42
|
if result.respond_to?(:each)
|
43
|
-
string = ""
|
43
|
+
string = +""
|
44
44
|
result.each { |r| string << r }
|
45
45
|
string
|
46
46
|
else
|
@@ -53,7 +53,6 @@ module ActionController
|
|
53
53
|
end
|
54
54
|
|
55
55
|
private
|
56
|
-
|
57
56
|
def _process_variant(options)
|
58
57
|
if defined?(request) && !request.nil? && request.variant.present?
|
59
58
|
options[:variant] = request.variant
|
@@ -73,7 +72,7 @@ module ActionController
|
|
73
72
|
end
|
74
73
|
|
75
74
|
def _set_rendered_content_type(format)
|
76
|
-
if format && !response.
|
75
|
+
if format && !response.media_type
|
77
76
|
self.content_type = format.to_s
|
78
77
|
end
|
79
78
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require "rack/session/abstract/id"
|
4
4
|
require "action_controller/metal/exceptions"
|
5
5
|
require "active_support/security_utils"
|
6
|
-
require "active_support/core_ext/string/strip"
|
7
6
|
|
8
7
|
module ActionController #:nodoc:
|
9
8
|
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
|
@@ -18,7 +17,7 @@ module ActionController #:nodoc:
|
|
18
17
|
# access. When a request reaches your application, \Rails verifies the received
|
19
18
|
# token with the token in the session. All requests are checked except GET requests
|
20
19
|
# as these should be idempotent. Keep in mind that all session-oriented requests
|
21
|
-
#
|
20
|
+
# are CSRF protected by default, including JavaScript and HTML requests.
|
22
21
|
#
|
23
22
|
# Since HTML and JavaScript requests are typically made from the browser, we
|
24
23
|
# need to ensure to verify request authenticity for the web browser. We can
|
@@ -31,31 +30,30 @@ module ActionController #:nodoc:
|
|
31
30
|
# URL on your site. When your JavaScript response loads on their site, it executes.
|
32
31
|
# With carefully crafted JavaScript on their end, sensitive data in your JavaScript
|
33
32
|
# response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
|
34
|
-
# Ajax) requests are allowed to make
|
33
|
+
# Ajax) requests are allowed to make requests for JavaScript responses.
|
35
34
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# <tt>
|
35
|
+
# Subclasses of <tt>ActionController::Base</tt> are protected by default with the
|
36
|
+
# <tt>:exception</tt> strategy, which raises an
|
37
|
+
# <tt>ActionController::InvalidAuthenticityToken</tt> error on unverified requests.
|
38
|
+
#
|
39
|
+
# APIs may want to disable this behavior since they are typically designed to be
|
40
|
+
# state-less: that is, the request API client handles the session instead of Rails.
|
41
|
+
# One way to achieve this is to use the <tt>:null_session</tt> strategy instead,
|
42
|
+
# which allows unverified requests to be handled, but with an empty session:
|
39
43
|
#
|
40
44
|
# class ApplicationController < ActionController::Base
|
41
|
-
# protect_from_forgery
|
45
|
+
# protect_from_forgery with: :null_session
|
42
46
|
# end
|
43
47
|
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# <tt>:null_session</tt> method, which provides an empty session
|
47
|
-
# during request.
|
48
|
-
#
|
49
|
-
# We may want to disable CSRF protection for APIs since they are typically
|
50
|
-
# designed to be state-less. That is, the request API client will handle
|
51
|
-
# the session for you instead of Rails.
|
48
|
+
# Note that API only applications don't include this module or a session middleware
|
49
|
+
# by default, and so don't require CSRF protection to be configured.
|
52
50
|
#
|
53
51
|
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
|
54
52
|
# value of this token must be added to every layout that renders forms by including
|
55
53
|
# <tt>csrf_meta_tags</tt> in the HTML +head+.
|
56
54
|
#
|
57
55
|
# Learn more about CSRF attacks and securing your application in the
|
58
|
-
# {Ruby on Rails Security Guide}[
|
56
|
+
# {Ruby on Rails Security Guide}[https://guides.rubyonrails.org/security.html].
|
59
57
|
module RequestForgeryProtection
|
60
58
|
extend ActiveSupport::Concern
|
61
59
|
|
@@ -149,7 +147,6 @@ module ActionController #:nodoc:
|
|
149
147
|
end
|
150
148
|
|
151
149
|
private
|
152
|
-
|
153
150
|
def protection_method_class(name)
|
154
151
|
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
|
155
152
|
rescue NameError
|
@@ -173,7 +170,6 @@ module ActionController #:nodoc:
|
|
173
170
|
end
|
174
171
|
|
175
172
|
private
|
176
|
-
|
177
173
|
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
|
178
174
|
def initialize(req)
|
179
175
|
super(nil, req)
|
@@ -280,7 +276,7 @@ module ActionController #:nodoc:
|
|
280
276
|
|
281
277
|
# Check for cross-origin JavaScript responses.
|
282
278
|
def non_xhr_javascript_response? # :doc:
|
283
|
-
|
279
|
+
%r(\A(?:text|application)/javascript).match?(media_type) && !request.xhr?
|
284
280
|
end
|
285
281
|
|
286
282
|
AUTHENTICITY_TOKEN_LENGTH = 32
|
@@ -425,9 +421,14 @@ module ActionController #:nodoc:
|
|
425
421
|
end
|
426
422
|
|
427
423
|
def xor_byte_strings(s1, s2) # :doc:
|
428
|
-
|
429
|
-
s1.
|
430
|
-
|
424
|
+
s2 = s2.dup
|
425
|
+
size = s1.bytesize
|
426
|
+
i = 0
|
427
|
+
while i < size
|
428
|
+
s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
|
429
|
+
i += 1
|
430
|
+
end
|
431
|
+
s2
|
431
432
|
end
|
432
433
|
|
433
434
|
# The form's authenticity parameter. Override to provide your own.
|
@@ -440,11 +441,11 @@ module ActionController #:nodoc:
|
|
440
441
|
allow_forgery_protection
|
441
442
|
end
|
442
443
|
|
443
|
-
NULL_ORIGIN_MESSAGE =
|
444
|
+
NULL_ORIGIN_MESSAGE = <<~MSG
|
444
445
|
The browser returned a 'null' origin for a request with origin-based forgery protection turned on. This usually
|
445
446
|
means you have the 'no-referrer' Referrer-Policy header enabled, or that the request came from a site that
|
446
447
|
refused to give its origin. This makes it impossible for Rails to verify the source of the requests. Likely the
|
447
|
-
best solution is to change your referrer policy to something less strict like same-origin or strict-
|
448
|
+
best solution is to change your referrer policy to something less strict like same-origin or strict-origin.
|
448
449
|
If you cannot change the referrer policy, you can disable origin checking with the
|
449
450
|
Rails.application.config.action_controller.forgery_protection_origin_check setting.
|
450
451
|
MSG
|
@@ -474,30 +475,6 @@ module ActionController #:nodoc:
|
|
474
475
|
end
|
475
476
|
end
|
476
477
|
|
477
|
-
if RUBY_VERSION.start_with?("2.2")
|
478
|
-
# Backported https://github.com/ruby/ruby/commit/6b6680945ed3274cddbc34fdfd410d74081a3e94
|
479
|
-
using Module.new {
|
480
|
-
refine Base64.singleton_class do
|
481
|
-
def urlsafe_encode64(bin, padding: true)
|
482
|
-
str = strict_encode64(bin).tr("+/", "-_")
|
483
|
-
str = str.delete("=") unless padding
|
484
|
-
str
|
485
|
-
end
|
486
|
-
|
487
|
-
def urlsafe_decode64(str)
|
488
|
-
# NOTE: RFC 4648 does say nothing about unpadded input, but says that
|
489
|
-
# "the excess pad characters MAY also be ignored", so it is inferred that
|
490
|
-
# unpadded input is also acceptable.
|
491
|
-
str = str.tr("-_", "+/")
|
492
|
-
if !str.end_with?("=") && str.length % 4 != 0
|
493
|
-
str = str.ljust((str.length + 3) & ~3, "=")
|
494
|
-
end
|
495
|
-
strict_decode64(str)
|
496
|
-
end
|
497
|
-
end
|
498
|
-
}
|
499
|
-
end
|
500
|
-
|
501
478
|
def encode_csrf_token(csrf_token) # :nodoc:
|
502
479
|
if urlsafe_csrf_tokens
|
503
480
|
Base64.urlsafe_encode64(csrf_token, padding: false)
|