actionpack 6.1.4 → 7.0.0.rc1

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.

Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +189 -372
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +7 -21
  7. data/lib/abstract_controller/caching/fragments.rb +2 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +21 -7
  10. data/lib/abstract_controller/collector.rb +4 -2
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +3 -2
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/translation.rb +3 -2
  16. data/lib/abstract_controller/url_for.rb +4 -6
  17. data/lib/action_controller/api.rb +1 -1
  18. data/lib/action_controller/log_subscriber.rb +4 -3
  19. data/lib/action_controller/metal/conditional_get.rb +38 -1
  20. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  21. data/lib/action_controller/metal/cookies.rb +1 -1
  22. data/lib/action_controller/metal/data_streaming.rb +5 -13
  23. data/lib/action_controller/metal/exceptions.rb +19 -30
  24. data/lib/action_controller/metal/flash.rb +6 -2
  25. data/lib/action_controller/metal/helpers.rb +1 -1
  26. data/lib/action_controller/metal/http_authentication.rb +17 -16
  27. data/lib/action_controller/metal/instrumentation.rb +57 -52
  28. data/lib/action_controller/metal/live.rb +42 -2
  29. data/lib/action_controller/metal/mime_responds.rb +3 -3
  30. data/lib/action_controller/metal/params_wrapper.rb +20 -11
  31. data/lib/action_controller/metal/permissions_policy.rb +1 -1
  32. data/lib/action_controller/metal/redirecting.rb +86 -16
  33. data/lib/action_controller/metal/rendering.rb +7 -7
  34. data/lib/action_controller/metal/request_forgery_protection.rb +64 -24
  35. data/lib/action_controller/metal/rescue.rb +1 -1
  36. data/lib/action_controller/metal/streaming.rb +1 -3
  37. data/lib/action_controller/metal/strong_parameters.rb +84 -47
  38. data/lib/action_controller/metal/testing.rb +0 -2
  39. data/lib/action_controller/metal.rb +7 -10
  40. data/lib/action_controller/railtie.rb +49 -6
  41. data/lib/action_controller/test_case.rb +19 -4
  42. data/lib/action_controller.rb +1 -5
  43. data/lib/action_dispatch/http/cache.rb +13 -6
  44. data/lib/action_dispatch/http/content_security_policy.rb +39 -35
  45. data/lib/action_dispatch/http/filter_parameters.rb +5 -0
  46. data/lib/action_dispatch/http/mime_negotiation.rb +13 -3
  47. data/lib/action_dispatch/http/mime_type.rb +9 -11
  48. data/lib/action_dispatch/http/parameters.rb +4 -4
  49. data/lib/action_dispatch/http/permissions_policy.rb +1 -1
  50. data/lib/action_dispatch/http/request.rb +10 -19
  51. data/lib/action_dispatch/http/response.rb +1 -13
  52. data/lib/action_dispatch/http/url.rb +11 -19
  53. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  54. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  55. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  56. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  57. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  58. data/lib/action_dispatch/journey/route.rb +6 -13
  59. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  60. data/lib/action_dispatch/journey/router.rb +1 -1
  61. data/lib/action_dispatch/journey/routes.rb +3 -3
  62. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  63. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  64. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  65. data/lib/action_dispatch/middleware/cookies.rb +8 -4
  66. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  67. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  68. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  69. data/lib/action_dispatch/middleware/executor.rb +3 -0
  70. data/lib/action_dispatch/middleware/flash.rb +9 -11
  71. data/lib/action_dispatch/middleware/host_authorization.rb +49 -37
  72. data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
  73. data/lib/action_dispatch/middleware/server_timing.rb +33 -0
  74. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  75. data/lib/action_dispatch/middleware/show_exceptions.rb +17 -9
  76. data/lib/action_dispatch/middleware/stack.rb +27 -9
  77. data/lib/action_dispatch/middleware/static.rb +2 -6
  78. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  79. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  80. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  81. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +4 -3
  82. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +3 -1
  83. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  84. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  85. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  86. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  87. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  88. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  89. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  90. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  91. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
  92. data/lib/action_dispatch/railtie.rb +8 -2
  93. data/lib/action_dispatch/request/session.rb +43 -13
  94. data/lib/action_dispatch/routing/inspector.rb +1 -1
  95. data/lib/action_dispatch/routing/mapper.rb +54 -78
  96. data/lib/action_dispatch/routing/redirection.rb +0 -2
  97. data/lib/action_dispatch/routing/route_set.rb +14 -6
  98. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  99. data/lib/action_dispatch/routing/url_for.rb +1 -2
  100. data/lib/action_dispatch/routing.rb +2 -2
  101. data/lib/action_dispatch/system_test_case.rb +12 -6
  102. data/lib/action_dispatch/system_testing/browser.rb +2 -12
  103. data/lib/action_dispatch/system_testing/driver.rb +35 -11
  104. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +10 -6
  105. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  106. data/lib/action_dispatch/testing/assertions.rb +2 -5
  107. data/lib/action_dispatch/testing/integration.rb +6 -8
  108. data/lib/action_dispatch/testing/test_process.rb +3 -26
  109. data/lib/action_dispatch.rb +2 -1
  110. data/lib/action_pack/gem_version.rb +4 -4
  111. data/lib/action_pack.rb +1 -1
  112. metadata +19 -17
