actionpack 5.2.1 → 7.0.2.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +264 -220
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +24 -4
- data/lib/abstract_controller/caching/fragments.rb +8 -24
- data/lib/abstract_controller/caching.rb +2 -2
- data/lib/abstract_controller/callbacks.rb +34 -8
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +107 -90
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +12 -5
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api.rb +5 -4
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +13 -9
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +57 -6
- data/lib/action_controller/metal/content_security_policy.rb +2 -3
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +9 -18
- 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 +55 -12
- data/lib/action_controller/metal/flash.rb +10 -6
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +41 -39
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +59 -55
- data/lib/action_controller/metal/live.rb +80 -33
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +22 -7
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +50 -31
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +93 -23
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +14 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
- data/lib/action_controller/metal/rescue.rb +2 -2
- data/lib/action_controller/metal/streaming.rb +1 -4
- data/lib/action_controller/metal/strong_parameters.rb +236 -88
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +16 -17
- data/lib/action_controller/railtie.rb +49 -6
- 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 +98 -68
- data/lib/action_controller.rb +4 -5
- data/lib/action_dispatch/http/cache.rb +45 -32
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -56
- data/lib/action_dispatch/http/filter_parameters.rb +14 -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 +44 -16
- data/lib/action_dispatch/http/mime_type.rb +47 -30
- data/lib/action_dispatch/http/parameters.rb +18 -27
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +49 -35
- data/lib/action_dispatch/http/response.rb +34 -26
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +86 -94
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
- data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +83 -16
- 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 +42 -34
- data/lib/action_dispatch/journey/route.rb +14 -31
- data/lib/action_dispatch/journey/router/utils.rb +16 -14
- data/lib/action_dispatch/journey/router.rb +27 -35
- data/lib/action_dispatch/journey/routes.rb +3 -5
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +136 -113
- data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
- data/lib/action_dispatch/middleware/executor.rb +4 -1
- data/lib/action_dispatch/middleware/flash.rb +10 -12
- data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +79 -7
- data/lib/action_dispatch/middleware/static.rb +150 -94
- 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 +6 -11
- 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 +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
- 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 +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
- data/lib/action_dispatch/railtie.rb +16 -4
- data/lib/action_dispatch/request/session.rb +59 -22
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +102 -54
- data/lib/action_dispatch/routing/mapper.rb +184 -156
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- data/lib/action_dispatch/routing/redirection.rb +4 -6
- data/lib/action_dispatch/routing/route_set.rb +83 -73
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +2 -3
- data/lib/action_dispatch/routing.rb +23 -22
- data/lib/action_dispatch/system_test_case.rb +65 -16
- data/lib/action_dispatch/system_testing/browser.rb +43 -16
- data/lib/action_dispatch/system_testing/driver.rb +42 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -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 +3 -6
- data/lib/action_dispatch/testing/integration.rb +61 -30
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +8 -6
- 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 +15 -7
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +44 -25
- 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
@@ -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
|
@@ -125,9 +124,14 @@ module ActionController
|
|
125
124
|
class ClientDisconnected < RuntimeError
|
126
125
|
end
|
127
126
|
|
128
|
-
class Buffer < ActionDispatch::Response::Buffer
|
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
|
|
@@ -164,6 +168,11 @@ module ActionController
|
|
164
168
|
end
|
165
169
|
end
|
166
170
|
|
171
|
+
# Same as +write+ but automatically include a newline at the end of the string.
|
172
|
+
def writeln(string)
|
173
|
+
write string.end_with?("\n") ? string : "#{string}\n"
|
174
|
+
end
|
175
|
+
|
167
176
|
# Write a 'close' event to the buffer; the producer/writing thread
|
168
177
|
# uses this to notify us that it's finished supplying content.
|
169
178
|
#
|
@@ -205,7 +214,6 @@ module ActionController
|
|
205
214
|
end
|
206
215
|
|
207
216
|
private
|
208
|
-
|
209
217
|
def each_chunk(&block)
|
210
218
|
loop do
|
211
219
|
str = nil
|
@@ -216,11 +224,14 @@ module ActionController
|
|
216
224
|
yield str
|
217
225
|
end
|
218
226
|
end
|
227
|
+
|
228
|
+
def build_queue(queue_size)
|
229
|
+
queue_size ? SizedQueue.new(queue_size) : Queue.new
|
230
|
+
end
|
219
231
|
end
|
220
232
|
|
221
|
-
class Response < ActionDispatch::Response
|
233
|
+
class Response < ActionDispatch::Response # :nodoc: all
|
222
234
|
private
|
223
|
-
|
224
235
|
def before_committed
|
225
236
|
super
|
226
237
|
jar = request.cookie_jar
|
@@ -280,33 +291,69 @@ module ActionController
|
|
280
291
|
raise error if error
|
281
292
|
end
|
282
293
|
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
-
}
|
294
|
+
def response_body=(body)
|
295
|
+
super
|
296
|
+
response.close if response
|
293
297
|
end
|
294
298
|
|
295
|
-
|
296
|
-
|
297
|
-
|
299
|
+
# Sends a stream to the browser, which is helpful when you're generating exports or other running data where you
|
300
|
+
# don't want the entire file buffered in memory first. Similar to send_data, but where the data is generated live.
|
301
|
+
#
|
302
|
+
# Options:
|
303
|
+
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
304
|
+
# * <tt>:type</tt> - specifies an HTTP content type.
|
305
|
+
# You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
|
306
|
+
# If omitted, type will be inferred from the file extension specified in <tt>:filename</tt>.
|
307
|
+
# If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
|
308
|
+
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
309
|
+
# Valid values are 'inline' and 'attachment' (default).
|
310
|
+
#
|
311
|
+
# Example of generating a csv export:
|
312
|
+
#
|
313
|
+
# send_stream(filename: "subscribers.csv") do |stream|
|
314
|
+
# stream.write "email_address,updated_at\n"
|
315
|
+
#
|
316
|
+
# @subscribers.find_each do |subscriber|
|
317
|
+
# stream.write "#{subscriber.email_address},#{subscriber.updated_at}\n"
|
318
|
+
# end
|
319
|
+
# end
|
320
|
+
def send_stream(filename:, disposition: "attachment", type: nil)
|
321
|
+
response.headers["Content-Type"] =
|
322
|
+
(type.is_a?(Symbol) ? Mime[type].to_s : type) ||
|
323
|
+
Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete(".")) ||
|
324
|
+
"application/octet-stream"
|
325
|
+
|
326
|
+
response.headers["Content-Disposition"] =
|
327
|
+
ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)
|
328
|
+
|
329
|
+
yield response.stream
|
330
|
+
ensure
|
331
|
+
response.stream.close
|
332
|
+
end
|
298
333
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
334
|
+
private
|
335
|
+
# Spawn a new thread to serve up the controller in. This is to get
|
336
|
+
# around the fact that Rack isn't based around IOs and we need to use
|
337
|
+
# a thread to stream data from the response bodies. Nobody should call
|
338
|
+
# this method except in Rails internals. Seriously!
|
339
|
+
def new_controller_thread # :nodoc:
|
340
|
+
Thread.new {
|
341
|
+
t2 = Thread.current
|
342
|
+
t2.abort_on_exception = true
|
343
|
+
yield
|
344
|
+
}
|
304
345
|
end
|
305
|
-
end
|
306
346
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
347
|
+
def log_error(exception)
|
348
|
+
logger = ActionController::Base.logger
|
349
|
+
return unless logger
|
350
|
+
|
351
|
+
logger.fatal do
|
352
|
+
message = +"\n#{exception.class} (#{exception.message}):\n"
|
353
|
+
message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
|
354
|
+
message << " " << exception.backtrace.join("\n ")
|
355
|
+
"#{message}\n\n"
|
356
|
+
end
|
357
|
+
end
|
311
358
|
end
|
312
359
|
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
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "abstract_controller/collector"
|
4
4
|
|
5
|
-
module ActionController
|
5
|
+
module ActionController # :nodoc:
|
6
6
|
module MimeResponds
|
7
7
|
# Without web-service support, an action which collects the data for displaying a list of people
|
8
8
|
# might look something like this:
|
@@ -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
|
@@ -103,9 +103,9 @@ module ActionController #:nodoc:
|
|
103
103
|
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
|
104
104
|
# +config/initializers/mime_types.rb+ as follows.
|
105
105
|
#
|
106
|
-
# Mime::Type.register "image/
|
106
|
+
# Mime::Type.register "image/jpeg", :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
|
@@ -274,7 +289,7 @@ module ActionController #:nodoc:
|
|
274
289
|
@format = request.negotiate_mime(@responses.keys)
|
275
290
|
end
|
276
291
|
|
277
|
-
class VariantCollector
|
292
|
+
class VariantCollector # :nodoc:
|
278
293
|
def initialize(variant = nil)
|
279
294
|
@variant = variant
|
280
295
|
@variants = {}
|
@@ -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
|
@@ -9,11 +9,14 @@ module ActionController
|
|
9
9
|
# Wraps the parameters hash into a nested hash. This will allow clients to
|
10
10
|
# submit requests without having to specify any root elements.
|
11
11
|
#
|
12
|
-
# This functionality is enabled
|
13
|
-
#
|
12
|
+
# This functionality is enabled by default for JSON, and can be customized by
|
13
|
+
# setting the format array:
|
14
14
|
#
|
15
|
-
#
|
16
|
-
#
|
15
|
+
# class ApplicationController < ActionController::Base
|
16
|
+
# wrap_parameters format: [:json, :xml]
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# You could also turn it on per controller:
|
17
20
|
#
|
18
21
|
# class UsersController < ApplicationController
|
19
22
|
# wrap_parameters format: [:json, :xml, :url_encoded_form, :multipart_form]
|
@@ -68,6 +71,12 @@ module ActionController
|
|
68
71
|
# will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to
|
69
72
|
# determine the wrapper key respectively. If both models don't exist,
|
70
73
|
# it will then fallback to use +user+ as the key.
|
74
|
+
#
|
75
|
+
# To disable this functionality for a controller:
|
76
|
+
#
|
77
|
+
# class UsersController < ApplicationController
|
78
|
+
# wrap_parameters false
|
79
|
+
# end
|
71
80
|
module ParamsWrapper
|
72
81
|
extend ActiveSupport::Concern
|
73
82
|
|
@@ -93,7 +102,7 @@ module ActionController
|
|
93
102
|
end
|
94
103
|
|
95
104
|
def model
|
96
|
-
super ||
|
105
|
+
super || self.model = _default_wrap_model
|
97
106
|
end
|
98
107
|
|
99
108
|
def include
|
@@ -107,15 +116,19 @@ module ActionController
|
|
107
116
|
|
108
117
|
unless super || exclude
|
109
118
|
if m.respond_to?(:attribute_names) && m.attribute_names.any?
|
119
|
+
self.include = m.attribute_names
|
120
|
+
|
110
121
|
if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
|
111
|
-
self.include
|
112
|
-
|
113
|
-
|
122
|
+
self.include += m.stored_attributes.values.flatten.map(&:to_s)
|
123
|
+
end
|
124
|
+
|
125
|
+
if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any?
|
126
|
+
self.include += m.attribute_aliases.keys
|
114
127
|
end
|
115
128
|
|
116
129
|
if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
|
117
130
|
self.include += m.nested_attributes_options.keys.map do |key|
|
118
|
-
key.to_s.concat("_attributes")
|
131
|
+
(+key.to_s).concat("_attributes")
|
119
132
|
end
|
120
133
|
end
|
121
134
|
|
@@ -151,7 +164,7 @@ module ActionController
|
|
151
164
|
# try to find Foo::Bar::User, Foo::User and finally User.
|
152
165
|
def _default_wrap_model
|
153
166
|
return nil if klass.anonymous?
|
154
|
-
model_name = klass.name.
|
167
|
+
model_name = klass.name.delete_suffix("Controller").classify
|
155
168
|
|
156
169
|
begin
|
157
170
|
if model_klass = model_name.safe_constantize
|
@@ -189,7 +202,7 @@ module ActionController
|
|
189
202
|
#
|
190
203
|
# wrap_parameters Person
|
191
204
|
# # wraps parameters by determining the wrapper key from Person class
|
192
|
-
# (+person+, in this case) and the list of attribute names
|
205
|
+
# # (+person+, in this case) and the list of attribute names
|
193
206
|
#
|
194
207
|
# wrap_parameters include: [:username, :title]
|
195
208
|
# # wraps only +:username+ and +:title+ attributes from parameters.
|
@@ -238,25 +251,13 @@ module ActionController
|
|
238
251
|
end
|
239
252
|
end
|
240
253
|
|
241
|
-
# Performs parameters wrapping upon the request. Called automatically
|
242
|
-
# by the metal call stack.
|
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
|
256
|
-
super
|
257
|
-
end
|
258
|
-
|
259
254
|
private
|
255
|
+
# Performs parameters wrapping upon the request. Called automatically
|
256
|
+
# by the metal call stack.
|
257
|
+
def process_action(*)
|
258
|
+
_perform_parameter_wrapping if _wrapper_enabled?
|
259
|
+
super
|
260
|
+
end
|
260
261
|
|
261
262
|
# Returns the wrapper key which will be used to store wrapped parameters.
|
262
263
|
def _wrapper_key
|
@@ -276,9 +277,11 @@ module ActionController
|
|
276
277
|
def _extract_parameters(parameters)
|
277
278
|
if include_only = _wrapper_options.include
|
278
279
|
parameters.slice(*include_only)
|
280
|
+
elsif _wrapper_options.exclude
|
281
|
+
exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
|
282
|
+
parameters.except(*exclude)
|
279
283
|
else
|
280
|
-
|
281
|
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
|
284
|
+
parameters.except(*EXCLUDE_PARAMETERS)
|
282
285
|
end
|
283
286
|
end
|
284
287
|
|
@@ -287,7 +290,23 @@ module ActionController
|
|
287
290
|
return false unless request.has_content_type?
|
288
291
|
|
289
292
|
ref = request.content_mime_type.ref
|
293
|
+
|
290
294
|
_wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
|
295
|
+
rescue ActionDispatch::Http::Parameters::ParseError
|
296
|
+
false
|
297
|
+
end
|
298
|
+
|
299
|
+
def _perform_parameter_wrapping
|
300
|
+
wrapped_hash = _wrap_parameters request.request_parameters
|
301
|
+
wrapped_keys = request.request_parameters.keys
|
302
|
+
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
|
303
|
+
|
304
|
+
# This will make the wrapped hash accessible from controller and view.
|
305
|
+
request.parameters.merge! wrapped_hash
|
306
|
+
request.request_parameters.merge! wrapped_hash
|
307
|
+
|
308
|
+
# This will display the wrapped hash in the log file.
|
309
|
+
request.filtered_parameters.merge! wrapped_filtered_hash
|
291
310
|
end
|
292
311
|
end
|
293
312
|
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
|