actionpack 8.0.2 → 8.1.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +252 -137
- data/README.rdoc +1 -1
- data/lib/abstract_controller/asset_paths.rb +4 -2
- data/lib/abstract_controller/base.rb +11 -14
- data/lib/abstract_controller/caching.rb +6 -3
- data/lib/abstract_controller/collector.rb +1 -1
- data/lib/abstract_controller/logger.rb +2 -1
- data/lib/action_controller/base.rb +1 -1
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/form_builder.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +7 -0
- data/lib/action_controller/metal/allow_browser.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +25 -0
- data/lib/action_controller/metal/data_streaming.rb +1 -3
- data/lib/action_controller/metal/exceptions.rb +5 -0
- data/lib/action_controller/metal/flash.rb +1 -4
- data/lib/action_controller/metal/head.rb +3 -1
- data/lib/action_controller/metal/live.rb +0 -6
- data/lib/action_controller/metal/permissions_policy.rb +9 -0
- data/lib/action_controller/metal/rate_limiting.rb +22 -7
- data/lib/action_controller/metal/redirecting.rb +63 -7
- data/lib/action_controller/metal/renderers.rb +27 -6
- data/lib/action_controller/metal/rendering.rb +8 -2
- data/lib/action_controller/metal/request_forgery_protection.rb +18 -10
- data/lib/action_controller/metal/rescue.rb +9 -0
- data/lib/action_controller/railtie.rb +2 -6
- data/lib/action_controller/renderer.rb +0 -1
- data/lib/action_dispatch/constants.rb +6 -0
- data/lib/action_dispatch/http/cache.rb +111 -1
- data/lib/action_dispatch/http/content_security_policy.rb +13 -1
- data/lib/action_dispatch/http/filter_parameters.rb +5 -3
- data/lib/action_dispatch/http/mime_negotiation.rb +8 -3
- data/lib/action_dispatch/http/mime_types.rb +1 -0
- data/lib/action_dispatch/http/param_builder.rb +28 -27
- data/lib/action_dispatch/http/parameters.rb +3 -3
- data/lib/action_dispatch/http/permissions_policy.rb +4 -0
- data/lib/action_dispatch/http/query_parser.rb +12 -10
- data/lib/action_dispatch/http/request.rb +10 -5
- data/lib/action_dispatch/http/response.rb +65 -17
- data/lib/action_dispatch/http/url.rb +101 -5
- data/lib/action_dispatch/journey/gtg/simulator.rb +33 -12
- data/lib/action_dispatch/journey/gtg/transition_table.rb +29 -39
- data/lib/action_dispatch/journey/nodes/node.rb +2 -1
- data/lib/action_dispatch/journey/route.rb +45 -31
- data/lib/action_dispatch/journey/router/utils.rb +8 -14
- data/lib/action_dispatch/journey/router.rb +59 -81
- data/lib/action_dispatch/journey/routes.rb +7 -0
- data/lib/action_dispatch/journey/visitors.rb +55 -23
- data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
- data/lib/action_dispatch/middleware/cookies.rb +4 -2
- data/lib/action_dispatch/middleware/debug_exceptions.rb +10 -2
- data/lib/action_dispatch/middleware/debug_view.rb +11 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +14 -8
- data/lib/action_dispatch/middleware/executor.rb +12 -2
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -6
- data/lib/action_dispatch/middleware/session/cache_store.rb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
- data/lib/action_dispatch/railtie.rb +10 -2
- data/lib/action_dispatch/routing/inspector.rb +4 -1
- data/lib/action_dispatch/routing/mapper.rb +323 -173
- data/lib/action_dispatch/routing/route_set.rb +3 -6
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +2 -2
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +11 -3
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +13 -12
@@ -128,7 +128,7 @@ module ActionController
|
|
128
128
|
#
|
129
129
|
# Action Controller sends content to the user by using one of five rendering
|
130
130
|
# methods. The most versatile and common is the rendering of a template.
|
131
|
-
#
|
131
|
+
# Also included with \Rails is Action View, which enables rendering of ERB
|
132
132
|
# templates. It's automatically configured. The controller passes objects to the
|
133
133
|
# view by assigning instance variables:
|
134
134
|
#
|
@@ -9,9 +9,8 @@ module ActionController
|
|
9
9
|
# of calculations, renderings, and database calls around for subsequent
|
10
10
|
# requests.
|
11
11
|
#
|
12
|
-
# You can read more about each approach by clicking the modules below.
|
13
|
-
#
|
14
12
|
# Note: To turn off all caching provided by Action Controller, set
|
13
|
+
#
|
15
14
|
# config.action_controller.perform_caching = false
|
16
15
|
#
|
17
16
|
# ## Caching stores
|
@@ -40,7 +40,7 @@ module ActionController
|
|
40
40
|
# rendered by this controller and its subclasses.
|
41
41
|
#
|
42
42
|
# #### Parameters
|
43
|
-
# * `builder` - Default form builder
|
43
|
+
# * `builder` - Default form builder. Accepts a subclass of
|
44
44
|
# ActionView::Helpers::FormBuilder
|
45
45
|
def default_form_builder(builder)
|
46
46
|
self._default_form_builder = builder
|
@@ -49,6 +49,13 @@ module ActionController
|
|
49
49
|
end
|
50
50
|
subscribe_log_level :halted_callback, :info
|
51
51
|
|
52
|
+
# Manually subscribed below
|
53
|
+
def rescue_from_callback(event)
|
54
|
+
exception = event.payload[:exception]
|
55
|
+
info { "rescue_from handled #{exception.class} (#{exception.message}) - #{exception.backtrace.first.delete_prefix("#{Rails.root}/")}" }
|
56
|
+
end
|
57
|
+
subscribe_log_level :rescue_from_callback, :info
|
58
|
+
|
52
59
|
def send_file(event)
|
53
60
|
info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
|
54
61
|
end
|
@@ -14,7 +14,7 @@ module ActionController # :nodoc:
|
|
14
14
|
# aren't reporting a user-agent header, will be allowed access.
|
15
15
|
#
|
16
16
|
# A browser that's blocked will by default be served the file in
|
17
|
-
# public/406-unsupported-browser.html with
|
17
|
+
# public/406-unsupported-browser.html with an HTTP status code of "406 Not
|
18
18
|
# Acceptable".
|
19
19
|
#
|
20
20
|
# In addition to specifically named browser versions, you can also pass
|
@@ -332,6 +332,31 @@ module ActionController
|
|
332
332
|
response.cache_control.replace(no_store: true)
|
333
333
|
end
|
334
334
|
|
335
|
+
# Adds the `must-understand` directive to the `Cache-Control` header, which indicates
|
336
|
+
# that a cache MUST understand the semantics of the response status code that has been
|
337
|
+
# received, or discard the response.
|
338
|
+
#
|
339
|
+
# This is particularly useful when returning responses with new or uncommon
|
340
|
+
# status codes that might not be properly interpreted by older caches.
|
341
|
+
#
|
342
|
+
# #### Example
|
343
|
+
#
|
344
|
+
# def show
|
345
|
+
# @article = Article.find(params[:id])
|
346
|
+
#
|
347
|
+
# if @article.early_access?
|
348
|
+
# must_understand
|
349
|
+
# render status: 203 # Non-Authoritative Information
|
350
|
+
# else
|
351
|
+
# fresh_when @article
|
352
|
+
# end
|
353
|
+
# end
|
354
|
+
#
|
355
|
+
def must_understand
|
356
|
+
response.cache_control[:must_understand] = true
|
357
|
+
response.cache_control[:no_store] = true
|
358
|
+
end
|
359
|
+
|
335
360
|
private
|
336
361
|
def combine_etags(validator, options)
|
337
362
|
[validator, *etaggers.map { |etagger| instance_exec(options, &etagger) }].compact
|
@@ -134,9 +134,7 @@ module ActionController # :nodoc:
|
|
134
134
|
raise ArgumentError, ":type option required" if content_type.nil?
|
135
135
|
|
136
136
|
if content_type.is_a?(Symbol)
|
137
|
-
|
138
|
-
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
|
139
|
-
self.content_type = extension
|
137
|
+
self.content_type = content_type
|
140
138
|
else
|
141
139
|
if !type_provided && options[:filename]
|
142
140
|
# If type wasn't provided, try guessing from file extension.
|
@@ -38,15 +38,12 @@ module ActionController # :nodoc:
|
|
38
38
|
define_method(type) do
|
39
39
|
request.flash[type]
|
40
40
|
end
|
41
|
+
private type
|
41
42
|
helper_method(type) if respond_to?(:helper_method)
|
42
43
|
|
43
44
|
self._flash_types += [type]
|
44
45
|
end
|
45
46
|
end
|
46
|
-
|
47
|
-
def action_methods # :nodoc:
|
48
|
-
@action_methods ||= super - _flash_types.map(&:to_s).to_set
|
49
|
-
end
|
50
47
|
end
|
51
48
|
|
52
49
|
private
|
@@ -25,6 +25,8 @@ module ActionController
|
|
25
25
|
raise ArgumentError, "#{status.inspect} is not a valid value for `status`."
|
26
26
|
end
|
27
27
|
|
28
|
+
raise ::AbstractController::DoubleRenderError if response_body
|
29
|
+
|
28
30
|
status ||= :ok
|
29
31
|
|
30
32
|
if options
|
@@ -41,7 +43,7 @@ module ActionController
|
|
41
43
|
|
42
44
|
if include_content?(response_code)
|
43
45
|
unless self.media_type
|
44
|
-
self.content_type = content_type || ((f = formats) && Mime[f.first]) ||
|
46
|
+
self.content_type = content_type || ((f = formats) && Mime[f.first]) || :html
|
45
47
|
end
|
46
48
|
|
47
49
|
response.charset = false
|
@@ -171,12 +171,6 @@ module ActionController
|
|
171
171
|
@ignore_disconnect = false
|
172
172
|
end
|
173
173
|
|
174
|
-
# ActionDispatch::Response delegates #to_ary to the internal
|
175
|
-
# ActionDispatch::Response::Buffer, defining #to_ary is an indicator that the
|
176
|
-
# response body can be buffered and/or cached by Rack middlewares, this is not
|
177
|
-
# the case for Live responses so we undefine it for this Buffer subclass.
|
178
|
-
undef_method :to_ary
|
179
|
-
|
180
174
|
def write(string)
|
181
175
|
unless @response.committed?
|
182
176
|
@response.headers["Cache-Control"] ||= "no-cache"
|
@@ -24,8 +24,17 @@ module ActionController # :nodoc:
|
|
24
24
|
# end
|
25
25
|
# end
|
26
26
|
#
|
27
|
+
# Requires a global policy defined in an initializer, which can be
|
28
|
+
# empty:
|
29
|
+
#
|
30
|
+
# Rails.application.config.permissions_policy do |policy|
|
31
|
+
# # policy.gyroscope :none
|
32
|
+
# end
|
27
33
|
def permissions_policy(**options, &block)
|
28
34
|
before_action(options) do
|
35
|
+
unless request.respond_to?(:permissions_policy)
|
36
|
+
raise "Cannot override permissions_policy if no global permissions_policy configured."
|
37
|
+
end
|
29
38
|
if block_given?
|
30
39
|
policy = request.permissions_policy.clone
|
31
40
|
instance_exec(policy, &block)
|
@@ -18,8 +18,13 @@ module ActionController # :nodoc:
|
|
18
18
|
# parameter. It's evaluated within the context of the controller processing the
|
19
19
|
# request.
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
21
|
+
# By default, rate limits are scoped to the controller's path. If you want to
|
22
|
+
# share rate limits across multiple controllers, you can provide your own scope,
|
23
|
+
# by passing value in the `scope:` parameter.
|
24
|
+
#
|
25
|
+
# Requests that exceed the rate limit will raise an `ActionController::TooManyRequests`
|
26
|
+
# error. By default, Action Dispatch will rescue from the error and refuse the request
|
27
|
+
# with a `429 Too Many Requests` response. You can specialize this by passing a callable in the `with:`
|
23
28
|
# parameter. It's evaluated within the context of the controller processing the
|
24
29
|
# request.
|
25
30
|
#
|
@@ -46,23 +51,33 @@ module ActionController # :nodoc:
|
|
46
51
|
# class APIController < ApplicationController
|
47
52
|
# RATE_LIMIT_STORE = ActiveSupport::Cache::RedisCacheStore.new(url: ENV["REDIS_URL"])
|
48
53
|
# rate_limit to: 10, within: 3.minutes, store: RATE_LIMIT_STORE
|
54
|
+
# rate_limit to: 100, within: 5.minutes, scope: :api_global
|
49
55
|
# end
|
50
56
|
#
|
51
57
|
# class SessionsController < ApplicationController
|
52
58
|
# rate_limit to: 3, within: 2.seconds, name: "short-term"
|
53
59
|
# rate_limit to: 10, within: 5.minutes, name: "long-term"
|
54
60
|
# end
|
55
|
-
def rate_limit(to:, within:, by: -> { request.remote_ip }, with: -> {
|
56
|
-
before_action -> { rate_limiting(to: to, within: within, by: by, with: with, store: store, name: name) }, **options
|
61
|
+
def rate_limit(to:, within:, by: -> { request.remote_ip }, with: -> { raise TooManyRequests }, store: cache_store, name: nil, scope: nil, **options)
|
62
|
+
before_action -> { rate_limiting(to: to, within: within, by: by, with: with, store: store, name: name, scope: scope || controller_path) }, **options
|
57
63
|
end
|
58
64
|
end
|
59
65
|
|
60
66
|
private
|
61
|
-
def rate_limiting(to:, within:, by:, with:, store:, name:)
|
62
|
-
|
67
|
+
def rate_limiting(to:, within:, by:, with:, store:, name:, scope:)
|
68
|
+
by = instance_exec(&by)
|
69
|
+
cache_key = ["rate-limit", scope, name, by].compact.join(":")
|
63
70
|
count = store.increment(cache_key, 1, expires_in: within)
|
64
71
|
if count && count > to
|
65
|
-
ActiveSupport::Notifications.instrument("rate_limit.action_controller",
|
72
|
+
ActiveSupport::Notifications.instrument("rate_limit.action_controller",
|
73
|
+
request: request,
|
74
|
+
count: count,
|
75
|
+
to: to,
|
76
|
+
within: within,
|
77
|
+
by: by,
|
78
|
+
name: name,
|
79
|
+
scope: scope,
|
80
|
+
cache_key: cache_key) do
|
66
81
|
instance_exec(&with)
|
67
82
|
end
|
68
83
|
end
|
@@ -15,6 +15,19 @@ module ActionController
|
|
15
15
|
|
16
16
|
included do
|
17
17
|
mattr_accessor :raise_on_open_redirects, default: false
|
18
|
+
mattr_accessor :action_on_path_relative_redirect, default: :log
|
19
|
+
class_attribute :_allowed_redirect_hosts, :allowed_redirect_hosts_permissions, instance_accessor: false, instance_predicate: false
|
20
|
+
singleton_class.alias_method :allowed_redirect_hosts, :_allowed_redirect_hosts
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods # :nodoc:
|
24
|
+
def allowed_redirect_hosts=(hosts)
|
25
|
+
hosts = hosts.dup.freeze
|
26
|
+
self._allowed_redirect_hosts = hosts
|
27
|
+
self.allowed_redirect_hosts_permissions = if hosts.present?
|
28
|
+
ActionDispatch::HostAuthorization::Permissions.new(hosts)
|
29
|
+
end
|
30
|
+
end
|
18
31
|
end
|
19
32
|
|
20
33
|
# Redirects the browser to the target specified in `options`. This parameter can
|
@@ -100,6 +113,26 @@ module ActionController
|
|
100
113
|
#
|
101
114
|
# See #url_from for more information on what an internal and safe URL is, or how
|
102
115
|
# to fall back to an alternate redirect URL in the unsafe case.
|
116
|
+
#
|
117
|
+
# ### Path Relative URL Redirect Protection
|
118
|
+
#
|
119
|
+
# Rails also protects against potentially unsafe path relative URL redirects that don't
|
120
|
+
# start with a leading slash. These can create security vulnerabilities:
|
121
|
+
#
|
122
|
+
# redirect_to "example.com" # Creates http://yourdomain.comexample.com
|
123
|
+
# redirect_to "@attacker.com" # Creates http://yourdomain.com@attacker.com
|
124
|
+
# # which browsers interpret as user@host
|
125
|
+
#
|
126
|
+
# You can configure how Rails handles these cases using:
|
127
|
+
#
|
128
|
+
# config.action_controller.action_on_path_relative_redirect = :log # default
|
129
|
+
# config.action_controller.action_on_path_relative_redirect = :notify
|
130
|
+
# config.action_controller.action_on_path_relative_redirect = :raise
|
131
|
+
#
|
132
|
+
# * `:log` - Logs a warning but allows the redirect
|
133
|
+
# * `:notify` - Sends an ActiveSupport notification but allows the redirect
|
134
|
+
# (includes stack trace to help identify the source)
|
135
|
+
# * `:raise` - Raises an UnsafeRedirectError
|
103
136
|
def redirect_to(options = {}, response_options = {})
|
104
137
|
raise ActionControllerError.new("Cannot redirect to nil!") unless options
|
105
138
|
raise AbstractController::DoubleRenderError if response_body
|
@@ -166,6 +199,10 @@ module ActionController
|
|
166
199
|
when /\A([a-z][a-z\d\-+.]*:|\/\/).*/i
|
167
200
|
options.to_str
|
168
201
|
when String
|
202
|
+
if !options.start_with?("/", "?") && !options.empty?
|
203
|
+
_handle_path_relative_redirect(options)
|
204
|
+
end
|
205
|
+
|
169
206
|
request.protocol + request.host_with_port + options
|
170
207
|
when Proc
|
171
208
|
_compute_redirect_to_location request, instance_eval(&options)
|
@@ -212,9 +249,9 @@ module ActionController
|
|
212
249
|
|
213
250
|
def _extract_redirect_to_status(options, response_options)
|
214
251
|
if options.is_a?(Hash) && options.key?(:status)
|
215
|
-
|
252
|
+
ActionDispatch::Response.rack_status_code(options.delete(:status))
|
216
253
|
elsif response_options.key?(:status)
|
217
|
-
|
254
|
+
ActionDispatch::Response.rack_status_code(response_options[:status])
|
218
255
|
else
|
219
256
|
302
|
220
257
|
end
|
@@ -229,12 +266,14 @@ module ActionController
|
|
229
266
|
end
|
230
267
|
|
231
268
|
def _url_host_allowed?(url)
|
232
|
-
|
269
|
+
url_to_s = url.to_s
|
270
|
+
host = URI(url_to_s).host
|
233
271
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
272
|
+
if host.nil?
|
273
|
+
url_to_s.start_with?("/") && !url_to_s.start_with?("//")
|
274
|
+
else
|
275
|
+
host == request.host || self.class.allowed_redirect_hosts_permissions&.allows?(host)
|
276
|
+
end
|
238
277
|
rescue ArgumentError, URI::Error
|
239
278
|
false
|
240
279
|
end
|
@@ -248,5 +287,22 @@ module ActionController
|
|
248
287
|
raise UnsafeRedirectError, msg
|
249
288
|
end
|
250
289
|
end
|
290
|
+
|
291
|
+
def _handle_path_relative_redirect(url)
|
292
|
+
message = "Path relative URL redirect detected: #{url.inspect}"
|
293
|
+
|
294
|
+
case action_on_path_relative_redirect
|
295
|
+
when :log
|
296
|
+
logger&.warn message
|
297
|
+
when :notify
|
298
|
+
ActiveSupport::Notifications.instrument("unsafe_redirect.action_controller",
|
299
|
+
url: url,
|
300
|
+
message: message,
|
301
|
+
stack_trace: caller
|
302
|
+
)
|
303
|
+
when :raise
|
304
|
+
raise UnsafeRedirectError, message
|
305
|
+
end
|
306
|
+
end
|
251
307
|
end
|
252
308
|
end
|
@@ -27,8 +27,23 @@ module ActionController
|
|
27
27
|
# Default values are `:json`, `:js`, `:xml`.
|
28
28
|
RENDERERS = Set.new
|
29
29
|
|
30
|
+
module DeprecatedEscapeJsonResponses # :nodoc:
|
31
|
+
def escape_json_responses=(value)
|
32
|
+
if value
|
33
|
+
ActionController.deprecator.warn(<<~MSG.squish)
|
34
|
+
Setting action_controller.escape_json_responses = true is deprecated and will have no effect in Rails 8.2.
|
35
|
+
Set it to `false`, or remove the config.
|
36
|
+
MSG
|
37
|
+
end
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
30
42
|
included do
|
31
43
|
class_attribute :_renderers, default: Set.new.freeze
|
44
|
+
class_attribute :escape_json_responses, instance_writer: false, instance_accessor: false, default: true
|
45
|
+
|
46
|
+
singleton_class.prepend DeprecatedEscapeJsonResponses
|
32
47
|
end
|
33
48
|
|
34
49
|
# Used in ActionController::Base and ActionController::API to include all
|
@@ -86,7 +101,7 @@ module ActionController
|
|
86
101
|
remove_possible_method(method_name)
|
87
102
|
end
|
88
103
|
|
89
|
-
def self._render_with_renderer_method_name(key)
|
104
|
+
def self._render_with_renderer_method_name(key) # :nodoc:
|
90
105
|
"_render_with_renderer_#{key}"
|
91
106
|
end
|
92
107
|
|
@@ -140,7 +155,7 @@ module ActionController
|
|
140
155
|
_render_to_body_with_renderer(options) || super
|
141
156
|
end
|
142
157
|
|
143
|
-
def _render_to_body_with_renderer(options)
|
158
|
+
def _render_to_body_with_renderer(options) # :nodoc:
|
144
159
|
_renderers.each do |name|
|
145
160
|
if options.key?(name)
|
146
161
|
_process_options(options)
|
@@ -153,28 +168,34 @@ module ActionController
|
|
153
168
|
|
154
169
|
add :json do |json, options|
|
155
170
|
json_options = options.except(:callback, :content_type, :status)
|
171
|
+
json_options[:escape] ||= false if !self.class.escape_json_responses? && options[:callback].blank?
|
156
172
|
json = json.to_json(json_options) unless json.kind_of?(String)
|
157
173
|
|
158
174
|
if options[:callback].present?
|
159
175
|
if media_type.nil? || media_type == Mime[:json]
|
160
|
-
self.content_type =
|
176
|
+
self.content_type = :js
|
161
177
|
end
|
162
178
|
|
163
179
|
"/**/#{options[:callback]}(#{json})"
|
164
180
|
else
|
165
|
-
self.content_type =
|
181
|
+
self.content_type = :json if media_type.nil?
|
166
182
|
json
|
167
183
|
end
|
168
184
|
end
|
169
185
|
|
170
186
|
add :js do |js, options|
|
171
|
-
self.content_type =
|
187
|
+
self.content_type = :js if media_type.nil?
|
172
188
|
js.respond_to?(:to_js) ? js.to_js(options) : js
|
173
189
|
end
|
174
190
|
|
175
191
|
add :xml do |xml, options|
|
176
|
-
self.content_type =
|
192
|
+
self.content_type = :xml if media_type.nil?
|
177
193
|
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
|
178
194
|
end
|
195
|
+
|
196
|
+
add :markdown do |md, options|
|
197
|
+
self.content_type = :md if media_type.nil?
|
198
|
+
md.respond_to?(:to_markdown) ? md.to_markdown : md
|
199
|
+
end
|
179
200
|
end
|
180
201
|
end
|
@@ -160,6 +160,12 @@ module ActionController
|
|
160
160
|
# render "posts/new", status: :unprocessable_entity
|
161
161
|
# # => renders app/views/posts/new.html.erb with HTTP status code 422
|
162
162
|
#
|
163
|
+
# `:variants`
|
164
|
+
# : This tells Rails to look for the first template matching any of the variations.
|
165
|
+
#
|
166
|
+
# render "posts/index", variants: [:mobile]
|
167
|
+
# # => renders app/views/posts/index.html+mobile.erb
|
168
|
+
#
|
163
169
|
#--
|
164
170
|
# Check for double render errors and set the content_type after rendering.
|
165
171
|
def render(*args)
|
@@ -208,7 +214,7 @@ module ActionController
|
|
208
214
|
end
|
209
215
|
|
210
216
|
def _set_html_content_type
|
211
|
-
self.content_type =
|
217
|
+
self.content_type = :html
|
212
218
|
end
|
213
219
|
|
214
220
|
def _set_rendered_content_type(format)
|
@@ -232,7 +238,7 @@ module ActionController
|
|
232
238
|
end
|
233
239
|
|
234
240
|
if options[:status]
|
235
|
-
options[:status] =
|
241
|
+
options[:status] = ActionDispatch::Response.rack_status_code(options[:status])
|
236
242
|
end
|
237
243
|
|
238
244
|
super
|
@@ -71,32 +71,39 @@ module ActionController # :nodoc:
|
|
71
71
|
included do
|
72
72
|
# Sets the token parameter name for RequestForgery. Calling
|
73
73
|
# `protect_from_forgery` sets it to `:authenticity_token` by default.
|
74
|
-
|
74
|
+
singleton_class.delegate :request_forgery_protection_token, :request_forgery_protection_token=, to: :config
|
75
|
+
delegate :request_forgery_protection_token, :request_forgery_protection_token=, to: :config
|
75
76
|
self.request_forgery_protection_token ||= :authenticity_token
|
76
77
|
|
77
78
|
# Holds the class which implements the request forgery protection.
|
78
|
-
|
79
|
+
singleton_class.delegate :forgery_protection_strategy, :forgery_protection_strategy=, to: :config
|
80
|
+
delegate :forgery_protection_strategy, :forgery_protection_strategy=, to: :config
|
79
81
|
self.forgery_protection_strategy = nil
|
80
82
|
|
81
83
|
# Controls whether request forgery protection is turned on or not. Turned off by
|
82
84
|
# default only in test mode.
|
83
|
-
|
85
|
+
singleton_class.delegate :allow_forgery_protection, :allow_forgery_protection=, to: :config
|
86
|
+
delegate :allow_forgery_protection, :allow_forgery_protection=, to: :config
|
84
87
|
self.allow_forgery_protection = true if allow_forgery_protection.nil?
|
85
88
|
|
86
89
|
# Controls whether a CSRF failure logs a warning. On by default.
|
87
|
-
|
90
|
+
singleton_class.delegate :log_warning_on_csrf_failure, :log_warning_on_csrf_failure=, to: :config
|
91
|
+
delegate :log_warning_on_csrf_failure, :log_warning_on_csrf_failure=, to: :config
|
88
92
|
self.log_warning_on_csrf_failure = true
|
89
93
|
|
90
94
|
# Controls whether the Origin header is checked in addition to the CSRF token.
|
91
|
-
|
95
|
+
singleton_class.delegate :forgery_protection_origin_check, :forgery_protection_origin_check=, to: :config
|
96
|
+
delegate :forgery_protection_origin_check, :forgery_protection_origin_check=, to: :config
|
92
97
|
self.forgery_protection_origin_check = false
|
93
98
|
|
94
99
|
# Controls whether form-action/method specific CSRF tokens are used.
|
95
|
-
|
100
|
+
singleton_class.delegate :per_form_csrf_tokens, :per_form_csrf_tokens=, to: :config
|
101
|
+
delegate :per_form_csrf_tokens, :per_form_csrf_tokens=, to: :config
|
96
102
|
self.per_form_csrf_tokens = false
|
97
103
|
|
98
104
|
# The strategy to use for storing and retrieving CSRF tokens.
|
99
|
-
|
105
|
+
singleton_class.delegate :csrf_token_storage_strategy, :csrf_token_storage_strategy=, to: :config
|
106
|
+
delegate :csrf_token_storage_strategy, :csrf_token_storage_strategy=, to: :config
|
100
107
|
self.csrf_token_storage_strategy = SessionStore.new
|
101
108
|
|
102
109
|
helper_method :form_authenticity_token
|
@@ -461,7 +468,7 @@ module ActionController # :nodoc:
|
|
461
468
|
# * Does the `X-CSRF-Token` header match the form_authenticity_token?
|
462
469
|
#
|
463
470
|
def verified_request? # :doc:
|
464
|
-
|
471
|
+
request.get? || request.head? || !protect_against_forgery? ||
|
465
472
|
(valid_request_origin? && any_authenticity_token_valid?)
|
466
473
|
end
|
467
474
|
|
@@ -621,6 +628,7 @@ module ActionController # :nodoc:
|
|
621
628
|
If you cannot change the referrer policy, you can disable origin checking with the
|
622
629
|
Rails.application.config.action_controller.forgery_protection_origin_check setting.
|
623
630
|
MSG
|
631
|
+
private_constant :NULL_ORIGIN_MESSAGE
|
624
632
|
|
625
633
|
# Checks if the request originated from the same origin by looking at the Origin
|
626
634
|
# header.
|
@@ -634,7 +642,7 @@ module ActionController # :nodoc:
|
|
634
642
|
end
|
635
643
|
end
|
636
644
|
|
637
|
-
def normalize_action_path(action_path)
|
645
|
+
def normalize_action_path(action_path)
|
638
646
|
uri = URI.parse(action_path)
|
639
647
|
|
640
648
|
if uri.relative? && (action_path.blank? || !action_path.start_with?("/"))
|
@@ -644,7 +652,7 @@ module ActionController # :nodoc:
|
|
644
652
|
end
|
645
653
|
end
|
646
654
|
|
647
|
-
def normalize_relative_action_path(rel_action_path)
|
655
|
+
def normalize_relative_action_path(rel_action_path)
|
648
656
|
uri = URI.parse(request.path)
|
649
657
|
# add the action path to the request.path
|
650
658
|
uri.path += "/#{rel_action_path}"
|
@@ -13,6 +13,15 @@ module ActionController # :nodoc:
|
|
13
13
|
extend ActiveSupport::Concern
|
14
14
|
include ActiveSupport::Rescuable
|
15
15
|
|
16
|
+
module ClassMethods
|
17
|
+
def handler_for_rescue(exception, ...) # :nodoc:
|
18
|
+
if handler = super
|
19
|
+
ActiveSupport::Notifications.instrument("rescue_from_callback.action_controller", exception: exception)
|
20
|
+
handler
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
16
25
|
# Override this method if you want to customize when detailed exceptions must be
|
17
26
|
# shown. This method is only called when `consider_all_requests_local` is
|
18
27
|
# `false`. By default, it returns `false`, but someone may set it to
|
@@ -13,8 +13,10 @@ module ActionController
|
|
13
13
|
class Railtie < Rails::Railtie # :nodoc:
|
14
14
|
config.action_controller = ActiveSupport::OrderedOptions.new
|
15
15
|
config.action_controller.raise_on_open_redirects = false
|
16
|
+
config.action_controller.action_on_path_relative_redirect = :log
|
16
17
|
config.action_controller.log_query_tags_around_actions = true
|
17
18
|
config.action_controller.wrap_parameters_by_default = false
|
19
|
+
config.action_controller.allowed_redirect_hosts = []
|
18
20
|
|
19
21
|
config.eager_load_namespaces << AbstractController
|
20
22
|
config.eager_load_namespaces << ActionController
|
@@ -93,12 +95,6 @@ module ActionController
|
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
96
|
-
initializer "action_controller.compile_config_methods" do
|
97
|
-
ActiveSupport.on_load(:action_controller) do
|
98
|
-
config.compile_methods! if config.respond_to?(:compile_methods!)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
98
|
initializer "action_controller.request_forgery_protection" do |app|
|
103
99
|
ActiveSupport.on_load(:action_controller_base) do
|
104
100
|
if app.config.action_controller.default_protect_from_forgery
|
@@ -96,7 +96,6 @@ module ActionController
|
|
96
96
|
# * `:script_name` - The portion of the incoming request's URL path that
|
97
97
|
# corresponds to the application. Converts to Rack's `SCRIPT_NAME`.
|
98
98
|
# * `:input` - The input stream. Converts to Rack's `rack.input`.
|
99
|
-
#
|
100
99
|
# * `defaults` - Default values for the Rack env. Entries are specified in the
|
101
100
|
# same format as `env`. `env` will be merged on top of these values.
|
102
101
|
# `defaults` will be retained when calling #new on a renderer instance.
|
@@ -30,5 +30,11 @@ module ActionDispatch
|
|
30
30
|
SERVER_TIMING = "server-timing"
|
31
31
|
STRICT_TRANSPORT_SECURITY = "strict-transport-security"
|
32
32
|
end
|
33
|
+
|
34
|
+
if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3.1")
|
35
|
+
UNPROCESSABLE_CONTENT = :unprocessable_entity
|
36
|
+
else
|
37
|
+
UNPROCESSABLE_CONTENT = :unprocessable_content
|
38
|
+
end
|
33
39
|
end
|
34
40
|
end
|