@@ -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 #:nodoc:
8
- class InvalidAuthenticityToken < ActionControllerError #:nodoc:
7
+ module ActionController # :nodoc:
8
+ class InvalidAuthenticityToken < ActionControllerError # :nodoc:
9
9
  end
10
10
 
11
- class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
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
- # Valid unverified request handling methods are:
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
- ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
152
- rescue NameError
153
- raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, or :reset_session"
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 #:nodoc:
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 #:nodoc:
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
- if valid_request_origin?
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).handle_unverified_request
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
- #:nodoc:
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
- # Sets the token value for the current session.
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: {}) # :doc:
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, padding: false)
512
+ SecureRandom.urlsafe_base64(AUTHENTICITY_TOKEN_LENGTH)
473
513
  else
474
514
  SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
475
515
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActionController #:nodoc:
3
+ module ActionController # :nodoc:
4
4
  # This module is responsible for providing +rescue_from+ helpers
5
5
  # to controllers and configuring when detailed exceptions must be
6
6
  # shown.
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "rack/chunked"
4
4
 
5
- module ActionController #:nodoc:
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
- class Correction
31
- def initialize(error)
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
- maybe_these.sort_by { |n|
40
- DidYouMean::Jaro.distance(@error.param.to_s, n)
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 # => <ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
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+ - Allow to control the behavior when parameters
110
- # that are not explicitly permitted are found. The values can be +false+ to just filter them
111
- # out, <tt>:log</tt> to additionally write a message on the logger, or <tt>:raise</tt> to raise
112
- # ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt>
113
- # in test and development environments, +false+ otherwise.
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
- # # => <ActionController::Parameters {} permitted: true>
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
- # # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
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
- # # => <ActionController::Parameters {} permitted: true>
573
+ # # => #<ActionController::Parameters {} permitted: true>
586
574
  #
587
575
  # params.require(:person).permit(contact: :phone)
588
- # # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
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
- # # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
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] # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
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) # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
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, {}) # => <ActionController::Parameters {} permitted: false>
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) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
670
- # params.slice(:d) # => <ActionController::Parameters {} permitted: false>
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) # => <ActionController::Parameters {"c"=>3} permitted: false>
687
- # params.except(:d) # => <ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
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) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
696
- # params # => <ActionController::Parameters {"c"=>3} permitted: false>
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
- # # => <ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
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 each_element(object, &block)
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).map { |el| yield el }.compact
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 ||= Parameters.new(request.parameters)
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
  module ActionController
4
4
  module Testing
5
- extend ActiveSupport::Concern
6
-
7
5
  # Behavior specific to functional tests
8
6
  module Functional # :nodoc:
9
7
  def recycle!
@@ -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 #:nodoc:
17
- class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
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) #:nodoc:
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) #:nodoc:
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 #:nodoc:
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(*args, &block)
223
- middleware_stack.use(*args, &block)
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 #:nodoc:
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.delete(:permit_all_parameters) { false }
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.delete(:always_permitted_parameters)
34
+ app.config.action_controller.always_permitted_parameters
32
35
  end
33
- ActionController::Parameters.action_on_unpermitted_parameters = options.delete(:action_on_unpermitted_parameters) do
34
- (Rails.env.test? || Rails.env.development?) ? :log : false
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.each do |k, v|
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