actionpack 6.1.4.1 → 7.0.0.rc2
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 +191 -378
- 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 +21 -7
- 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 +3 -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 +4 -3
- 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/exceptions.rb +19 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/helpers.rb +1 -1
- data/lib/action_controller/metal/http_authentication.rb +17 -16
- data/lib/action_controller/metal/instrumentation.rb +57 -52
- data/lib/action_controller/metal/live.rb +42 -2
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/params_wrapper.rb +20 -11
- data/lib/action_controller/metal/permissions_policy.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +86 -16
- data/lib/action_controller/metal/rendering.rb +7 -7
- data/lib/action_controller/metal/request_forgery_protection.rb +64 -24
- 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 +84 -47
- data/lib/action_controller/metal/testing.rb +0 -2
- data/lib/action_controller/metal.rb +7 -10
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/test_case.rb +19 -4
- data/lib/action_controller.rb +1 -5
- data/lib/action_dispatch/http/cache.rb +13 -6
- 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 +1 -13
- data/lib/action_dispatch/http/url.rb +11 -19
- 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 +6 -13
- 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/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 +8 -4
- 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/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +9 -11
- data/lib/action_dispatch/middleware/host_authorization.rb +44 -30
- data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +17 -9
- data/lib/action_dispatch/middleware/stack.rb +27 -9
- data/lib/action_dispatch/middleware/static.rb +2 -6
- 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 +4 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +3 -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/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/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +54 -78
- data/lib/action_dispatch/routing/redirection.rb +0 -2
- data/lib/action_dispatch/routing/route_set.rb +14 -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 +12 -6
- data/lib/action_dispatch/system_testing/browser.rb +2 -12
- data/lib/action_dispatch/system_testing/driver.rb +35 -11
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +10 -6
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- 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 +3 -26
- data/lib/action_dispatch.rb +2 -1
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +18 -16
@@ -4,11 +4,11 @@ require "rack/session/abstract/id"
|
|
4
4
|
require "action_controller/metal/exceptions"
|
5
5
|
require "active_support/security_utils"
|
6
6
|
|
7
|
-
module ActionController
|
8
|
-
class InvalidAuthenticityToken < ActionControllerError
|
7
|
+
module ActionController # :nodoc:
|
8
|
+
class InvalidAuthenticityToken < ActionControllerError # :nodoc:
|
9
9
|
end
|
10
10
|
|
11
|
-
class InvalidCrossOriginRequest < ActionControllerError
|
11
|
+
class InvalidCrossOriginRequest < ActionControllerError # :nodoc:
|
12
12
|
end
|
13
13
|
|
14
14
|
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
|
@@ -124,10 +124,26 @@ module ActionController #:nodoc:
|
|
124
124
|
# If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
|
125
125
|
# * <tt>:with</tt> - Set the method to handle unverified request.
|
126
126
|
#
|
127
|
-
#
|
127
|
+
# Built-in unverified request handling methods are:
|
128
128
|
# * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
|
129
129
|
# * <tt>:reset_session</tt> - Resets the session.
|
130
130
|
# * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
|
131
|
+
#
|
132
|
+
# You can also implement custom strategy classes for unverified request handling:
|
133
|
+
#
|
134
|
+
# class CustomStrategy
|
135
|
+
# def initialize(controller)
|
136
|
+
# @controller = controller
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# def handle_unverified_request
|
140
|
+
# # Custom behaviour for unverfied request
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# class ApplicationController < ActionController:x:Base
|
145
|
+
# protect_from_forgery with: CustomStrategy
|
146
|
+
# end
|
131
147
|
def protect_from_forgery(options = {})
|
132
148
|
options = options.reverse_merge(prepend: false)
|
133
149
|
|
@@ -148,9 +164,18 @@ module ActionController #:nodoc:
|
|
148
164
|
|
149
165
|
private
|
150
166
|
def protection_method_class(name)
|
151
|
-
|
152
|
-
|
153
|
-
|
167
|
+
case name
|
168
|
+
when :null_session
|
169
|
+
ProtectionMethods::NullSession
|
170
|
+
when :reset_session
|
171
|
+
ProtectionMethods::ResetSession
|
172
|
+
when :exception
|
173
|
+
ProtectionMethods::Exception
|
174
|
+
when Class
|
175
|
+
name
|
176
|
+
else
|
177
|
+
raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, :reset_session, or a custom forgery protection class."
|
178
|
+
end
|
154
179
|
end
|
155
180
|
end
|
156
181
|
|
@@ -170,7 +195,7 @@ module ActionController #:nodoc:
|
|
170
195
|
end
|
171
196
|
|
172
197
|
private
|
173
|
-
class NullSessionHash < Rack::Session::Abstract::SessionHash
|
198
|
+
class NullSessionHash < Rack::Session::Abstract::SessionHash # :nodoc:
|
174
199
|
def initialize(req)
|
175
200
|
super(nil, req)
|
176
201
|
@data = {}
|
@@ -183,9 +208,13 @@ module ActionController #:nodoc:
|
|
183
208
|
def exists?
|
184
209
|
true
|
185
210
|
end
|
211
|
+
|
212
|
+
def enabled?
|
213
|
+
false
|
214
|
+
end
|
186
215
|
end
|
187
216
|
|
188
|
-
class NullCookieJar < ActionDispatch::Cookies::CookieJar
|
217
|
+
class NullCookieJar < ActionDispatch::Cookies::CookieJar # :nodoc:
|
189
218
|
def write(*)
|
190
219
|
# nothing
|
191
220
|
end
|
@@ -203,12 +232,14 @@ module ActionController #:nodoc:
|
|
203
232
|
end
|
204
233
|
|
205
234
|
class Exception
|
235
|
+
attr_accessor :warning_message
|
236
|
+
|
206
237
|
def initialize(controller)
|
207
238
|
@controller = controller
|
208
239
|
end
|
209
240
|
|
210
241
|
def handle_unverified_request
|
211
|
-
raise ActionController::InvalidAuthenticityToken
|
242
|
+
raise ActionController::InvalidAuthenticityToken, warning_message
|
212
243
|
end
|
213
244
|
end
|
214
245
|
end
|
@@ -228,22 +259,31 @@ module ActionController #:nodoc:
|
|
228
259
|
mark_for_same_origin_verification!
|
229
260
|
|
230
261
|
if !verified_request?
|
231
|
-
if logger && log_warning_on_csrf_failure
|
232
|
-
|
233
|
-
logger.warn "Can't verify CSRF token authenticity."
|
234
|
-
else
|
235
|
-
logger.warn "HTTP Origin header (#{request.origin}) didn't match request.base_url (#{request.base_url})"
|
236
|
-
end
|
237
|
-
end
|
262
|
+
logger.warn unverified_request_warning_message if logger && log_warning_on_csrf_failure
|
263
|
+
|
238
264
|
handle_unverified_request
|
239
265
|
end
|
240
266
|
end
|
241
267
|
|
242
268
|
def handle_unverified_request # :doc:
|
243
|
-
forgery_protection_strategy.new(self)
|
269
|
+
protection_strategy = forgery_protection_strategy.new(self)
|
270
|
+
|
271
|
+
if protection_strategy.respond_to?(:warning_message)
|
272
|
+
protection_strategy.warning_message = unverified_request_warning_message
|
273
|
+
end
|
274
|
+
|
275
|
+
protection_strategy.handle_unverified_request
|
276
|
+
end
|
277
|
+
|
278
|
+
def unverified_request_warning_message # :nodoc:
|
279
|
+
if valid_request_origin?
|
280
|
+
"Can't verify CSRF token authenticity."
|
281
|
+
else
|
282
|
+
"HTTP Origin header (#{request.origin}) didn't match request.base_url (#{request.base_url})"
|
283
|
+
end
|
244
284
|
end
|
245
285
|
|
246
|
-
|
286
|
+
# :nodoc:
|
247
287
|
CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
|
248
288
|
"<script> tag on another site requested protected JavaScript. " \
|
249
289
|
"If you know what you're doing, go ahead and disable forgery " \
|
@@ -303,15 +343,15 @@ module ActionController #:nodoc:
|
|
303
343
|
[form_authenticity_param, request.x_csrf_token]
|
304
344
|
end
|
305
345
|
|
306
|
-
#
|
307
|
-
def form_authenticity_token(form_options: {})
|
346
|
+
# Creates the authenticity token for the current request.
|
347
|
+
def form_authenticity_token(form_options: {}) # :doc:
|
308
348
|
masked_authenticity_token(session, form_options: form_options)
|
309
349
|
end
|
310
350
|
|
311
351
|
# Creates a masked version of the authenticity token that varies
|
312
352
|
# on each request. The masking is used to mitigate SSL attacks
|
313
353
|
# like BREACH.
|
314
|
-
def masked_authenticity_token(session, form_options: {})
|
354
|
+
def masked_authenticity_token(session, form_options: {})
|
315
355
|
action, method = form_options.values_at(:action, :method)
|
316
356
|
|
317
357
|
raw_token = if per_form_csrf_tokens && action && method
|
@@ -438,7 +478,7 @@ module ActionController #:nodoc:
|
|
438
478
|
|
439
479
|
# Checks if the controller allows forgery protection.
|
440
480
|
def protect_against_forgery? # :doc:
|
441
|
-
allow_forgery_protection
|
481
|
+
allow_forgery_protection && (!session.respond_to?(:enabled?) || session.enabled?)
|
442
482
|
end
|
443
483
|
|
444
484
|
NULL_ORIGIN_MESSAGE = <<~MSG
|
@@ -469,7 +509,7 @@ module ActionController #:nodoc:
|
|
469
509
|
|
470
510
|
def generate_csrf_token # :nodoc:
|
471
511
|
if urlsafe_csrf_tokens
|
472
|
-
SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH
|
512
|
+
SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH)
|
473
513
|
else
|
474
514
|
SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
|
475
515
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "rack/chunked"
|
4
4
|
|
5
|
-
module ActionController
|
5
|
+
module ActionController # :nodoc:
|
6
6
|
# Allows views to be streamed back to the client as they are rendered.
|
7
7
|
#
|
8
8
|
# By default, Rails renders views by first rendering the template
|
@@ -193,8 +193,6 @@ module ActionController #:nodoc:
|
|
193
193
|
# To be described.
|
194
194
|
#
|
195
195
|
module Streaming
|
196
|
-
extend ActiveSupport::Concern
|
197
|
-
|
198
196
|
private
|
199
197
|
# Set proper cache control and transfer encoding when streaming
|
200
198
|
def _process_options(options)
|
@@ -27,28 +27,13 @@ module ActionController
|
|
27
27
|
super("param is missing or the value is empty: #{param}")
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
@error = error
|
33
|
-
end
|
34
|
-
|
35
|
-
def corrections
|
36
|
-
if @error.param && @error.keys
|
37
|
-
maybe_these = @error.keys
|
30
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
31
|
+
include DidYouMean::Correctable # :nodoc:
|
38
32
|
|
39
|
-
|
40
|
-
|
41
|
-
}.reverse.first(4)
|
42
|
-
else
|
43
|
-
[]
|
44
|
-
end
|
33
|
+
def corrections # :nodoc:
|
34
|
+
@corrections ||= DidYouMean::SpellChecker.new(dictionary: keys).correct(param.to_s)
|
45
35
|
end
|
46
36
|
end
|
47
|
-
|
48
|
-
# We may not have DYM, and DYM might not let us register error handlers
|
49
|
-
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
50
|
-
DidYouMean.correct_error(self, Correction)
|
51
|
-
end
|
52
37
|
end
|
53
38
|
|
54
39
|
# Raised when a supplied parameter is not expected and
|
@@ -96,7 +81,7 @@ module ActionController
|
|
96
81
|
# })
|
97
82
|
#
|
98
83
|
# permitted = params.require(:person).permit(:name, :age)
|
99
|
-
# permitted # =>
|
84
|
+
# permitted # => #<ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
|
100
85
|
# permitted.permitted? # => true
|
101
86
|
#
|
102
87
|
# Person.first.update!(permitted)
|
@@ -106,11 +91,13 @@ module ActionController
|
|
106
91
|
#
|
107
92
|
# * +permit_all_parameters+ - If it's +true+, all the parameters will be
|
108
93
|
# permitted by default. The default is +false+.
|
109
|
-
# * +action_on_unpermitted_parameters+ -
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
94
|
+
# * +action_on_unpermitted_parameters+ - Controls behavior when parameters that are not explicitly
|
95
|
+
# permitted are found. The default value is <tt>:log</tt> in test and development environments,
|
96
|
+
# +false+ otherwise. The values can be:
|
97
|
+
# * +false+ to take no action.
|
98
|
+
# * <tt>:log</tt> to emit an <tt>ActiveSupport::Notifications.instrument</tt> event on the
|
99
|
+
# <tt>unpermitted_parameters.action_controller</tt> topic and log at the DEBUG level.
|
100
|
+
# * <tt>:raise</tt> to raise a <tt>ActionController::UnpermittedParameters</tt> exception.
|
114
101
|
#
|
115
102
|
# Examples:
|
116
103
|
#
|
@@ -124,7 +111,7 @@ module ActionController
|
|
124
111
|
#
|
125
112
|
# params = ActionController::Parameters.new(a: "123", b: "456")
|
126
113
|
# params.permit(:c)
|
127
|
-
# # =>
|
114
|
+
# # => #<ActionController::Parameters {} permitted: true>
|
128
115
|
#
|
129
116
|
# ActionController::Parameters.action_on_unpermitted_parameters = :raise
|
130
117
|
#
|
@@ -277,8 +264,9 @@ module ActionController
|
|
277
264
|
# params = ActionController::Parameters.new(name: "Francesco")
|
278
265
|
# params.permitted? # => true
|
279
266
|
# Person.new(params) # => #<Person id: nil, name: "Francesco">
|
280
|
-
def initialize(parameters = {})
|
267
|
+
def initialize(parameters = {}, logging_context = {})
|
281
268
|
@parameters = parameters.with_indifferent_access
|
269
|
+
@logging_context = logging_context
|
282
270
|
@permitted = self.class.permit_all_parameters
|
283
271
|
end
|
284
272
|
|
@@ -454,7 +442,7 @@ module ActionController
|
|
454
442
|
# either present or the singleton +false+, returns said value:
|
455
443
|
#
|
456
444
|
# ActionController::Parameters.new(person: { name: "Francesco" }).require(:person)
|
457
|
-
# # =>
|
445
|
+
# # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
458
446
|
#
|
459
447
|
# Otherwise raises <tt>ActionController::ParameterMissing</tt>:
|
460
448
|
#
|
@@ -582,13 +570,48 @@ module ActionController
|
|
582
570
|
# })
|
583
571
|
#
|
584
572
|
# params.require(:person).permit(:contact)
|
585
|
-
# # =>
|
573
|
+
# # => #<ActionController::Parameters {} permitted: true>
|
586
574
|
#
|
587
575
|
# params.require(:person).permit(contact: :phone)
|
588
|
-
# # =>
|
576
|
+
# # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
|
589
577
|
#
|
590
578
|
# params.require(:person).permit(contact: [ :email, :phone ])
|
591
|
-
# # =>
|
579
|
+
# # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
|
580
|
+
#
|
581
|
+
# If your parameters specify multiple parameters indexed by a number,
|
582
|
+
# you can permit each set of parameters under the numeric key to be the same using the same syntax as permitting a single item.
|
583
|
+
#
|
584
|
+
# params = ActionController::Parameters.new({
|
585
|
+
# person: {
|
586
|
+
# '0': {
|
587
|
+
# email: "none@test.com",
|
588
|
+
# phone: "555-1234"
|
589
|
+
# },
|
590
|
+
# '1': {
|
591
|
+
# email: "nothing@test.com",
|
592
|
+
# phone: "555-6789"
|
593
|
+
# },
|
594
|
+
# }
|
595
|
+
# })
|
596
|
+
# params.permit(person: [:email]).to_h
|
597
|
+
# # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"email"=>"nothing@test.com"}}}
|
598
|
+
#
|
599
|
+
# If you want to specify what keys you want from each numeric key, you can instead specify each one individually
|
600
|
+
#
|
601
|
+
# params = ActionController::Parameters.new({
|
602
|
+
# person: {
|
603
|
+
# '0': {
|
604
|
+
# email: "none@test.com",
|
605
|
+
# phone: "555-1234"
|
606
|
+
# },
|
607
|
+
# '1': {
|
608
|
+
# email: "nothing@test.com",
|
609
|
+
# phone: "555-6789"
|
610
|
+
# },
|
611
|
+
# }
|
612
|
+
# })
|
613
|
+
# params.permit(person: { '0': [:email], '1': [:phone]}).to_h
|
614
|
+
# # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"phone"=>"555-6789"}}}
|
592
615
|
def permit(*filters)
|
593
616
|
params = self.class.new
|
594
617
|
|
@@ -610,7 +633,7 @@ module ActionController
|
|
610
633
|
# returns +nil+.
|
611
634
|
#
|
612
635
|
# params = ActionController::Parameters.new(person: { name: "Francesco" })
|
613
|
-
# params[:person] # =>
|
636
|
+
# params[:person] # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
614
637
|
# params[:none] # => nil
|
615
638
|
def [](key)
|
616
639
|
convert_hashes_to_parameters(key, @parameters[key])
|
@@ -630,9 +653,9 @@ module ActionController
|
|
630
653
|
# is given, then that will be run and its result returned.
|
631
654
|
#
|
632
655
|
# params = ActionController::Parameters.new(person: { name: "Francesco" })
|
633
|
-
# params.fetch(:person) # =>
|
656
|
+
# params.fetch(:person) # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
634
657
|
# params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
|
635
|
-
# params.fetch(:none, {}) # =>
|
658
|
+
# params.fetch(:none, {}) # => #<ActionController::Parameters {} permitted: false>
|
636
659
|
# params.fetch(:none, "Francesco") # => "Francesco"
|
637
660
|
# params.fetch(:none) { "Francesco" } # => "Francesco"
|
638
661
|
def fetch(key, *args)
|
@@ -666,8 +689,8 @@ module ActionController
|
|
666
689
|
# don't exist, returns an empty hash.
|
667
690
|
#
|
668
691
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
669
|
-
# params.slice(:a, :b) # =>
|
670
|
-
# params.slice(:d) # =>
|
692
|
+
# params.slice(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
|
693
|
+
# params.slice(:d) # => #<ActionController::Parameters {} permitted: false>
|
671
694
|
def slice(*keys)
|
672
695
|
new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
|
673
696
|
end
|
@@ -683,8 +706,8 @@ module ActionController
|
|
683
706
|
# filters out the given +keys+.
|
684
707
|
#
|
685
708
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
686
|
-
# params.except(:a, :b) # =>
|
687
|
-
# params.except(:d) # =>
|
709
|
+
# params.except(:a, :b) # => #<ActionController::Parameters {"c"=>3} permitted: false>
|
710
|
+
# params.except(:d) # => #<ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
|
688
711
|
def except(*keys)
|
689
712
|
new_instance_with_inherited_permitted_status(@parameters.except(*keys))
|
690
713
|
end
|
@@ -692,8 +715,8 @@ module ActionController
|
|
692
715
|
# Removes and returns the key/value pairs matching the given keys.
|
693
716
|
#
|
694
717
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
695
|
-
# params.extract!(:a, :b) # =>
|
696
|
-
# params # =>
|
718
|
+
# params.extract!(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
|
719
|
+
# params # => #<ActionController::Parameters {"c"=>3} permitted: false>
|
697
720
|
def extract!(*keys)
|
698
721
|
new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
|
699
722
|
end
|
@@ -703,7 +726,7 @@ module ActionController
|
|
703
726
|
#
|
704
727
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
705
728
|
# params.transform_values { |x| x * 2 }
|
706
|
-
# # =>
|
729
|
+
# # => #<ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
|
707
730
|
def transform_values
|
708
731
|
return to_enum(:transform_values) unless block_given?
|
709
732
|
new_instance_with_inherited_permitted_status(
|
@@ -949,12 +972,18 @@ module ActionController
|
|
949
972
|
end
|
950
973
|
end
|
951
974
|
|
952
|
-
def
|
975
|
+
def specify_numeric_keys?(filter)
|
976
|
+
if filter.respond_to?(:keys)
|
977
|
+
filter.keys.any? { |key| /\A-?\d+\z/.match?(key) }
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
981
|
+
def each_element(object, filter, &block)
|
953
982
|
case object
|
954
983
|
when Array
|
955
|
-
object.grep(Parameters).
|
984
|
+
object.grep(Parameters).filter_map(&block)
|
956
985
|
when Parameters
|
957
|
-
if object.nested_attributes?
|
986
|
+
if object.nested_attributes? && !specify_numeric_keys?(filter)
|
958
987
|
object.each_nested_attribute(&block)
|
959
988
|
else
|
960
989
|
yield object
|
@@ -968,7 +997,7 @@ module ActionController
|
|
968
997
|
case self.class.action_on_unpermitted_parameters
|
969
998
|
when :log
|
970
999
|
name = "unpermitted_parameters.action_controller"
|
971
|
-
ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys)
|
1000
|
+
ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys, context: @logging_context)
|
972
1001
|
when :raise
|
973
1002
|
raise ActionController::UnpermittedParameters.new(unpermitted_keys)
|
974
1003
|
end
|
@@ -1067,7 +1096,7 @@ module ActionController
|
|
1067
1096
|
end
|
1068
1097
|
elsif non_scalar?(value)
|
1069
1098
|
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
|
1070
|
-
params[key] = each_element(value) do |element|
|
1099
|
+
params[key] = each_element(value, filter[key]) do |element|
|
1071
1100
|
element.permit(*Array.wrap(filter[key]))
|
1072
1101
|
end
|
1073
1102
|
end
|
@@ -1184,7 +1213,15 @@ module ActionController
|
|
1184
1213
|
# Returns a new ActionController::Parameters object that
|
1185
1214
|
# has been instantiated with the <tt>request.parameters</tt>.
|
1186
1215
|
def params
|
1187
|
-
@_params ||=
|
1216
|
+
@_params ||= begin
|
1217
|
+
context = {
|
1218
|
+
controller: self.class.name,
|
1219
|
+
action: action_name,
|
1220
|
+
request: request,
|
1221
|
+
params: request.filtered_parameters
|
1222
|
+
}
|
1223
|
+
Parameters.new(request.parameters, context)
|
1224
|
+
end
|
1188
1225
|
end
|
1189
1226
|
|
1190
1227
|
# Assigns the given +value+ to the +params+ hash. If +value+
|
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/array/extract_options"
|
4
4
|
require "action_dispatch/middleware/stack"
|
5
|
-
require "action_dispatch/http/request"
|
6
|
-
require "action_dispatch/http/response"
|
7
5
|
|
8
6
|
module ActionController
|
9
7
|
# Extend ActionDispatch middleware stack to make it aware of options
|
@@ -13,8 +11,8 @@ module ActionController
|
|
13
11
|
# use AuthenticationMiddleware, except: [:index, :show]
|
14
12
|
# end
|
15
13
|
#
|
16
|
-
class MiddlewareStack < ActionDispatch::MiddlewareStack
|
17
|
-
class Middleware < ActionDispatch::MiddlewareStack::Middleware
|
14
|
+
class MiddlewareStack < ActionDispatch::MiddlewareStack # :nodoc:
|
15
|
+
class Middleware < ActionDispatch::MiddlewareStack::Middleware # :nodoc:
|
18
16
|
def initialize(klass, args, actions, strategy, block)
|
19
17
|
@actions = actions
|
20
18
|
@strategy = strategy
|
@@ -184,7 +182,7 @@ module ActionController
|
|
184
182
|
response_body || response.committed?
|
185
183
|
end
|
186
184
|
|
187
|
-
def dispatch(name, request, response)
|
185
|
+
def dispatch(name, request, response) # :nodoc:
|
188
186
|
set_request!(request)
|
189
187
|
set_response!(response)
|
190
188
|
process(name)
|
@@ -196,12 +194,12 @@ module ActionController
|
|
196
194
|
@_response = response
|
197
195
|
end
|
198
196
|
|
199
|
-
def set_request!(request)
|
197
|
+
def set_request!(request) # :nodoc:
|
200
198
|
@_request = request
|
201
199
|
@_request.controller_instance = self
|
202
200
|
end
|
203
201
|
|
204
|
-
def to_a
|
202
|
+
def to_a # :nodoc:
|
205
203
|
response.to_a
|
206
204
|
end
|
207
205
|
|
@@ -219,10 +217,9 @@ module ActionController
|
|
219
217
|
class << self
|
220
218
|
# Pushes the given Rack middleware and its arguments to the bottom of the
|
221
219
|
# middleware stack.
|
222
|
-
def use(
|
223
|
-
middleware_stack.use(
|
220
|
+
def use(...)
|
221
|
+
middleware_stack.use(...)
|
224
222
|
end
|
225
|
-
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
226
223
|
end
|
227
224
|
|
228
225
|
# Alias for +middleware_stack+.
|
@@ -8,8 +8,11 @@ require "action_controller/railties/helpers"
|
|
8
8
|
require "action_view/railtie"
|
9
9
|
|
10
10
|
module ActionController
|
11
|
-
class Railtie < Rails::Railtie
|
11
|
+
class Railtie < Rails::Railtie # :nodoc:
|
12
12
|
config.action_controller = ActiveSupport::OrderedOptions.new
|
13
|
+
config.action_controller.raise_on_open_redirects = false
|
14
|
+
config.action_controller.log_query_tags_around_actions = true
|
15
|
+
config.action_controller.wrap_parameters_by_default = false
|
13
16
|
|
14
17
|
config.eager_load_namespaces << ActionController
|
15
18
|
|
@@ -25,14 +28,19 @@ module ActionController
|
|
25
28
|
options = app.config.action_controller
|
26
29
|
|
27
30
|
ActiveSupport.on_load(:action_controller, run_once: true) do
|
28
|
-
ActionController::Parameters.permit_all_parameters = options.
|
31
|
+
ActionController::Parameters.permit_all_parameters = options.permit_all_parameters || false
|
29
32
|
if app.config.action_controller[:always_permitted_parameters]
|
30
33
|
ActionController::Parameters.always_permitted_parameters =
|
31
|
-
app.config.action_controller.
|
34
|
+
app.config.action_controller.always_permitted_parameters
|
32
35
|
end
|
33
|
-
|
34
|
-
|
36
|
+
|
37
|
+
action_on_unpermitted_parameters = options.action_on_unpermitted_parameters
|
38
|
+
|
39
|
+
if action_on_unpermitted_parameters.nil?
|
40
|
+
action_on_unpermitted_parameters = (Rails.env.test? || Rails.env.development?) ? :log : false
|
35
41
|
end
|
42
|
+
|
43
|
+
ActionController::Parameters.action_on_unpermitted_parameters = action_on_unpermitted_parameters
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
@@ -55,7 +63,18 @@ module ActionController
|
|
55
63
|
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
56
64
|
extend ::ActionController::Railties::Helpers
|
57
65
|
|
58
|
-
options.
|
66
|
+
wrap_parameters format: [:json] if options.wrap_parameters_by_default && respond_to?(:wrap_parameters)
|
67
|
+
|
68
|
+
# Configs used in other initializers
|
69
|
+
filtered_options = options.except(
|
70
|
+
:log_query_tags_around_actions,
|
71
|
+
:permit_all_parameters,
|
72
|
+
:action_on_unpermitted_parameters,
|
73
|
+
:always_permitted_parameters,
|
74
|
+
:wrap_parameters_by_default
|
75
|
+
)
|
76
|
+
|
77
|
+
filtered_options.each do |k, v|
|
59
78
|
k = "#{k}="
|
60
79
|
if respond_to?(k)
|
61
80
|
send(k, v)
|
@@ -85,5 +104,29 @@ module ActionController
|
|
85
104
|
ActionController::Metal.descendants.each(&:action_methods) if config.eager_load
|
86
105
|
end
|
87
106
|
end
|
107
|
+
|
108
|
+
initializer "action_controller.query_log_tags" do |app|
|
109
|
+
query_logs_tags_enabled = app.config.respond_to?(:active_record) &&
|
110
|
+
app.config.active_record.query_log_tags_enabled &&
|
111
|
+
app.config.action_controller.log_query_tags_around_actions
|
112
|
+
|
113
|
+
if query_logs_tags_enabled
|
114
|
+
app.config.active_record.query_log_tags += [:controller, :action]
|
115
|
+
|
116
|
+
ActiveSupport.on_load(:active_record) do
|
117
|
+
ActiveRecord::QueryLogs.taggings.merge!(
|
118
|
+
controller: ->(context) { context[:controller]&.controller_name },
|
119
|
+
action: ->(context) { context[:controller]&.action_name },
|
120
|
+
namespaced_controller: ->(context) { context[:controller].class.name if context[:controller] }
|
121
|
+
)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
initializer "action_controller.test_case" do |app|
|
127
|
+
ActiveSupport.on_load(:action_controller_test_case) do
|
128
|
+
ActionController::TestCase.executor_around_each_request = app.config.active_support.executor_around_test_case
|
129
|
+
end
|
130
|
+
end
|
88
131
|
end
|
89
132
|
end
|