actionpack 4.2.10 → 6.1.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 +5 -5
- data/CHANGELOG.md +311 -461
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +81 -51
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +61 -33
- data/lib/abstract_controller/collector.rb +9 -13
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +115 -99
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
- data/lib/abstract_controller/rendering.rb +48 -47
- data/lib/abstract_controller/translation.rb +17 -8
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +13 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +150 -0
- data/lib/action_controller/base.rb +29 -24
- data/lib/action_controller/caching.rb +12 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +17 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +134 -46
- data/lib/action_controller/metal/content_security_policy.rb +51 -0
- data/lib/action_controller/metal/cookies.rb +6 -4
- data/lib/action_controller/metal/data_streaming.rb +30 -50
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
- data/lib/action_controller/metal/exceptions.rb +63 -15
- data/lib/action_controller/metal/flash.rb +9 -8
- data/lib/action_controller/metal/head.rb +26 -21
- data/lib/action_controller/metal/helpers.rb +37 -18
- data/lib/action_controller/metal/http_authentication.rb +81 -73
- data/lib/action_controller/metal/implicit_render.rb +53 -9
- data/lib/action_controller/metal/instrumentation.rb +32 -35
- data/lib/action_controller/metal/live.rb +110 -119
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +49 -47
- data/lib/action_controller/metal/parameter_encoding.rb +82 -0
- data/lib/action_controller/metal/params_wrapper.rb +84 -66
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +53 -32
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +77 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
- data/lib/action_controller/metal/rescue.rb +10 -17
- data/lib/action_controller/metal/streaming.rb +12 -11
- data/lib/action_controller/metal/strong_parameters.rb +714 -186
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +104 -87
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +3 -1
- data/lib/action_controller/renderer.rb +141 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +298 -421
- data/lib/action_controller.rb +34 -23
- data/lib/action_dispatch/http/cache.rb +108 -58
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +286 -0
- data/lib/action_dispatch/http/filter_parameters.rb +32 -25
- data/lib/action_dispatch/http/filter_redirect.rb +10 -12
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +82 -50
- data/lib/action_dispatch/http/mime_type.rb +153 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameters.rb +90 -40
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +226 -121
- data/lib/action_dispatch/http/response.rb +248 -113
- data/lib/action_dispatch/http/upload.rb +21 -7
- data/lib/action_dispatch/http/url.rb +182 -100
- data/lib/action_dispatch/journey/formatter.rb +91 -44
- data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
- data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
- data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
- data/lib/action_dispatch/journey/nodes/node.rb +29 -15
- data/lib/action_dispatch/journey/parser.rb +17 -16
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +58 -54
- data/lib/action_dispatch/journey/route.rb +100 -32
- data/lib/action_dispatch/journey/router/utils.rb +29 -18
- data/lib/action_dispatch/journey/router.rb +55 -51
- data/lib/action_dispatch/journey/routes.rb +17 -17
- data/lib/action_dispatch/journey/scanner.rb +26 -17
- data/lib/action_dispatch/journey/visitors.rb +98 -54
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +3 -6
- data/lib/action_dispatch/middleware/cookies.rb +347 -217
- data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
- data/lib/action_dispatch/middleware/request_id.rb +17 -10
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
- data/lib/action_dispatch/middleware/ssl.rb +118 -35
- data/lib/action_dispatch/middleware/stack.rb +82 -41
- data/lib/action_dispatch/middleware/static.rb +156 -89
- 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 +4 -14
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- 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 +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -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 +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
- data/lib/action_dispatch/railtie.rb +27 -13
- data/lib/action_dispatch/request/session.rb +109 -61
- data/lib/action_dispatch/request/utils.rb +90 -23
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +141 -102
- data/lib/action_dispatch/routing/mapper.rb +811 -473
- data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
- data/lib/action_dispatch/routing/redirection.rb +37 -27
- data/lib/action_dispatch/routing/route_set.rb +363 -331
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +66 -26
- data/lib/action_dispatch/routing.rb +36 -36
- data/lib/action_dispatch/system_test_case.rb +190 -0
- data/lib/action_dispatch/system_testing/browser.rb +86 -0
- data/lib/action_dispatch/system_testing/driver.rb +67 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
- data/lib/action_dispatch/testing/assertion_response.rb +46 -0
- data/lib/action_dispatch/testing/assertions/response.rb +44 -22
- data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +391 -220
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +53 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +11 -11
- data/lib/action_dispatch.rb +35 -21
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +75 -46
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,16 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/slice"
|
4
|
+
require "active_support/core_ext/hash/except"
|
5
|
+
require "active_support/core_ext/module/anonymous"
|
6
|
+
require "action_dispatch/http/mime_type"
|
6
7
|
|
7
8
|
module ActionController
|
8
9
|
# Wraps the parameters hash into a nested hash. This will allow clients to
|
9
10
|
# submit requests without having to specify any root elements.
|
10
11
|
#
|
11
12
|
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
|
12
|
-
# and can be customized.
|
13
|
-
# need to be created for the functionality to be enabled.
|
13
|
+
# and can be customized.
|
14
14
|
#
|
15
15
|
# You could also turn it on per controller by setting the format array to
|
16
16
|
# a non-empty array:
|
@@ -42,7 +42,7 @@ module ActionController
|
|
42
42
|
# wrap_parameters :person, include: [:username, :password]
|
43
43
|
# end
|
44
44
|
#
|
45
|
-
# On
|
45
|
+
# On Active Record models with no +:include+ or +:exclude+ option set,
|
46
46
|
# it will only wrap the parameters returned by the class method
|
47
47
|
# <tt>attribute_names</tt>.
|
48
48
|
#
|
@@ -73,7 +73,7 @@ module ActionController
|
|
73
73
|
|
74
74
|
EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
|
75
75
|
|
76
|
-
require
|
76
|
+
require "mutex_m"
|
77
77
|
|
78
78
|
class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc:
|
79
79
|
include Mutex_m
|
@@ -86,14 +86,14 @@ module ActionController
|
|
86
86
|
new name, format, include, exclude, nil, nil
|
87
87
|
end
|
88
88
|
|
89
|
-
def initialize(name, format, include, exclude, klass, model) # nodoc
|
89
|
+
def initialize(name, format, include, exclude, klass, model) # :nodoc:
|
90
90
|
super
|
91
91
|
@include_set = include
|
92
92
|
@name_set = name
|
93
93
|
end
|
94
94
|
|
95
95
|
def model
|
96
|
-
super ||
|
96
|
+
super || self.model = _default_wrap_model
|
97
97
|
end
|
98
98
|
|
99
99
|
def include
|
@@ -108,6 +108,22 @@ module ActionController
|
|
108
108
|
unless super || exclude
|
109
109
|
if m.respond_to?(:attribute_names) && m.attribute_names.any?
|
110
110
|
self.include = m.attribute_names
|
111
|
+
|
112
|
+
if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
|
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
|
118
|
+
end
|
119
|
+
|
120
|
+
if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
|
121
|
+
self.include += m.nested_attributes_options.keys.map do |key|
|
122
|
+
(+key.to_s).concat("_attributes")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
self.include
|
111
127
|
end
|
112
128
|
end
|
113
129
|
end
|
@@ -130,35 +146,34 @@ module ActionController
|
|
130
146
|
end
|
131
147
|
|
132
148
|
private
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
149
|
+
# Determine the wrapper model from the controller's name. By convention,
|
150
|
+
# this could be done by trying to find the defined model that has the
|
151
|
+
# same singular name as the controller. For example, +UsersController+
|
152
|
+
# will try to find if the +User+ model exists.
|
153
|
+
#
|
154
|
+
# This method also does namespace lookup. Foo::Bar::UsersController will
|
155
|
+
# try to find Foo::Bar::User, Foo::User and finally User.
|
156
|
+
def _default_wrap_model
|
157
|
+
return nil if klass.anonymous?
|
158
|
+
model_name = klass.name.delete_suffix("Controller").classify
|
159
|
+
|
160
|
+
begin
|
161
|
+
if model_klass = model_name.safe_constantize
|
162
|
+
model_klass
|
163
|
+
else
|
164
|
+
namespaces = model_name.split("::")
|
165
|
+
namespaces.delete_at(-2)
|
166
|
+
break if namespaces.last == model_name
|
167
|
+
model_name = namespaces.join("::")
|
168
|
+
end
|
169
|
+
end until model_klass
|
154
170
|
|
155
|
-
|
156
|
-
|
171
|
+
model_klass
|
172
|
+
end
|
157
173
|
end
|
158
174
|
|
159
175
|
included do
|
160
|
-
class_attribute :_wrapper_options
|
161
|
-
self._wrapper_options = Options.from_hash(format: [])
|
176
|
+
class_attribute :_wrapper_options, default: Options.from_hash(format: [])
|
162
177
|
end
|
163
178
|
|
164
179
|
module ClassMethods
|
@@ -178,7 +193,7 @@ module ActionController
|
|
178
193
|
#
|
179
194
|
# wrap_parameters Person
|
180
195
|
# # wraps parameters by determining the wrapper key from Person class
|
181
|
-
# (+person+, in this case) and the list of attribute names
|
196
|
+
# # (+person+, in this case) and the list of attribute names
|
182
197
|
#
|
183
198
|
# wrap_parameters include: [:username, :title]
|
184
199
|
# # wraps only +:username+ and +:title+ attributes from parameters.
|
@@ -200,14 +215,14 @@ module ActionController
|
|
200
215
|
when Hash
|
201
216
|
options = name_or_model_or_options
|
202
217
|
when false
|
203
|
-
options = options.merge(:
|
218
|
+
options = options.merge(format: [])
|
204
219
|
when Symbol, String
|
205
|
-
options = options.merge(:
|
220
|
+
options = options.merge(name: name_or_model_or_options)
|
206
221
|
else
|
207
222
|
model = name_or_model_or_options
|
208
223
|
end
|
209
224
|
|
210
|
-
opts
|
225
|
+
opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
|
211
226
|
opts.model = model
|
212
227
|
opts.klass = self
|
213
228
|
|
@@ -215,7 +230,7 @@ module ActionController
|
|
215
230
|
end
|
216
231
|
|
217
232
|
# Sets the default wrapper key or model which will be used to determine
|
218
|
-
# wrapper key and attribute names.
|
233
|
+
# wrapper key and attribute names. Called automatically when the
|
219
234
|
# module is inherited.
|
220
235
|
def inherited(klass)
|
221
236
|
if klass._wrapper_options.format.any?
|
@@ -227,32 +242,15 @@ module ActionController
|
|
227
242
|
end
|
228
243
|
end
|
229
244
|
|
230
|
-
# Performs parameters wrapping upon the request.
|
245
|
+
# Performs parameters wrapping upon the request. Called automatically
|
231
246
|
# by the metal call stack.
|
232
|
-
def process_action(*
|
233
|
-
if _wrapper_enabled?
|
234
|
-
if request.parameters[_wrapper_key].present?
|
235
|
-
wrapped_hash = _extract_parameters(request.parameters)
|
236
|
-
else
|
237
|
-
wrapped_hash = _wrap_parameters request.request_parameters
|
238
|
-
end
|
239
|
-
|
240
|
-
wrapped_keys = request.request_parameters.keys
|
241
|
-
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
|
242
|
-
|
243
|
-
# This will make the wrapped hash accessible from controller and view
|
244
|
-
request.parameters.merge! wrapped_hash
|
245
|
-
request.request_parameters.merge! wrapped_hash
|
246
|
-
|
247
|
-
# This will display the wrapped hash in the log file
|
248
|
-
request.filtered_parameters.merge! wrapped_filtered_hash
|
249
|
-
end
|
247
|
+
def process_action(*)
|
248
|
+
_perform_parameter_wrapping if _wrapper_enabled?
|
250
249
|
super
|
251
250
|
end
|
252
251
|
|
253
252
|
private
|
254
|
-
|
255
|
-
# Returns the wrapper key which will be used to stored wrapped parameters.
|
253
|
+
# Returns the wrapper key which will be used to store wrapped parameters.
|
256
254
|
def _wrapper_key
|
257
255
|
_wrapper_options.name
|
258
256
|
end
|
@@ -270,16 +268,36 @@ module ActionController
|
|
270
268
|
def _extract_parameters(parameters)
|
271
269
|
if include_only = _wrapper_options.include
|
272
270
|
parameters.slice(*include_only)
|
271
|
+
elsif _wrapper_options.exclude
|
272
|
+
exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
|
273
|
+
parameters.except(*exclude)
|
273
274
|
else
|
274
|
-
|
275
|
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
|
275
|
+
parameters.except(*EXCLUDE_PARAMETERS)
|
276
276
|
end
|
277
277
|
end
|
278
278
|
|
279
279
|
# Checks if we should perform parameters wrapping.
|
280
280
|
def _wrapper_enabled?
|
281
|
-
|
282
|
-
|
281
|
+
return false unless request.has_content_type?
|
282
|
+
|
283
|
+
ref = request.content_mime_type.ref
|
284
|
+
|
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
|
283
301
|
end
|
284
302
|
end
|
285
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
|
@@ -1,17 +1,10 @@
|
|
1
|
-
|
2
|
-
class RedirectBackError < AbstractController::Error #:nodoc:
|
3
|
-
DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
|
4
|
-
|
5
|
-
def initialize(message = nil)
|
6
|
-
super(message || DEFAULT_MESSAGE)
|
7
|
-
end
|
8
|
-
end
|
1
|
+
# frozen_string_literal: true
|
9
2
|
|
3
|
+
module ActionController
|
10
4
|
module Redirecting
|
11
5
|
extend ActiveSupport::Concern
|
12
6
|
|
13
7
|
include AbstractController::Logger
|
14
|
-
include ActionController::RackDelegation
|
15
8
|
include ActionController::UrlFor
|
16
9
|
|
17
10
|
# Redirects the browser to the target specified in +options+. This parameter can be any one of:
|
@@ -21,34 +14,31 @@ module ActionController
|
|
21
14
|
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
|
22
15
|
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
|
23
16
|
# * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
|
24
|
-
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
|
25
|
-
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
|
26
17
|
#
|
27
18
|
# === Examples:
|
28
19
|
#
|
29
20
|
# redirect_to action: "show", id: 5
|
30
|
-
# redirect_to post
|
21
|
+
# redirect_to @post
|
31
22
|
# redirect_to "http://www.rubyonrails.org"
|
32
23
|
# redirect_to "/images/screenshot.jpg"
|
33
|
-
# redirect_to
|
34
|
-
# redirect_to :back
|
24
|
+
# redirect_to posts_url
|
35
25
|
# redirect_to proc { edit_post_url(@post) }
|
36
26
|
#
|
37
|
-
# The redirection happens as a
|
27
|
+
# The redirection happens as a <tt>302 Found</tt> header unless otherwise specified using the <tt>:status</tt> option:
|
38
28
|
#
|
39
29
|
# redirect_to post_url(@post), status: :found
|
40
30
|
# redirect_to action: 'atom', status: :moved_permanently
|
41
31
|
# redirect_to post_url(@post), status: 301
|
42
32
|
# redirect_to action: 'atom', status: 302
|
43
33
|
#
|
44
|
-
# The status code can either be a standard {HTTP Status code}[
|
34
|
+
# The status code can either be a standard {HTTP Status code}[https://www.iana.org/assignments/http-status-codes] as an
|
45
35
|
# integer, or a symbol representing the downcased, underscored and symbolized description.
|
46
36
|
# Note that the status code must be a 3xx HTTP code, or redirection will not occur.
|
47
37
|
#
|
48
38
|
# If you are using XHR requests other than GET or POST and redirecting after the
|
49
39
|
# request then some browsers will follow the redirect using the original request
|
50
40
|
# method. This may lead to undesirable behavior such as a double DELETE. To work
|
51
|
-
# around this
|
41
|
+
# around this you can return a <tt>303 See Other</tt> status code which will be
|
52
42
|
# followed using a GET request.
|
53
43
|
#
|
54
44
|
# redirect_to posts_url, status: :see_other
|
@@ -62,18 +52,45 @@ module ActionController
|
|
62
52
|
# redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
|
63
53
|
# redirect_to({ action: 'atom' }, alert: "Something serious happened")
|
64
54
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
def redirect_to(options = {}, response_status = {}) #:doc:
|
55
|
+
# Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
|
56
|
+
# To terminate the execution of the function immediately after the +redirect_to+, use return.
|
57
|
+
# redirect_to post_url(@post) and return
|
58
|
+
def redirect_to(options = {}, response_options = {})
|
70
59
|
raise ActionControllerError.new("Cannot redirect to nil!") unless options
|
71
|
-
raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
|
72
60
|
raise AbstractController::DoubleRenderError if response_body
|
73
61
|
|
74
|
-
self.status = _extract_redirect_to_status(options,
|
62
|
+
self.status = _extract_redirect_to_status(options, response_options)
|
75
63
|
self.location = _compute_redirect_to_location(request, options)
|
76
|
-
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
|
64
|
+
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Redirects the browser to the page that issued the request (the referrer)
|
68
|
+
# if possible, otherwise redirects to the provided default fallback
|
69
|
+
# location.
|
70
|
+
#
|
71
|
+
# The referrer information is pulled from the HTTP +Referer+ (sic) header on
|
72
|
+
# the request. This is an optional header and its presence on the request is
|
73
|
+
# subject to browser security settings and user preferences. If the request
|
74
|
+
# is missing this header, the <tt>fallback_location</tt> will be used.
|
75
|
+
#
|
76
|
+
# redirect_back fallback_location: { action: "show", id: 5 }
|
77
|
+
# redirect_back fallback_location: @post
|
78
|
+
# redirect_back fallback_location: "http://www.rubyonrails.org"
|
79
|
+
# redirect_back fallback_location: "/images/screenshot.jpg"
|
80
|
+
# redirect_back fallback_location: posts_url
|
81
|
+
# redirect_back fallback_location: proc { edit_post_url(@post) }
|
82
|
+
# redirect_back fallback_location: '/', allow_other_host: false
|
83
|
+
#
|
84
|
+
# ==== Options
|
85
|
+
# * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
|
86
|
+
# * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
|
87
|
+
#
|
88
|
+
# All other options that can be passed to #redirect_to are accepted as
|
89
|
+
# options and the behavior is identical.
|
90
|
+
def redirect_back(fallback_location:, allow_other_host: true, **args)
|
91
|
+
referer = request.headers["Referer"]
|
92
|
+
redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer))
|
93
|
+
redirect_to redirect_to_referer ? referer : fallback_location, **args
|
77
94
|
end
|
78
95
|
|
79
96
|
def _compute_redirect_to_location(request, options) #:nodoc:
|
@@ -81,16 +98,14 @@ module ActionController
|
|
81
98
|
# The scheme name consist of a letter followed by any combination of
|
82
99
|
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
|
83
100
|
# characters; and is terminated by a colon (":").
|
84
|
-
# See
|
101
|
+
# See https://tools.ietf.org/html/rfc3986#section-3.1
|
85
102
|
# The protocol relative scheme starts with a double slash "//".
|
86
103
|
when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
|
87
104
|
options
|
88
105
|
when String
|
89
106
|
request.protocol + request.host_with_port + options
|
90
|
-
when :back
|
91
|
-
request.headers["Referer"] or raise RedirectBackError
|
92
107
|
when Proc
|
93
|
-
_compute_redirect_to_location request, options
|
108
|
+
_compute_redirect_to_location request, instance_eval(&options)
|
94
109
|
else
|
95
110
|
url_for(options)
|
96
111
|
end.delete("\0\r\n")
|
@@ -99,14 +114,20 @@ module ActionController
|
|
99
114
|
public :_compute_redirect_to_location
|
100
115
|
|
101
116
|
private
|
102
|
-
def _extract_redirect_to_status(options,
|
117
|
+
def _extract_redirect_to_status(options, response_options)
|
103
118
|
if options.is_a?(Hash) && options.key?(:status)
|
104
119
|
Rack::Utils.status_code(options.delete(:status))
|
105
|
-
elsif
|
106
|
-
Rack::Utils.status_code(
|
120
|
+
elsif response_options.key?(:status)
|
121
|
+
Rack::Utils.status_code(response_options[:status])
|
107
122
|
else
|
108
123
|
302
|
109
124
|
end
|
110
125
|
end
|
126
|
+
|
127
|
+
def _url_host_allowed?(url)
|
128
|
+
URI(url.to_s).host == request.host
|
129
|
+
rescue ArgumentError, URI::Error
|
130
|
+
false
|
131
|
+
end
|
111
132
|
end
|
112
133
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
2
4
|
|
3
5
|
module ActionController
|
4
6
|
# See <tt>Renderers.add</tt>
|
@@ -11,6 +13,7 @@ module ActionController
|
|
11
13
|
Renderers.remove(key)
|
12
14
|
end
|
13
15
|
|
16
|
+
# See <tt>Responder#api_behavior</tt>
|
14
17
|
class MissingRenderer < LoadError
|
15
18
|
def initialize(format)
|
16
19
|
super "No renderer defined for format: #{format}"
|
@@ -20,40 +23,24 @@ module ActionController
|
|
20
23
|
module Renderers
|
21
24
|
extend ActiveSupport::Concern
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
26
|
+
# A Set containing renderer names that correspond to available renderer procs.
|
27
|
+
# Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
|
28
|
+
RENDERERS = Set.new
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
renderers = _renderers + args
|
31
|
-
self._renderers = renderers.freeze
|
32
|
-
end
|
33
|
-
alias use_renderer use_renderers
|
30
|
+
included do
|
31
|
+
class_attribute :_renderers, default: Set.new.freeze
|
34
32
|
end
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
# Used in <tt>ActionController::Base</tt>
|
35
|
+
# and <tt>ActionController::API</tt> to include all
|
36
|
+
# renderers by default.
|
37
|
+
module All
|
38
|
+
extend ActiveSupport::Concern
|
39
|
+
include Renderers
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
if options.key?(name)
|
43
|
-
_process_options(options)
|
44
|
-
method_name = Renderers._render_with_renderer_method_name(name)
|
45
|
-
return send(method_name, options.delete(name), options)
|
46
|
-
end
|
41
|
+
included do
|
42
|
+
self._renderers = RENDERERS
|
47
43
|
end
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
|
51
|
-
# A Set containing renderer names that correspond to available renderer procs.
|
52
|
-
# Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
|
53
|
-
RENDERERS = Set.new
|
54
|
-
|
55
|
-
def self._render_with_renderer_method_name(key)
|
56
|
-
"_render_with_renderer_#{key}"
|
57
44
|
end
|
58
45
|
|
59
46
|
# Adds a new renderer to call within controller actions.
|
@@ -68,11 +55,11 @@ module ActionController
|
|
68
55
|
# ActionController::Renderers.add :csv do |obj, options|
|
69
56
|
# filename = options[:filename] || 'data'
|
70
57
|
# str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
|
71
|
-
# send_data str, type: Mime
|
58
|
+
# send_data str, type: Mime[:csv],
|
72
59
|
# disposition: "attachment; filename=#{filename}.csv"
|
73
60
|
# end
|
74
61
|
#
|
75
|
-
# Note that we used Mime
|
62
|
+
# Note that we used Mime[:csv] for the csv mime type as it comes with Rails.
|
76
63
|
# For a custom renderer, you'll need to register a mime type with
|
77
64
|
# <tt>Mime::Type.register</tt>.
|
78
65
|
#
|
@@ -92,46 +79,102 @@ module ActionController
|
|
92
79
|
|
93
80
|
# This method is the opposite of add method.
|
94
81
|
#
|
95
|
-
#
|
82
|
+
# To remove a csv renderer:
|
96
83
|
#
|
97
84
|
# ActionController::Renderers.remove(:csv)
|
98
85
|
def self.remove(key)
|
99
86
|
RENDERERS.delete(key.to_sym)
|
100
87
|
method_name = _render_with_renderer_method_name(key)
|
101
|
-
|
88
|
+
remove_possible_method(method_name)
|
102
89
|
end
|
103
90
|
|
104
|
-
|
105
|
-
|
106
|
-
|
91
|
+
def self._render_with_renderer_method_name(key)
|
92
|
+
"_render_with_renderer_#{key}"
|
93
|
+
end
|
107
94
|
|
108
|
-
|
109
|
-
|
95
|
+
module ClassMethods
|
96
|
+
# Adds, by name, a renderer or renderers to the +_renderers+ available
|
97
|
+
# to call within controller actions.
|
98
|
+
#
|
99
|
+
# It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
|
100
|
+
# otherwise to add an available renderer proc to a specific controller.
|
101
|
+
#
|
102
|
+
# Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
|
103
|
+
# include <tt>ActionController::Renderers::All</tt>, making all renderers
|
104
|
+
# available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
|
105
|
+
#
|
106
|
+
# Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
|
107
|
+
# must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
|
108
|
+
# and <tt>ActionController::Renderers</tt>, and have at least one renderer.
|
109
|
+
#
|
110
|
+
# Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
|
111
|
+
# you may specify which renderers to include by passing the renderer name or names to
|
112
|
+
# +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
|
113
|
+
# (+_render_with_renderer_json+) might look like:
|
114
|
+
#
|
115
|
+
# class MetalRenderingController < ActionController::Metal
|
116
|
+
# include AbstractController::Rendering
|
117
|
+
# include ActionController::Rendering
|
118
|
+
# include ActionController::Renderers
|
119
|
+
#
|
120
|
+
# use_renderers :json
|
121
|
+
#
|
122
|
+
# def show
|
123
|
+
# render json: record
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# You must specify a +use_renderer+, else the +controller.renderer+ and
|
128
|
+
# +controller._renderers+ will be <tt>nil</tt>, and the action will fail.
|
129
|
+
def use_renderers(*args)
|
130
|
+
renderers = _renderers + args
|
131
|
+
self._renderers = renderers.freeze
|
110
132
|
end
|
133
|
+
alias use_renderer use_renderers
|
134
|
+
end
|
135
|
+
|
136
|
+
# Called by +render+ in <tt>AbstractController::Rendering</tt>
|
137
|
+
# which sets the return value as the +response_body+.
|
138
|
+
#
|
139
|
+
# If no renderer is found, +super+ returns control to
|
140
|
+
# <tt>ActionView::Rendering.render_to_body</tt>, if present.
|
141
|
+
def render_to_body(options)
|
142
|
+
_render_to_body_with_renderer(options) || super
|
143
|
+
end
|
144
|
+
|
145
|
+
def _render_to_body_with_renderer(options)
|
146
|
+
_renderers.each do |name|
|
147
|
+
if options.key?(name)
|
148
|
+
_process_options(options)
|
149
|
+
method_name = Renderers._render_with_renderer_method_name(name)
|
150
|
+
return send(method_name, options.delete(name), options)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
nil
|
111
154
|
end
|
112
155
|
|
113
156
|
add :json do |json, options|
|
114
157
|
json = json.to_json(options) unless json.kind_of?(String)
|
115
158
|
|
116
159
|
if options[:callback].present?
|
117
|
-
if
|
118
|
-
self.content_type = Mime
|
160
|
+
if media_type.nil? || media_type == Mime[:json]
|
161
|
+
self.content_type = Mime[:js]
|
119
162
|
end
|
120
163
|
|
121
164
|
"/**/#{options[:callback]}(#{json})"
|
122
165
|
else
|
123
|
-
self.content_type
|
166
|
+
self.content_type = Mime[:json] if media_type.nil?
|
124
167
|
json
|
125
168
|
end
|
126
169
|
end
|
127
170
|
|
128
171
|
add :js do |js, options|
|
129
|
-
self.content_type
|
172
|
+
self.content_type = Mime[:js] if media_type.nil?
|
130
173
|
js.respond_to?(:to_js) ? js.to_js(options) : js
|
131
174
|
end
|
132
175
|
|
133
176
|
add :xml do |xml, options|
|
134
|
-
self.content_type
|
177
|
+
self.content_type = Mime[:xml] if media_type.nil?
|
135
178
|
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
|
136
179
|
end
|
137
180
|
end
|