actionpack 6.1.3.2 → 7.0.0.alpha2
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 +103 -387
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +7 -21
- data/lib/abstract_controller/caching/fragments.rb +2 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +9 -8
- data/lib/abstract_controller/collector.rb +4 -2
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +3 -2
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/translation.rb +0 -2
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/action_controller/api.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +38 -1
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -13
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/http_authentication.rb +15 -15
- data/lib/action_controller/metal/instrumentation.rb +55 -52
- data/lib/action_controller/metal/live.rb +52 -3
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/params_wrapper.rb +10 -9
- data/lib/action_controller/metal/permissions_policy.rb +1 -1
- data/lib/action_controller/metal/query_tags.rb +16 -0
- data/lib/action_controller/metal/redirecting.rb +50 -16
- data/lib/action_controller/metal/rendering.rb +7 -7
- data/lib/action_controller/metal/request_forgery_protection.rb +64 -20
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +1 -3
- data/lib/action_controller/metal/strong_parameters.rb +24 -28
- data/lib/action_controller/metal/testing.rb +0 -2
- data/lib/action_controller/metal.rb +7 -10
- data/lib/action_controller/railtie.rb +42 -5
- data/lib/action_controller/test_case.rb +9 -2
- data/lib/action_controller.rb +2 -5
- data/lib/action_dispatch/http/cache.rb +18 -12
- data/lib/action_dispatch/http/content_security_policy.rb +39 -35
- data/lib/action_dispatch/http/filter_parameters.rb +5 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +13 -3
- data/lib/action_dispatch/http/mime_type.rb +9 -11
- data/lib/action_dispatch/http/parameters.rb +4 -4
- data/lib/action_dispatch/http/permissions_policy.rb +1 -1
- data/lib/action_dispatch/http/request.rb +10 -19
- data/lib/action_dispatch/http/response.rb +3 -3
- data/lib/action_dispatch/http/url.rb +9 -10
- data/lib/action_dispatch/journey/formatter.rb +2 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +22 -13
- data/lib/action_dispatch/journey/route.rb +5 -12
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +1 -1
- data/lib/action_dispatch/journey/routes.rb +3 -3
- data/lib/action_dispatch/journey/visitors.rb +1 -1
- 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/middleware/actionable_exceptions.rb +0 -1
- data/lib/action_dispatch/middleware/cookies.rb +7 -3
- data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
- data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
- data/lib/action_dispatch/middleware/flash.rb +9 -11
- data/lib/action_dispatch/middleware/host_authorization.rb +9 -17
- data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +7 -9
- data/lib/action_dispatch/middleware/stack.rb +27 -9
- data/lib/action_dispatch/middleware/static.rb +2 -5
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
- data/lib/action_dispatch/railtie.rb +8 -2
- data/lib/action_dispatch/request/session.rb +43 -13
- data/lib/action_dispatch/routing/mapper.rb +44 -72
- data/lib/action_dispatch/routing/redirection.rb +0 -2
- data/lib/action_dispatch/routing/route_set.rb +9 -6
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +1 -2
- data/lib/action_dispatch/routing.rb +2 -2
- data/lib/action_dispatch/system_test_case.rb +5 -5
- data/lib/action_dispatch/system_testing/driver.rb +24 -4
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +10 -6
- data/lib/action_dispatch/testing/assertions.rb +2 -5
- data/lib/action_dispatch/testing/integration.rb +6 -8
- data/lib/action_dispatch/testing/test_process.rb +12 -9
- data/lib/action_dispatch.rb +1 -1
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +21 -20
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionController
|
3
|
+
module ActionController # :nodoc:
|
4
4
|
module Flash
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
@@ -41,10 +41,14 @@ module ActionController #:nodoc:
|
|
41
41
|
self._flash_types += [type]
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
45
|
+
def action_methods # :nodoc:
|
46
|
+
@action_methods ||= super - _flash_types.map(&:to_s).to_set
|
47
|
+
end
|
44
48
|
end
|
45
49
|
|
46
50
|
private
|
47
|
-
def redirect_to(options = {}, response_options_and_flash = {})
|
51
|
+
def redirect_to(options = {}, response_options_and_flash = {}) # :doc:
|
48
52
|
self.class._flash_types.each do |flash_type|
|
49
53
|
if type = response_options_and_flash.delete(flash_type)
|
50
54
|
flash[flash_type] = type
|
@@ -4,9 +4,9 @@ require "base64"
|
|
4
4
|
require "active_support/security_utils"
|
5
5
|
|
6
6
|
module ActionController
|
7
|
-
#
|
7
|
+
# HTTP Basic, Digest and Token authentication.
|
8
8
|
module HttpAuthentication
|
9
|
-
#
|
9
|
+
# HTTP \Basic authentication.
|
10
10
|
#
|
11
11
|
# === Simple \Basic example
|
12
12
|
#
|
@@ -24,8 +24,8 @@ module ActionController
|
|
24
24
|
#
|
25
25
|
# === Advanced \Basic example
|
26
26
|
#
|
27
|
-
# Here is a more advanced \Basic example where only Atom feeds and the XML API
|
28
|
-
#
|
27
|
+
# Here is a more advanced \Basic example where only Atom feeds and the XML API are protected by HTTP authentication.
|
28
|
+
# The regular HTML interface is protected by a session approach:
|
29
29
|
#
|
30
30
|
# class ApplicationController < ActionController::Base
|
31
31
|
# before_action :set_account, :authenticate
|
@@ -134,15 +134,15 @@ module ActionController
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
-
#
|
137
|
+
# HTTP \Digest authentication.
|
138
138
|
#
|
139
139
|
# === Simple \Digest example
|
140
140
|
#
|
141
|
-
# require "
|
141
|
+
# require "openssl"
|
142
142
|
# class PostsController < ApplicationController
|
143
143
|
# REALM = "SuperSecret"
|
144
144
|
# USERS = {"dhh" => "secret", #plain text password
|
145
|
-
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
145
|
+
# "dap" => OpenSSL::Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
146
146
|
#
|
147
147
|
# before_action :authenticate, except: [:index]
|
148
148
|
#
|
@@ -230,12 +230,12 @@ module ActionController
|
|
230
230
|
# of a plain-text password.
|
231
231
|
def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
|
232
232
|
ha1 = password_is_ha1 ? password : ha1(credentials, password)
|
233
|
-
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
|
234
|
-
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
|
233
|
+
ha2 = OpenSSL::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
|
234
|
+
OpenSSL::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
|
235
235
|
end
|
236
236
|
|
237
237
|
def ha1(credentials, password)
|
238
|
-
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
|
238
|
+
OpenSSL::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
|
239
239
|
end
|
240
240
|
|
241
241
|
def encode_credentials(http_method, credentials, password, password_is_ha1)
|
@@ -309,7 +309,7 @@ module ActionController
|
|
309
309
|
def nonce(secret_key, time = Time.now)
|
310
310
|
t = time.to_i
|
311
311
|
hashed = [t, secret_key]
|
312
|
-
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
|
312
|
+
digest = OpenSSL::Digest::MD5.hexdigest(hashed.join(":"))
|
313
313
|
::Base64.strict_encode64("#{t}:#{digest}")
|
314
314
|
end
|
315
315
|
|
@@ -326,11 +326,11 @@ module ActionController
|
|
326
326
|
|
327
327
|
# Opaque based on digest of secret key
|
328
328
|
def opaque(secret_key)
|
329
|
-
::Digest::MD5.hexdigest(secret_key)
|
329
|
+
OpenSSL::Digest::MD5.hexdigest(secret_key)
|
330
330
|
end
|
331
331
|
end
|
332
332
|
|
333
|
-
#
|
333
|
+
# HTTP Token authentication.
|
334
334
|
#
|
335
335
|
# Simple Token example:
|
336
336
|
#
|
@@ -358,8 +358,8 @@ module ActionController
|
|
358
358
|
# end
|
359
359
|
#
|
360
360
|
#
|
361
|
-
# Here is a more advanced Token example where only Atom feeds and the XML API
|
362
|
-
#
|
361
|
+
# Here is a more advanced Token example where only Atom feeds and the XML API are protected by HTTP token authentication.
|
362
|
+
# The regular HTML interface is protected by a session approach:
|
363
363
|
#
|
364
364
|
# class ApplicationController < ActionController::Base
|
365
365
|
# before_action :set_account, :authenticate
|
@@ -16,30 +16,6 @@ module ActionController
|
|
16
16
|
|
17
17
|
attr_internal :view_runtime
|
18
18
|
|
19
|
-
def process_action(*)
|
20
|
-
raw_payload = {
|
21
|
-
controller: self.class.name,
|
22
|
-
action: action_name,
|
23
|
-
request: request,
|
24
|
-
params: request.filtered_parameters,
|
25
|
-
headers: request.headers,
|
26
|
-
format: request.format.ref,
|
27
|
-
method: request.request_method,
|
28
|
-
path: request.fullpath
|
29
|
-
}
|
30
|
-
|
31
|
-
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
|
32
|
-
|
33
|
-
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
34
|
-
result = super
|
35
|
-
payload[:response] = response
|
36
|
-
payload[:status] = response.status
|
37
|
-
result
|
38
|
-
ensure
|
39
|
-
append_info_to_payload(payload)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
19
|
def render(*)
|
44
20
|
render_output = nil
|
45
21
|
self.view_runtime = cleanup_view_runtime do
|
@@ -70,37 +46,64 @@ module ActionController
|
|
70
46
|
end
|
71
47
|
end
|
72
48
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
49
|
+
private
|
50
|
+
def process_action(*)
|
51
|
+
raw_payload = {
|
52
|
+
controller: self.class.name,
|
53
|
+
action: action_name,
|
54
|
+
request: request,
|
55
|
+
params: request.filtered_parameters,
|
56
|
+
headers: request.headers,
|
57
|
+
format: request.format.ref,
|
58
|
+
method: request.request_method,
|
59
|
+
path: request.fullpath
|
60
|
+
}
|
78
61
|
|
79
|
-
|
80
|
-
# views, like database querying time.
|
81
|
-
#
|
82
|
-
# def cleanup_view_runtime
|
83
|
-
# super - time_taken_in_something_expensive
|
84
|
-
# end
|
85
|
-
def cleanup_view_runtime # :doc:
|
86
|
-
yield
|
87
|
-
end
|
62
|
+
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
|
88
63
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
64
|
+
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
65
|
+
result = super
|
66
|
+
payload[:response] = response
|
67
|
+
payload[:status] = response.status
|
68
|
+
result
|
69
|
+
rescue => error
|
70
|
+
payload[:status] = ActionDispatch::ExceptionWrapper.status_code_for_exception(error.class.name)
|
71
|
+
raise
|
72
|
+
ensure
|
73
|
+
append_info_to_payload(payload)
|
74
|
+
end
|
75
|
+
end
|
94
76
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
77
|
+
# A hook invoked every time a before callback is halted.
|
78
|
+
def halted_callback_hook(filter, _)
|
79
|
+
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
80
|
+
end
|
81
|
+
|
82
|
+
# A hook which allows you to clean up any time, wrongly taken into account in
|
83
|
+
# views, like database querying time.
|
84
|
+
#
|
85
|
+
# def cleanup_view_runtime
|
86
|
+
# super - time_taken_in_something_expensive
|
87
|
+
# end
|
88
|
+
def cleanup_view_runtime # :doc:
|
89
|
+
yield
|
90
|
+
end
|
91
|
+
|
92
|
+
# Every time after an action is processed, this method is invoked
|
93
|
+
# with the payload, so you can add more information.
|
94
|
+
def append_info_to_payload(payload) # :doc:
|
95
|
+
payload[:view_runtime] = view_runtime
|
96
|
+
end
|
97
|
+
|
98
|
+
module ClassMethods
|
99
|
+
# A hook which allows other frameworks to log what happened during
|
100
|
+
# controller process action. This method should return an array
|
101
|
+
# with the messages to be added.
|
102
|
+
def log_process_action(payload) # :nodoc:
|
103
|
+
messages, view_runtime = [], payload[:view_runtime]
|
104
|
+
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
|
105
|
+
messages
|
106
|
+
end
|
103
107
|
end
|
104
|
-
end
|
105
108
|
end
|
106
109
|
end
|
@@ -124,9 +124,14 @@ module ActionController
|
|
124
124
|
class ClientDisconnected < RuntimeError
|
125
125
|
end
|
126
126
|
|
127
|
-
class Buffer < ActionDispatch::Response::Buffer
|
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,7 +141,7 @@ module ActionController
|
|
136
141
|
attr_accessor :ignore_disconnect
|
137
142
|
|
138
143
|
def initialize(response)
|
139
|
-
super(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
|
@@ -163,6 +168,11 @@ module ActionController
|
|
163
168
|
end
|
164
169
|
end
|
165
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
|
+
|
166
176
|
# Write a 'close' event to the buffer; the producer/writing thread
|
167
177
|
# uses this to notify us that it's finished supplying content.
|
168
178
|
#
|
@@ -214,9 +224,13 @@ module ActionController
|
|
214
224
|
yield str
|
215
225
|
end
|
216
226
|
end
|
227
|
+
|
228
|
+
def build_queue(queue_size)
|
229
|
+
queue_size ? SizedQueue.new(queue_size) : Queue.new
|
230
|
+
end
|
217
231
|
end
|
218
232
|
|
219
|
-
class Response < ActionDispatch::Response
|
233
|
+
class Response < ActionDispatch::Response # :nodoc: all
|
220
234
|
private
|
221
235
|
def before_committed
|
222
236
|
super
|
@@ -282,6 +296,41 @@ module ActionController
|
|
282
296
|
response.close if response
|
283
297
|
end
|
284
298
|
|
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
|
333
|
+
|
285
334
|
private
|
286
335
|
# Spawn a new thread to serve up the controller in. This is to get
|
287
336
|
# around the fact that Rack isn't based around IOs and we need to use
|
@@ -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:
|
@@ -103,7 +103,7 @@ 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
|
#
|
@@ -289,7 +289,7 @@ module ActionController #:nodoc:
|
|
289
289
|
@format = request.negotiate_mime(@responses.keys)
|
290
290
|
end
|
291
291
|
|
292
|
-
class VariantCollector
|
292
|
+
class VariantCollector # :nodoc:
|
293
293
|
def initialize(variant = nil)
|
294
294
|
@variant = variant
|
295
295
|
@variants = {}
|
@@ -242,14 +242,14 @@ module ActionController
|
|
242
242
|
end
|
243
243
|
end
|
244
244
|
|
245
|
-
# Performs parameters wrapping upon the request. Called automatically
|
246
|
-
# by the metal call stack.
|
247
|
-
def process_action(*)
|
248
|
-
_perform_parameter_wrapping if _wrapper_enabled?
|
249
|
-
super
|
250
|
-
end
|
251
|
-
|
252
245
|
private
|
246
|
+
# Performs parameters wrapping upon the request. Called automatically
|
247
|
+
# by the metal call stack.
|
248
|
+
def process_action(*)
|
249
|
+
_perform_parameter_wrapping if _wrapper_enabled?
|
250
|
+
super
|
251
|
+
end
|
252
|
+
|
253
253
|
# Returns the wrapper key which will be used to store wrapped parameters.
|
254
254
|
def _wrapper_key
|
255
255
|
_wrapper_options.name
|
@@ -281,7 +281,10 @@ module ActionController
|
|
281
281
|
return false unless request.has_content_type?
|
282
282
|
|
283
283
|
ref = request.content_mime_type.ref
|
284
|
+
|
284
285
|
_wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
|
286
|
+
rescue ActionDispatch::Http::Parameters::ParseError
|
287
|
+
false
|
285
288
|
end
|
286
289
|
|
287
290
|
def _perform_parameter_wrapping
|
@@ -295,8 +298,6 @@ module ActionController
|
|
295
298
|
|
296
299
|
# This will display the wrapped hash in the log file.
|
297
300
|
request.filtered_parameters.merge! wrapped_filtered_hash
|
298
|
-
rescue ActionDispatch::Http::Parameters::ParseError
|
299
|
-
# swallow parse error exception
|
300
301
|
end
|
301
302
|
end
|
302
303
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionController
|
3
|
+
module ActionController # :nodoc:
|
4
4
|
# HTTP Permissions Policy is a web standard for defining a mechanism to
|
5
5
|
# allow and deny the use of browser permissions in its own context, and
|
6
6
|
# in content within any <iframe> elements in the document.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
module QueryTags # :nodoc:
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
around_action :expose_controller_to_query_logs
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def expose_controller_to_query_logs(&block)
|
13
|
+
ActiveRecord::QueryLogs.set_context(controller: self, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -7,6 +7,10 @@ module ActionController
|
|
7
7
|
include AbstractController::Logger
|
8
8
|
include ActionController::UrlFor
|
9
9
|
|
10
|
+
included do
|
11
|
+
mattr_accessor :raise_on_open_redirects, default: false
|
12
|
+
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+.
|
@@ -54,16 +58,28 @@ module ActionController
|
|
54
58
|
#
|
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.
|
61
|
+
#
|
57
62
|
# redirect_to post_url(@post) and return
|
63
|
+
#
|
64
|
+
# Passing user input directly into +redirect_to+ is considered dangerous (e.g. `redirect_to(params[:location])`).
|
65
|
+
# Always use regular expressions or a permitted list when redirecting to a user specified location.
|
58
66
|
def redirect_to(options = {}, response_options = {})
|
67
|
+
response_options[:allow_other_host] ||= _allow_other_host unless response_options.key?(:allow_other_host)
|
68
|
+
|
59
69
|
raise ActionControllerError.new("Cannot redirect to nil!") unless options
|
60
70
|
raise AbstractController::DoubleRenderError if response_body
|
61
71
|
|
62
72
|
self.status = _extract_redirect_to_status(options, response_options)
|
63
|
-
self.location =
|
73
|
+
self.location = _compute_safe_redirect_to_location(request, options, response_options)
|
64
74
|
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
|
65
75
|
end
|
66
76
|
|
77
|
+
# Soft deprecated alias for <tt>redirect_back_or_to</tt> where the fallback_location location is supplied as a keyword argument instead
|
78
|
+
# of the first positional argument.
|
79
|
+
def redirect_back(fallback_location:, allow_other_host: _allow_other_host, **args)
|
80
|
+
redirect_back_or_to fallback_location, allow_other_host: allow_other_host, **args
|
81
|
+
end
|
82
|
+
|
67
83
|
# Redirects the browser to the page that issued the request (the referrer)
|
68
84
|
# if possible, otherwise redirects to the provided default fallback
|
69
85
|
# location.
|
@@ -73,35 +89,49 @@ module ActionController
|
|
73
89
|
# subject to browser security settings and user preferences. If the request
|
74
90
|
# is missing this header, the <tt>fallback_location</tt> will be used.
|
75
91
|
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
92
|
+
# redirect_back_or_to({ action: "show", id: 5 })
|
93
|
+
# redirect_back_or_to @post
|
94
|
+
# redirect_back_or_to "http://www.rubyonrails.org"
|
95
|
+
# redirect_back_or_to "/images/screenshot.jpg"
|
96
|
+
# redirect_back_or_to posts_url
|
97
|
+
# redirect_back_or_to proc { edit_post_url(@post) }
|
98
|
+
# redirect_back_or_to '/', allow_other_host: false
|
83
99
|
#
|
84
100
|
# ==== Options
|
85
|
-
# * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
|
86
101
|
# * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
|
87
102
|
#
|
88
103
|
# All other options that can be passed to #redirect_to are accepted as
|
89
104
|
# options and the behavior is identical.
|
90
|
-
def
|
91
|
-
|
92
|
-
|
93
|
-
|
105
|
+
def redirect_back_or_to(fallback_location, allow_other_host: _allow_other_host, **options)
|
106
|
+
location = request.referer || fallback_location
|
107
|
+
location = fallback_location unless allow_other_host || _url_host_allowed?(request.referer)
|
108
|
+
allow_other_host = true if _allow_other_host && !allow_other_host # if the fallback is an open redirect
|
109
|
+
|
110
|
+
redirect_to location, allow_other_host: allow_other_host, **options
|
94
111
|
end
|
95
112
|
|
96
|
-
def
|
113
|
+
def _compute_safe_redirect_to_location(request, options, response_options)
|
114
|
+
location = _compute_redirect_to_location(request, options)
|
115
|
+
|
116
|
+
if response_options[:allow_other_host] || _url_host_allowed?(location)
|
117
|
+
location
|
118
|
+
else
|
119
|
+
raise(ArgumentError, <<~MSG.squish)
|
120
|
+
Unsafe redirect #{location.truncate(100).inspect},
|
121
|
+
use :allow_other_host to redirect anyway.
|
122
|
+
MSG
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def _compute_redirect_to_location(request, options) # :nodoc:
|
97
127
|
case options
|
98
128
|
# The scheme name consist of a letter followed by any combination of
|
99
129
|
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
|
100
130
|
# characters; and is terminated by a colon (":").
|
101
131
|
# See https://tools.ietf.org/html/rfc3986#section-3.1
|
102
132
|
# The protocol relative scheme starts with a double slash "//".
|
103
|
-
when /\A([a-z][a-z\d
|
104
|
-
options
|
133
|
+
when /\A([a-z][a-z\d\-+.]*:|\/\/).*/i
|
134
|
+
options.to_str
|
105
135
|
when String
|
106
136
|
request.protocol + request.host_with_port + options
|
107
137
|
when Proc
|
@@ -114,6 +144,10 @@ module ActionController
|
|
114
144
|
public :_compute_redirect_to_location
|
115
145
|
|
116
146
|
private
|
147
|
+
def _allow_other_host
|
148
|
+
!raise_on_open_redirects
|
149
|
+
end
|
150
|
+
|
117
151
|
def _extract_redirect_to_status(options, response_options)
|
118
152
|
if options.is_a?(Hash) && options.key?(:status)
|
119
153
|
Rack::Utils.status_code(options.delete(:status))
|