actionpack 5.2.7.1 → 6.1.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +427 -338
- data/MIT-LICENSE +1 -2
- data/README.rdoc +4 -3
- data/lib/abstract_controller/base.rb +38 -4
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +14 -2
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/helpers.rb +106 -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 +11 -5
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +4 -3
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +10 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +19 -5
- data/lib/action_controller/metal/content_security_policy.rb +1 -2
- data/lib/action_controller/metal/cookies.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +6 -7
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +56 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +14 -5
- data/lib/action_controller/metal/http_authentication.rb +25 -23
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +13 -14
- data/lib/action_controller/metal/live.rb +39 -32
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +19 -4
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +32 -22
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +26 -7
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +8 -3
- data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +0 -1
- data/lib/action_controller/metal/strong_parameters.rb +168 -59
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +10 -8
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +71 -63
- data/lib/action_controller.rb +7 -4
- data/lib/action_dispatch/http/cache.rb +32 -28
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +34 -18
- data/lib/action_dispatch/http/filter_parameters.rb +9 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
- data/lib/action_dispatch/http/mime_type.rb +43 -24
- data/lib/action_dispatch/http/parameters.rb +14 -23
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +45 -22
- data/lib/action_dispatch/http/response.rb +45 -25
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +82 -82
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +13 -11
- 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 +19 -21
- data/lib/action_dispatch/journey/route.rb +10 -20
- data/lib/action_dispatch/journey/router/utils.rb +14 -12
- data/lib/action_dispatch/journey/router.rb +26 -34
- 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/journey.rb +0 -2
- 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 +150 -123
- data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
- 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 +75 -30
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +170 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
- data/lib/action_dispatch/middleware/show_exceptions.rb +13 -2
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +56 -2
- data/lib/action_dispatch/middleware/static.rb +153 -93
- 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/_message_and_suggestions.html.erb +22 -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 +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
- 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/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +11 -10
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +100 -52
- data/lib/action_dispatch/routing/mapper.rb +155 -103
- data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
- data/lib/action_dispatch/routing/redirection.rb +4 -4
- data/lib/action_dispatch/routing/route_set.rb +71 -69
- 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 +60 -11
- data/lib/action_dispatch/system_testing/browser.rb +53 -16
- data/lib/action_dispatch/system_testing/driver.rb +11 -3
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +60 -28
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +32 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +9 -3
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +36 -23
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -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,20 +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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
34
|
+
result = super
|
35
|
+
payload[:response] = response
|
36
|
+
payload[:status] = response.status
|
37
|
+
result
|
38
|
+
ensure
|
39
|
+
append_info_to_payload(payload)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
def render(*
|
43
|
+
def render(*)
|
44
44
|
render_output = nil
|
45
45
|
self.view_runtime = cleanup_view_runtime do
|
46
46
|
Benchmark.ms { render_output = super }
|
@@ -61,8 +61,8 @@ module ActionController
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
def redirect_to(*
|
65
|
-
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|
|
66
66
|
result = super
|
67
67
|
payload[:status] = response.status
|
68
68
|
payload[:location] = response.filtered_location
|
@@ -71,9 +71,8 @@ module ActionController
|
|
71
71
|
end
|
72
72
|
|
73
73
|
private
|
74
|
-
|
75
74
|
# A hook invoked every time a before callback is halted.
|
76
|
-
def halted_callback_hook(filter)
|
75
|
+
def halted_callback_hook(filter, _)
|
77
76
|
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
78
77
|
end
|
79
78
|
|
@@ -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
|
@@ -128,6 +127,11 @@ module ActionController
|
|
128
127
|
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
|
129
128
|
include MonitorMixin
|
130
129
|
|
130
|
+
class << self
|
131
|
+
attr_accessor :queue_size
|
132
|
+
end
|
133
|
+
@queue_size = 10
|
134
|
+
|
131
135
|
# Ignore that the client has disconnected.
|
132
136
|
#
|
133
137
|
# If this value is `true`, calling `write` after the client
|
@@ -137,16 +141,16 @@ module ActionController
|
|
137
141
|
attr_accessor :ignore_disconnect
|
138
142
|
|
139
143
|
def initialize(response)
|
144
|
+
super(response, build_queue(self.class.queue_size))
|
140
145
|
@error_callback = lambda { true }
|
141
146
|
@cv = new_cond
|
142
147
|
@aborted = false
|
143
148
|
@ignore_disconnect = false
|
144
|
-
super(response, SizedQueue.new(10))
|
145
149
|
end
|
146
150
|
|
147
151
|
def write(string)
|
148
152
|
unless @response.committed?
|
149
|
-
@response.
|
153
|
+
@response.headers["Cache-Control"] ||= "no-cache"
|
150
154
|
@response.delete_header "Content-Length"
|
151
155
|
end
|
152
156
|
|
@@ -205,7 +209,6 @@ module ActionController
|
|
205
209
|
end
|
206
210
|
|
207
211
|
private
|
208
|
-
|
209
212
|
def each_chunk(&block)
|
210
213
|
loop do
|
211
214
|
str = nil
|
@@ -216,11 +219,14 @@ module ActionController
|
|
216
219
|
yield str
|
217
220
|
end
|
218
221
|
end
|
222
|
+
|
223
|
+
def build_queue(queue_size)
|
224
|
+
queue_size ? SizedQueue.new(queue_size) : Queue.new
|
225
|
+
end
|
219
226
|
end
|
220
227
|
|
221
228
|
class Response < ActionDispatch::Response #:nodoc: all
|
222
229
|
private
|
223
|
-
|
224
230
|
def before_committed
|
225
231
|
super
|
226
232
|
jar = request.cookie_jar
|
@@ -280,33 +286,34 @@ module ActionController
|
|
280
286
|
raise error if error
|
281
287
|
end
|
282
288
|
|
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
289
|
def response_body=(body)
|
308
290
|
super
|
309
291
|
response.close if response
|
310
292
|
end
|
293
|
+
|
294
|
+
private
|
295
|
+
# Spawn a new thread to serve up the controller in. This is to get
|
296
|
+
# around the fact that Rack isn't based around IOs and we need to use
|
297
|
+
# a thread to stream data from the response bodies. Nobody should call
|
298
|
+
# this method except in Rails internals. Seriously!
|
299
|
+
def new_controller_thread # :nodoc:
|
300
|
+
Thread.new {
|
301
|
+
t2 = Thread.current
|
302
|
+
t2.abort_on_exception = true
|
303
|
+
yield
|
304
|
+
}
|
305
|
+
end
|
306
|
+
|
307
|
+
def log_error(exception)
|
308
|
+
logger = ActionController::Base.logger
|
309
|
+
return unless logger
|
310
|
+
|
311
|
+
logger.fatal do
|
312
|
+
message = +"\n#{exception.class} (#{exception.message}):\n"
|
313
|
+
message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
|
314
|
+
message << " " << exception.backtrace.join("\n ")
|
315
|
+
"#{message}\n\n"
|
316
|
+
end
|
317
|
+
end
|
311
318
|
end
|
312
319
|
end
|
@@ -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
|
@@ -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>,
|
@@ -134,7 +142,7 @@ module ActionController #:nodoc:
|
|
134
142
|
#
|
135
143
|
# You can set the variant in a +before_action+:
|
136
144
|
#
|
137
|
-
# request.variant = :tablet if request.user_agent
|
145
|
+
# request.variant = :tablet if /iPad/.match?(request.user_agent)
|
138
146
|
#
|
139
147
|
# Respond to variants in the action just like you respond to formats:
|
140
148
|
#
|
@@ -197,8 +205,11 @@ 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
|
-
_set_rendered_content_type
|
212
|
+
_set_rendered_content_type(format) unless collector.any_response?
|
202
213
|
response = collector.response
|
203
214
|
response.call if response
|
204
215
|
else
|
@@ -257,6 +268,10 @@ module ActionController #:nodoc:
|
|
257
268
|
end
|
258
269
|
end
|
259
270
|
|
271
|
+
def any_response?
|
272
|
+
!@responses.fetch(format, false) && @responses[Mime::ALL]
|
273
|
+
end
|
274
|
+
|
260
275
|
def response
|
261
276
|
response = @responses.fetch(format, @responses[Mime::ALL])
|
262
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,15 +107,19 @@ 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?
|
117
121
|
self.include += m.nested_attributes_options.keys.map do |key|
|
118
|
-
key.to_s.
|
122
|
+
(+key.to_s).concat("_attributes")
|
119
123
|
end
|
120
124
|
end
|
121
125
|
|
@@ -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,24 +244,12 @@ 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(*
|
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
|
247
|
+
def process_action(*)
|
248
|
+
_perform_parameter_wrapping if _wrapper_enabled?
|
256
249
|
super
|
257
250
|
end
|
258
251
|
|
259
252
|
private
|
260
|
-
|
261
253
|
# Returns the wrapper key which will be used to store wrapped parameters.
|
262
254
|
def _wrapper_key
|
263
255
|
_wrapper_options.name
|
@@ -276,9 +268,11 @@ module ActionController
|
|
276
268
|
def _extract_parameters(parameters)
|
277
269
|
if include_only = _wrapper_options.include
|
278
270
|
parameters.slice(*include_only)
|
271
|
+
elsif _wrapper_options.exclude
|
272
|
+
exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
|
273
|
+
parameters.except(*exclude)
|
279
274
|
else
|
280
|
-
|
281
|
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
|
275
|
+
parameters.except(*EXCLUDE_PARAMETERS)
|
282
276
|
end
|
283
277
|
end
|
284
278
|
|
@@ -287,7 +281,23 @@ module ActionController
|
|
287
281
|
return false unless request.has_content_type?
|
288
282
|
|
289
283
|
ref = request.content_mime_type.ref
|
284
|
+
|
290
285
|
_wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
|
286
|
+
rescue ActionDispatch::Http::Parameters::ParseError
|
287
|
+
false
|
288
|
+
end
|
289
|
+
|
290
|
+
def _perform_parameter_wrapping
|
291
|
+
wrapped_hash = _wrap_parameters request.request_parameters
|
292
|
+
wrapped_keys = request.request_parameters.keys
|
293
|
+
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
|
294
|
+
|
295
|
+
# This will make the wrapped hash accessible from controller and view.
|
296
|
+
request.parameters.merge! wrapped_hash
|
297
|
+
request.request_parameters.merge! wrapped_hash
|
298
|
+
|
299
|
+
# This will display the wrapped hash in the log file.
|
300
|
+
request.filtered_parameters.merge! wrapped_filtered_hash
|
291
301
|
end
|
292
302
|
end
|
293
303
|
end
|
@@ -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+.
|
@@ -55,12 +59,16 @@ module ActionController
|
|
55
59
|
# Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
|
56
60
|
# To terminate the execution of the function immediately after the +redirect_to+, use return.
|
57
61
|
# redirect_to post_url(@post) and return
|
58
|
-
def redirect_to(options = {},
|
62
|
+
def redirect_to(options = {}, response_options = {})
|
59
63
|
raise ActionControllerError.new("Cannot redirect to nil!") unless options
|
60
64
|
raise AbstractController::DoubleRenderError if response_body
|
61
65
|
|
62
|
-
self.status = _extract_redirect_to_status(options,
|
63
|
-
|
66
|
+
self.status = _extract_redirect_to_status(options, response_options)
|
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"]
|
@@ -114,11 +122,11 @@ module ActionController
|
|
114
122
|
public :_compute_redirect_to_location
|
115
123
|
|
116
124
|
private
|
117
|
-
def _extract_redirect_to_status(options,
|
125
|
+
def _extract_redirect_to_status(options, response_options)
|
118
126
|
if options.is_a?(Hash) && options.key?(:status)
|
119
127
|
Rack::Utils.status_code(options.delete(:status))
|
120
|
-
elsif
|
121
|
-
Rack::Utils.status_code(
|
128
|
+
elsif response_options.key?(:status)
|
129
|
+
Rack::Utils.status_code(response_options[:status])
|
122
130
|
else
|
123
131
|
302
|
124
132
|
end
|
@@ -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
|
@@ -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,11 +72,17 @@ 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
|
80
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
|
+
|
81
86
|
# Normalize arguments by catching blocks and setting them on :update.
|
82
87
|
def _normalize_args(action = nil, options = {}, &blk)
|
83
88
|
options = super
|