actionpack 6.0.6.1 → 6.1.7.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +416 -255
- data/MIT-LICENSE +1 -2
- data/lib/abstract_controller/base.rb +35 -2
- data/lib/abstract_controller/callbacks.rb +2 -2
- data/lib/abstract_controller/collector.rb +4 -2
- data/lib/abstract_controller/helpers.rb +105 -90
- data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +8 -2
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +2 -2
- data/lib/action_controller/base.rb +4 -2
- data/lib/action_controller/caching.rb +0 -1
- data/lib/action_controller/log_subscriber.rb +3 -3
- data/lib/action_controller/metal/conditional_get.rb +11 -3
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +1 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +3 -5
- data/lib/action_controller/metal/exceptions.rb +33 -0
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +11 -1
- data/lib/action_controller/metal/http_authentication.rb +5 -2
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +11 -9
- data/lib/action_controller/metal/live.rb +10 -1
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +6 -2
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +14 -8
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +21 -2
- data/lib/action_controller/metal/rendering.rb +6 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +104 -16
- data/lib/action_controller/metal.rb +2 -2
- data/lib/action_controller/renderer.rb +23 -13
- data/lib/action_controller/test_case.rb +65 -56
- data/lib/action_controller.rb +2 -3
- data/lib/action_dispatch/http/cache.rb +18 -17
- data/lib/action_dispatch/http/content_security_policy.rb +6 -1
- data/lib/action_dispatch/http/filter_parameters.rb +1 -1
- data/lib/action_dispatch/http/filter_redirect.rb +1 -1
- data/lib/action_dispatch/http/headers.rb +3 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +14 -8
- data/lib/action_dispatch/http/mime_type.rb +29 -16
- data/lib/action_dispatch/http/parameters.rb +1 -19
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +24 -8
- data/lib/action_dispatch/http/response.rb +17 -16
- data/lib/action_dispatch/http/url.rb +3 -2
- data/lib/action_dispatch/journey/formatter.rb +55 -30
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +4 -3
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +13 -18
- data/lib/action_dispatch/journey/route.rb +7 -18
- data/lib/action_dispatch/journey/router/utils.rb +6 -4
- data/lib/action_dispatch/journey/router.rb +26 -30
- data/lib/action_dispatch/journey/visitors.rb +1 -1
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +89 -46
- data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -15
- data/lib/action_dispatch/middleware/debug_view.rb +1 -1
- data/lib/action_dispatch/middleware/exception_wrapper.rb +28 -16
- data/lib/action_dispatch/middleware/host_authorization.rb +63 -14
- data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
- data/lib/action_dispatch/middleware/request_id.rb +4 -5
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
- data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +12 -0
- data/lib/action_dispatch/middleware/ssl.rb +12 -7
- data/lib/action_dispatch/middleware/stack.rb +19 -1
- data/lib/action_dispatch/middleware/static.rb +154 -93
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +100 -8
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +21 -1
- data/lib/action_dispatch/railtie.rb +3 -2
- data/lib/action_dispatch/request/session.rb +2 -8
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +8 -7
- data/lib/action_dispatch/routing/mapper.rb +102 -71
- data/lib/action_dispatch/routing/polymorphic_routes.rb +12 -11
- data/lib/action_dispatch/routing/redirection.rb +4 -4
- data/lib/action_dispatch/routing/route_set.rb +49 -41
- data/lib/action_dispatch/system_test_case.rb +35 -24
- data/lib/action_dispatch/system_testing/browser.rb +33 -27
- data/lib/action_dispatch/system_testing/driver.rb +6 -7
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
- data/lib/action_dispatch/testing/assertions/response.rb +2 -4
- data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +40 -29
- data/lib/action_dispatch/testing/test_process.rb +32 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch.rb +3 -2
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +18 -19
- data/lib/action_controller/metal/force_ssl.rb +0 -58
- data/lib/action_dispatch/http/parameter_filter.rb +0 -12
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
|
|
53
53
|
#
|
54
54
|
# Show a 404 page in the browser:
|
55
55
|
#
|
56
|
-
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
|
56
|
+
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404
|
57
57
|
#
|
58
58
|
# Read about the other Content-* HTTP headers if you'd like to
|
59
59
|
# provide the user with more information (such as Content-Description) in
|
@@ -26,10 +26,8 @@ module ActionController
|
|
26
26
|
included do
|
27
27
|
class_attribute :etag_with_template_digest, default: true
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
determine_template_etag(options) if etag_with_template_digest
|
32
|
-
end
|
29
|
+
etag do |options|
|
30
|
+
determine_template_etag(options) if etag_with_template_digest
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
@@ -46,7 +44,7 @@ module ActionController
|
|
46
44
|
# template digest from the ETag.
|
47
45
|
def pick_template_for_etag(options)
|
48
46
|
unless options[:template] == false
|
49
|
-
options[:template] ||
|
47
|
+
options[:template] || lookup_context.find_all(action_name, _prefixes).first&.virtual_path
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
@@ -23,6 +23,39 @@ module ActionController
|
|
23
23
|
end
|
24
24
|
|
25
25
|
class UrlGenerationError < ActionControllerError #:nodoc:
|
26
|
+
attr_reader :routes, :route_name, :method_name
|
27
|
+
|
28
|
+
def initialize(message, routes = nil, route_name = nil, method_name = nil)
|
29
|
+
@routes = routes
|
30
|
+
@route_name = route_name
|
31
|
+
@method_name = method_name
|
32
|
+
|
33
|
+
super(message)
|
34
|
+
end
|
35
|
+
|
36
|
+
class Correction
|
37
|
+
def initialize(error)
|
38
|
+
@error = error
|
39
|
+
end
|
40
|
+
|
41
|
+
def corrections
|
42
|
+
if @error.method_name
|
43
|
+
maybe_these = @error.routes.named_routes.helper_names.grep(/#{@error.route_name}/)
|
44
|
+
maybe_these -= [@error.method_name.to_s] # remove exact match
|
45
|
+
|
46
|
+
maybe_these.sort_by { |n|
|
47
|
+
DidYouMean::Jaro.distance(@error.route_name, n)
|
48
|
+
}.reverse.first(4)
|
49
|
+
else
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
56
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
57
|
+
DidYouMean.correct_error(self, Correction)
|
58
|
+
end
|
26
59
|
end
|
27
60
|
|
28
61
|
class MethodNotAllowed < ActionControllerError #:nodoc:
|
@@ -29,19 +29,22 @@ module ActionController
|
|
29
29
|
content_type = options.delete(:content_type)
|
30
30
|
|
31
31
|
options.each do |key, value|
|
32
|
-
headers[key.to_s.
|
32
|
+
headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
|
33
33
|
end
|
34
34
|
|
35
35
|
self.status = status
|
36
36
|
self.location = url_for(location) if location
|
37
37
|
|
38
|
-
self.response_body = ""
|
39
|
-
|
40
38
|
if include_content?(response_code)
|
41
|
-
self.
|
39
|
+
unless self.media_type
|
40
|
+
self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
|
41
|
+
end
|
42
|
+
|
42
43
|
response.charset = false
|
43
44
|
end
|
44
45
|
|
46
|
+
self.response_body = ""
|
47
|
+
|
45
48
|
true
|
46
49
|
end
|
47
50
|
|
@@ -11,7 +11,12 @@ module ActionController
|
|
11
11
|
#
|
12
12
|
# In previous versions of \Rails the controller will include a helper which
|
13
13
|
# matches the name of the controller, e.g., <tt>MyController</tt> will automatically
|
14
|
-
# include <tt>MyHelper</tt>.
|
14
|
+
# include <tt>MyHelper</tt>. You can revert to the old behavior with the following:
|
15
|
+
#
|
16
|
+
# # config/application.rb
|
17
|
+
# class Application < Rails::Application
|
18
|
+
# config.action_controller.include_all_helpers = false
|
19
|
+
# end
|
15
20
|
#
|
16
21
|
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
|
17
22
|
# controller which inherits from it.
|
@@ -73,6 +78,11 @@ module ActionController
|
|
73
78
|
end
|
74
79
|
|
75
80
|
# Provides a proxy to access helper methods from outside the view.
|
81
|
+
#
|
82
|
+
# Note that the proxy is rendered under a different view context.
|
83
|
+
# This may cause incorrect behaviour with capture methods. Consider
|
84
|
+
# using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper]
|
85
|
+
# instead when using +capture+.
|
76
86
|
def helpers
|
77
87
|
@helper_proxy ||= begin
|
78
88
|
proxy = ActionView::Base.empty
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "base64"
|
4
4
|
require "active_support/security_utils"
|
5
|
+
require "active_support/core_ext/array/access"
|
5
6
|
|
6
7
|
module ActionController
|
7
8
|
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
|
@@ -76,6 +77,8 @@ module ActionController
|
|
76
77
|
|
77
78
|
def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
|
78
79
|
authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
|
80
|
+
# This comparison uses & so that it doesn't short circuit and
|
81
|
+
# uses `secure_compare` so that length information isn't leaked.
|
79
82
|
ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
|
80
83
|
ActiveSupport::SecurityUtils.secure_compare(given_password, password)
|
81
84
|
end
|
@@ -136,7 +139,7 @@ module ActionController
|
|
136
139
|
#
|
137
140
|
# === Simple \Digest example
|
138
141
|
#
|
139
|
-
# require
|
142
|
+
# require "digest/md5"
|
140
143
|
# class PostsController < ApplicationController
|
141
144
|
# REALM = "SuperSecret"
|
142
145
|
# USERS = {"dhh" => "secret", #plain text password
|
@@ -482,7 +485,7 @@ module ActionController
|
|
482
485
|
def raw_params(auth)
|
483
486
|
_raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
484
487
|
|
485
|
-
if !
|
488
|
+
if !_raw_params.first&.start_with?(TOKEN_KEY)
|
486
489
|
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
487
490
|
end
|
488
491
|
|
@@ -22,7 +22,7 @@ module ActionController
|
|
22
22
|
# Third, if we DON'T find a template AND the request is a page load in a web
|
23
23
|
# browser (technically, a non-XHR GET request for an HTML response) where
|
24
24
|
# you reasonably expect to have rendered a template, then we raise
|
25
|
-
# <tt>
|
25
|
+
# <tt>ActionController::MissingExactTemplate</tt> with an explanation.
|
26
26
|
#
|
27
27
|
# Finally, if we DON'T find a template AND the request isn't a browser page
|
28
28
|
# load, then we implicitly respond with <tt>204 No Content</tt>.
|
@@ -16,10 +16,11 @@ module ActionController
|
|
16
16
|
|
17
17
|
attr_internal :view_runtime
|
18
18
|
|
19
|
-
def process_action(*
|
19
|
+
def process_action(*)
|
20
20
|
raw_payload = {
|
21
21
|
controller: self.class.name,
|
22
22
|
action: action_name,
|
23
|
+
request: request,
|
23
24
|
params: request.filtered_parameters,
|
24
25
|
headers: request.headers,
|
25
26
|
format: request.format.ref,
|
@@ -27,18 +28,19 @@ module ActionController
|
|
27
28
|
path: request.fullpath
|
28
29
|
}
|
29
30
|
|
30
|
-
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload
|
31
|
+
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
|
31
32
|
|
32
33
|
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
33
|
-
super
|
34
|
-
|
35
|
-
|
34
|
+
result = super
|
35
|
+
payload[:response] = response
|
36
|
+
payload[:status] = response.status
|
37
|
+
result
|
36
38
|
ensure
|
37
39
|
append_info_to_payload(payload)
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
def render(*
|
43
|
+
def render(*)
|
42
44
|
render_output = nil
|
43
45
|
self.view_runtime = cleanup_view_runtime do
|
44
46
|
Benchmark.ms { render_output = super }
|
@@ -59,8 +61,8 @@ module ActionController
|
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
62
|
-
def redirect_to(*
|
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
|
@@ -70,7 +72,7 @@ module ActionController
|
|
70
72
|
|
71
73
|
private
|
72
74
|
# A hook invoked every time a before callback is halted.
|
73
|
-
def halted_callback_hook(filter)
|
75
|
+
def halted_callback_hook(filter, _)
|
74
76
|
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
75
77
|
end
|
76
78
|
|
@@ -127,6 +127,11 @@ module ActionController
|
|
127
127
|
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
|
128
128
|
include MonitorMixin
|
129
129
|
|
130
|
+
class << self
|
131
|
+
attr_accessor :queue_size
|
132
|
+
end
|
133
|
+
@queue_size = 10
|
134
|
+
|
130
135
|
# Ignore that the client has disconnected.
|
131
136
|
#
|
132
137
|
# If this value is `true`, calling `write` after the client
|
@@ -136,11 +141,11 @@ module ActionController
|
|
136
141
|
attr_accessor :ignore_disconnect
|
137
142
|
|
138
143
|
def initialize(response)
|
144
|
+
super(response, build_queue(self.class.queue_size))
|
139
145
|
@error_callback = lambda { true }
|
140
146
|
@cv = new_cond
|
141
147
|
@aborted = false
|
142
148
|
@ignore_disconnect = false
|
143
|
-
super(response, SizedQueue.new(10))
|
144
149
|
end
|
145
150
|
|
146
151
|
def write(string)
|
@@ -214,6 +219,10 @@ module ActionController
|
|
214
219
|
yield str
|
215
220
|
end
|
216
221
|
end
|
222
|
+
|
223
|
+
def build_queue(queue_size)
|
224
|
+
queue_size ? SizedQueue.new(queue_size) : Queue.new
|
225
|
+
end
|
217
226
|
end
|
218
227
|
|
219
228
|
class Response < ActionDispatch::Response #:nodoc: all
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
module Logging
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Set a different log level per request.
|
9
|
+
#
|
10
|
+
# # Use the debug log level if a particular cookie is set.
|
11
|
+
# class ApplicationController < ActionController::Base
|
12
|
+
# log_at :debug, if: -> { cookies[:debug] }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
def log_at(level, **options)
|
16
|
+
around_action ->(_, action) { logger.log_at(level, &action) }, **options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -142,7 +142,7 @@ module ActionController #:nodoc:
|
|
142
142
|
#
|
143
143
|
# You can set the variant in a +before_action+:
|
144
144
|
#
|
145
|
-
# request.variant = :tablet if request.user_agent
|
145
|
+
# request.variant = :tablet if /iPad/.match?(request.user_agent)
|
146
146
|
#
|
147
147
|
# Respond to variants in the action just like you respond to formats:
|
148
148
|
#
|
@@ -209,7 +209,7 @@ module ActionController #:nodoc:
|
|
209
209
|
raise ActionController::RespondToMismatchError
|
210
210
|
end
|
211
211
|
_process_format(format)
|
212
|
-
_set_rendered_content_type
|
212
|
+
_set_rendered_content_type(format) unless collector.any_response?
|
213
213
|
response = collector.response
|
214
214
|
response.call if response
|
215
215
|
else
|
@@ -268,6 +268,10 @@ module ActionController #:nodoc:
|
|
268
268
|
end
|
269
269
|
end
|
270
270
|
|
271
|
+
def any_response?
|
272
|
+
!@responses.fetch(format, false) && @responses[Mime::ALL]
|
273
|
+
end
|
274
|
+
|
271
275
|
def response
|
272
276
|
response = @responses.fetch(format, @responses[Mime::ALL])
|
273
277
|
if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
|
@@ -12,11 +12,13 @@ module ActionController
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def setup_param_encode # :nodoc:
|
15
|
-
@_parameter_encodings = {}
|
15
|
+
@_parameter_encodings = Hash.new { |h, k| h[k] = {} }
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
@_parameter_encodings
|
18
|
+
def action_encoding_template(action) # :nodoc:
|
19
|
+
if @_parameter_encodings.has_key?(action.to_s)
|
20
|
+
@_parameter_encodings[action.to_s]
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
24
|
# Specify that a given action's parameters should all be encoded as
|
@@ -44,7 +46,36 @@ module ActionController
|
|
44
46
|
# encoded as ASCII-8BIT. This is useful in the case where an application
|
45
47
|
# must handle data but encoding of the data is unknown, like file system data.
|
46
48
|
def skip_parameter_encoding(action)
|
47
|
-
@_parameter_encodings[action.to_s] =
|
49
|
+
@_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Specify the encoding for a parameter on an action.
|
53
|
+
# If not specified the default is UTF-8.
|
54
|
+
#
|
55
|
+
# You can specify a binary (ASCII_8BIT) parameter with:
|
56
|
+
#
|
57
|
+
# class RepositoryController < ActionController::Base
|
58
|
+
# # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT
|
59
|
+
# param_encoding :show, :file_path, Encoding::ASCII_8BIT
|
60
|
+
#
|
61
|
+
# def show
|
62
|
+
# @repo = Repository.find_by_filesystem_path params[:file_path]
|
63
|
+
#
|
64
|
+
# # params[:repo_name] remains UTF-8 encoded
|
65
|
+
# @repo_name = params[:repo_name]
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# def index
|
69
|
+
# @repositories = Repository.all
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# The file_path parameter on the show action would be encoded as ASCII-8BIT,
|
74
|
+
# but all other arguments will remain UTF-8 encoded.
|
75
|
+
# This is useful in the case where an application must handle data
|
76
|
+
# but encoding of the data is unknown, like file system data.
|
77
|
+
def param_encoding(action, param, encoding)
|
78
|
+
@_parameter_encodings[action.to_s][param.to_s] = encoding
|
48
79
|
end
|
49
80
|
end
|
50
81
|
end
|
@@ -107,10 +107,14 @@ module ActionController
|
|
107
107
|
|
108
108
|
unless super || exclude
|
109
109
|
if m.respond_to?(:attribute_names) && m.attribute_names.any?
|
110
|
+
self.include = m.attribute_names
|
111
|
+
|
110
112
|
if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
|
111
|
-
self.include
|
112
|
-
|
113
|
-
|
113
|
+
self.include += m.stored_attributes.values.flatten.map(&:to_s)
|
114
|
+
end
|
115
|
+
|
116
|
+
if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any?
|
117
|
+
self.include += m.attribute_aliases.keys
|
114
118
|
end
|
115
119
|
|
116
120
|
if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
|
@@ -151,7 +155,7 @@ module ActionController
|
|
151
155
|
# try to find Foo::Bar::User, Foo::User and finally User.
|
152
156
|
def _default_wrap_model
|
153
157
|
return nil if klass.anonymous?
|
154
|
-
model_name = klass.name.
|
158
|
+
model_name = klass.name.delete_suffix("Controller").classify
|
155
159
|
|
156
160
|
begin
|
157
161
|
if model_klass = model_name.safe_constantize
|
@@ -189,7 +193,7 @@ module ActionController
|
|
189
193
|
#
|
190
194
|
# wrap_parameters Person
|
191
195
|
# # wraps parameters by determining the wrapper key from Person class
|
192
|
-
# (+person+, in this case) and the list of attribute names
|
196
|
+
# # (+person+, in this case) and the list of attribute names
|
193
197
|
#
|
194
198
|
# wrap_parameters include: [:username, :title]
|
195
199
|
# # wraps only +:username+ and +:title+ attributes from parameters.
|
@@ -240,7 +244,7 @@ module ActionController
|
|
240
244
|
|
241
245
|
# Performs parameters wrapping upon the request. Called automatically
|
242
246
|
# by the metal call stack.
|
243
|
-
def process_action(*
|
247
|
+
def process_action(*)
|
244
248
|
_perform_parameter_wrapping if _wrapper_enabled?
|
245
249
|
super
|
246
250
|
end
|
@@ -264,9 +268,11 @@ module ActionController
|
|
264
268
|
def _extract_parameters(parameters)
|
265
269
|
if include_only = _wrapper_options.include
|
266
270
|
parameters.slice(*include_only)
|
271
|
+
elsif _wrapper_options.exclude
|
272
|
+
exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
|
273
|
+
parameters.except(*exclude)
|
267
274
|
else
|
268
|
-
|
269
|
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
|
275
|
+
parameters.except(*EXCLUDE_PARAMETERS)
|
270
276
|
end
|
271
277
|
end
|
272
278
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController #:nodoc:
|
4
|
+
# HTTP Permissions Policy is a web standard for defining a mechanism to
|
5
|
+
# allow and deny the use of browser permissions in its own context, and
|
6
|
+
# in content within any <iframe> elements in the document.
|
7
|
+
#
|
8
|
+
# Full details of HTTP Permissions Policy specification and guidelines can
|
9
|
+
# be found at MDN:
|
10
|
+
#
|
11
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
|
12
|
+
#
|
13
|
+
# Examples of usage:
|
14
|
+
#
|
15
|
+
# # Global policy
|
16
|
+
# Rails.application.config.permissions_policy do |f|
|
17
|
+
# f.camera :none
|
18
|
+
# f.gyroscope :none
|
19
|
+
# f.microphone :none
|
20
|
+
# f.usb :none
|
21
|
+
# f.fullscreen :self
|
22
|
+
# f.payment :self, "https://secure.example.com"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # Controller level policy
|
26
|
+
# class PagesController < ApplicationController
|
27
|
+
# permissions_policy do |p|
|
28
|
+
# p.geolocation "https://example.com"
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
module PermissionsPolicy
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
def permissions_policy(**options, &block)
|
36
|
+
before_action(options) do
|
37
|
+
if block_given?
|
38
|
+
policy = request.permissions_policy.clone
|
39
|
+
yield policy
|
40
|
+
request.permissions_policy = policy
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -7,6 +7,10 @@ module ActionController
|
|
7
7
|
include AbstractController::Logger
|
8
8
|
include ActionController::UrlFor
|
9
9
|
|
10
|
+
ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
|
11
|
+
|
12
|
+
class UnsafeRedirectError < StandardError; end
|
13
|
+
|
10
14
|
# Redirects the browser to the target specified in +options+. This parameter can be any one of:
|
11
15
|
#
|
12
16
|
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
|
@@ -60,7 +64,11 @@ module ActionController
|
|
60
64
|
raise AbstractController::DoubleRenderError if response_body
|
61
65
|
|
62
66
|
self.status = _extract_redirect_to_status(options, response_options)
|
63
|
-
|
67
|
+
|
68
|
+
redirect_to_location = _compute_redirect_to_location(request, options)
|
69
|
+
_ensure_url_is_http_header_safe(redirect_to_location)
|
70
|
+
|
71
|
+
self.location = redirect_to_location
|
64
72
|
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
|
65
73
|
end
|
66
74
|
|
@@ -85,7 +93,7 @@ module ActionController
|
|
85
93
|
# * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
|
86
94
|
# * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
|
87
95
|
#
|
88
|
-
# All other options that can be passed to
|
96
|
+
# All other options that can be passed to #redirect_to are accepted as
|
89
97
|
# options and the behavior is identical.
|
90
98
|
def redirect_back(fallback_location:, allow_other_host: true, **args)
|
91
99
|
referer = request.headers["Referer"]
|
@@ -129,5 +137,16 @@ module ActionController
|
|
129
137
|
rescue ArgumentError, URI::Error
|
130
138
|
false
|
131
139
|
end
|
140
|
+
|
141
|
+
def _ensure_url_is_http_header_safe(url)
|
142
|
+
# Attempt to comply with the set of valid token characters
|
143
|
+
# defined for an HTTP header value in
|
144
|
+
# https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
|
145
|
+
if url.match(ILLEGAL_HEADER_VALUE_REGEX)
|
146
|
+
msg = "The redirect URL #{url} contains one or more illegal HTTP header field character. " \
|
147
|
+
"Set of legal characters defined in https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6"
|
148
|
+
raise UnsafeRedirectError, msg
|
149
|
+
end
|
150
|
+
end
|
132
151
|
end
|
133
152
|
end
|
@@ -77,6 +77,12 @@ module ActionController
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
def _set_vary_header
|
81
|
+
if response.headers["Vary"].blank? && request.should_apply_vary_header?
|
82
|
+
response.headers["Vary"] = "Accept"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
80
86
|
# Normalize arguments by catching blocks and setting them on :update.
|
81
87
|
def _normalize_args(action = nil, options = {}, &blk)
|
82
88
|
options = super
|