actionpack 5.2.1 → 7.0.2.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -220
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +24 -4
  7. data/lib/abstract_controller/caching/fragments.rb +8 -24
  8. data/lib/abstract_controller/caching.rb +2 -2
  9. data/lib/abstract_controller/callbacks.rb +34 -8
  10. data/lib/abstract_controller/collector.rb +5 -4
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +107 -90
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
  15. data/lib/abstract_controller/rendering.rb +9 -9
  16. data/lib/abstract_controller/translation.rb +12 -5
  17. data/lib/abstract_controller/url_for.rb +4 -6
  18. data/lib/abstract_controller.rb +2 -0
  19. data/lib/action_controller/api.rb +5 -4
  20. data/lib/action_controller/base.rb +6 -9
  21. data/lib/action_controller/caching.rb +1 -3
  22. data/lib/action_controller/log_subscriber.rb +13 -9
  23. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  24. data/lib/action_controller/metal/conditional_get.rb +57 -6
  25. data/lib/action_controller/metal/content_security_policy.rb +2 -3
  26. data/lib/action_controller/metal/cookies.rb +4 -2
  27. data/lib/action_controller/metal/data_streaming.rb +9 -18
  28. data/lib/action_controller/metal/default_headers.rb +17 -0
  29. data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
  30. data/lib/action_controller/metal/exceptions.rb +55 -12
  31. data/lib/action_controller/metal/flash.rb +10 -6
  32. data/lib/action_controller/metal/head.rb +7 -4
  33. data/lib/action_controller/metal/helpers.rb +15 -6
  34. data/lib/action_controller/metal/http_authentication.rb +41 -39
  35. data/lib/action_controller/metal/implicit_render.rb +5 -15
  36. data/lib/action_controller/metal/instrumentation.rb +59 -55
  37. data/lib/action_controller/metal/live.rb +80 -33
  38. data/lib/action_controller/metal/logging.rb +20 -0
  39. data/lib/action_controller/metal/mime_responds.rb +22 -7
  40. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  41. data/lib/action_controller/metal/params_wrapper.rb +50 -31
  42. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  43. data/lib/action_controller/metal/redirecting.rb +93 -23
  44. data/lib/action_controller/metal/renderers.rb +4 -4
  45. data/lib/action_controller/metal/rendering.rb +14 -9
  46. data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
  47. data/lib/action_controller/metal/rescue.rb +2 -2
  48. data/lib/action_controller/metal/streaming.rb +1 -4
  49. data/lib/action_controller/metal/strong_parameters.rb +236 -88
  50. data/lib/action_controller/metal/testing.rb +9 -2
  51. data/lib/action_controller/metal/url_for.rb +1 -1
  52. data/lib/action_controller/metal.rb +16 -17
  53. data/lib/action_controller/railtie.rb +49 -6
  54. data/lib/action_controller/railties/helpers.rb +1 -1
  55. data/lib/action_controller/renderer.rb +37 -13
  56. data/lib/action_controller/template_assertions.rb +1 -1
  57. data/lib/action_controller/test_case.rb +98 -68
  58. data/lib/action_controller.rb +4 -5
  59. data/lib/action_dispatch/http/cache.rb +45 -32
  60. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  61. data/lib/action_dispatch/http/content_security_policy.rb +69 -56
  62. data/lib/action_dispatch/http/filter_parameters.rb +14 -8
  63. data/lib/action_dispatch/http/filter_redirect.rb +2 -3
  64. data/lib/action_dispatch/http/headers.rb +4 -4
  65. data/lib/action_dispatch/http/mime_negotiation.rb +44 -16
  66. data/lib/action_dispatch/http/mime_type.rb +47 -30
  67. data/lib/action_dispatch/http/parameters.rb +18 -27
  68. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  69. data/lib/action_dispatch/http/request.rb +49 -35
  70. data/lib/action_dispatch/http/response.rb +34 -26
  71. data/lib/action_dispatch/http/upload.rb +9 -1
  72. data/lib/action_dispatch/http/url.rb +86 -94
  73. data/lib/action_dispatch/journey/formatter.rb +55 -31
  74. data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
  75. data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
  76. data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
  77. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  78. data/lib/action_dispatch/journey/nodes/node.rb +83 -16
  79. data/lib/action_dispatch/journey/parser.rb +13 -13
  80. data/lib/action_dispatch/journey/parser.y +1 -1
  81. data/lib/action_dispatch/journey/path/pattern.rb +42 -34
  82. data/lib/action_dispatch/journey/route.rb +14 -31
  83. data/lib/action_dispatch/journey/router/utils.rb +16 -14
  84. data/lib/action_dispatch/journey/router.rb +27 -35
  85. data/lib/action_dispatch/journey/routes.rb +3 -5
  86. data/lib/action_dispatch/journey/scanner.rb +10 -4
  87. data/lib/action_dispatch/journey/visitors.rb +1 -4
  88. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  89. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  90. data/lib/action_dispatch/journey.rb +0 -2
  91. data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
  92. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  93. data/lib/action_dispatch/middleware/cookies.rb +136 -113
  94. data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
  95. data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
  96. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  97. data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
  98. data/lib/action_dispatch/middleware/executor.rb +4 -1
  99. data/lib/action_dispatch/middleware/flash.rb +10 -12
  100. data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
  101. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  102. data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
  103. data/lib/action_dispatch/middleware/request_id.rb +5 -6
  104. data/lib/action_dispatch/middleware/server_timing.rb +33 -0
  105. data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
  106. data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
  107. data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
  109. data/lib/action_dispatch/middleware/ssl.rb +20 -15
  110. data/lib/action_dispatch/middleware/stack.rb +79 -7
  111. data/lib/action_dispatch/middleware/static.rb +150 -94
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  114. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +6 -11
  116. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  118. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
  119. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
  122. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
  124. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
  125. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
  126. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +5 -5
  129. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
  130. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
  131. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  132. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
  133. data/lib/action_dispatch/railtie.rb +16 -4
  134. data/lib/action_dispatch/request/session.rb +59 -22
  135. data/lib/action_dispatch/request/utils.rb +28 -2
  136. data/lib/action_dispatch/routing/inspector.rb +102 -54
  137. data/lib/action_dispatch/routing/mapper.rb +184 -156
  138. data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
  139. data/lib/action_dispatch/routing/redirection.rb +4 -6
  140. data/lib/action_dispatch/routing/route_set.rb +83 -73
  141. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  142. data/lib/action_dispatch/routing/url_for.rb +2 -3
  143. data/lib/action_dispatch/routing.rb +23 -22
  144. data/lib/action_dispatch/system_test_case.rb +65 -16
  145. data/lib/action_dispatch/system_testing/browser.rb +43 -16
  146. data/lib/action_dispatch/system_testing/driver.rb +42 -10
  147. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
  148. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -10
  149. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  150. data/lib/action_dispatch/testing/assertions/response.rb +4 -7
  151. data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
  152. data/lib/action_dispatch/testing/assertions.rb +3 -6
  153. data/lib/action_dispatch/testing/integration.rb +61 -30
  154. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  155. data/lib/action_dispatch/testing/test_process.rb +8 -6
  156. data/lib/action_dispatch/testing/test_request.rb +3 -3
  157. data/lib/action_dispatch/testing/test_response.rb +4 -32
  158. data/lib/action_dispatch.rb +15 -7
  159. data/lib/action_pack/gem_version.rb +4 -4
  160. data/lib/action_pack.rb +1 -1
  161. metadata +44 -25
  162. data/lib/action_controller/metal/force_ssl.rb +0 -99
  163. data/lib/action_dispatch/http/parameter_filter.rb +0 -86
  164. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  165. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
  166. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
  167. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -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.
@@ -18,7 +18,7 @@ module ActionController #:nodoc:
18
18
  end
19
19
 
20
20
  private
21
- def process_action(*args)
21
+ def process_action(*)
22
22
  super
23
23
  rescue Exception => exception
24
24
  request.env["action_dispatch.show_detailed_exceptions"] ||= show_detailed_exceptions?
@@ -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,10 +193,7 @@ module ActionController #:nodoc:
193
193
  # To be described.
194
194
  #
195
195
  module Streaming
196
- extend ActiveSupport::Concern
197
-
198
196
  private
199
-
200
197
  # Set proper cache control and transfer encoding when streaming
201
198
  def _process_options(options)
202
199
  super
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/hash/indifferent_access"
4
- require "active_support/core_ext/hash/transform_values"
5
4
  require "active_support/core_ext/array/wrap"
6
5
  require "active_support/core_ext/string/filters"
7
6
  require "active_support/core_ext/object/to_query"
8
- require "active_support/rescuable"
9
7
  require "action_dispatch/http/upload"
10
8
  require "rack/test"
11
9
  require "stringio"
@@ -21,12 +19,21 @@ module ActionController
21
19
  # params.require(:a)
22
20
  # # => ActionController::ParameterMissing: param is missing or the value is empty: a
23
21
  class ParameterMissing < KeyError
24
- attr_reader :param # :nodoc:
22
+ attr_reader :param, :keys # :nodoc:
25
23
 
26
- def initialize(param) # :nodoc:
24
+ def initialize(param, keys = nil) # :nodoc:
27
25
  @param = param
26
+ @keys = keys
28
27
  super("param is missing or the value is empty: #{param}")
29
28
  end
29
+
30
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
31
+ include DidYouMean::Correctable # :nodoc:
32
+
33
+ def corrections # :nodoc:
34
+ @corrections ||= DidYouMean::SpellChecker.new(dictionary: keys).correct(param.to_s)
35
+ end
36
+ end
30
37
  end
31
38
 
32
39
  # Raised when a supplied parameter is not expected and
@@ -59,7 +66,7 @@ module ActionController
59
66
 
60
67
  # == Action Controller \Parameters
61
68
  #
62
- # Allows you to choose which attributes should be whitelisted for mass updating
69
+ # Allows you to choose which attributes should be permitted for mass updating
63
70
  # and thus prevent accidentally exposing that which shouldn't be exposed.
64
71
  # Provides two methods for this purpose: #require and #permit. The former is
65
72
  # used to mark parameters as required. The latter is used to set the parameter
@@ -74,7 +81,7 @@ module ActionController
74
81
  # })
75
82
  #
76
83
  # permitted = params.require(:person).permit(:name, :age)
77
- # permitted # => <ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
84
+ # permitted # => #<ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
78
85
  # permitted.permitted? # => true
79
86
  #
80
87
  # Person.first.update!(permitted)
@@ -84,11 +91,13 @@ module ActionController
84
91
  #
85
92
  # * +permit_all_parameters+ - If it's +true+, all the parameters will be
86
93
  # permitted by default. The default is +false+.
87
- # * +action_on_unpermitted_parameters+ - Allow to control the behavior when parameters
88
- # that are not explicitly permitted are found. The values can be +false+ to just filter them
89
- # out, <tt>:log</tt> to additionally write a message on the logger, or <tt>:raise</tt> to raise
90
- # ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt>
91
- # 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.
92
101
  #
93
102
  # Examples:
94
103
  #
@@ -102,7 +111,7 @@ module ActionController
102
111
  #
103
112
  # params = ActionController::Parameters.new(a: "123", b: "456")
104
113
  # params.permit(:c)
105
- # # => <ActionController::Parameters {} permitted: true>
114
+ # # => #<ActionController::Parameters {} permitted: true>
106
115
  #
107
116
  # ActionController::Parameters.action_on_unpermitted_parameters = :raise
108
117
  #
@@ -133,6 +142,15 @@ module ActionController
133
142
  #
134
143
  # Returns a hash that can be used as the JSON representation for the parameters.
135
144
 
145
+ ##
146
+ # :method: each_key
147
+ #
148
+ # :call-seq:
149
+ # each_key()
150
+ #
151
+ # Calls block once for each key in the parameters, passing the key.
152
+ # If no block is given, an enumerator is returned instead.
153
+
136
154
  ##
137
155
  # :method: empty?
138
156
  #
@@ -173,6 +191,14 @@ module ActionController
173
191
  #
174
192
  # Returns true if the given key is present in the parameters.
175
193
 
194
+ ##
195
+ # :method: member?
196
+ #
197
+ # :call-seq:
198
+ # member?(key)
199
+ #
200
+ # Returns true if the given key is present in the parameters.
201
+
176
202
  ##
177
203
  # :method: keys
178
204
  #
@@ -204,8 +230,8 @@ module ActionController
204
230
  # values()
205
231
  #
206
232
  # Returns a new array of the values of the parameters.
207
- delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
208
- :as_json, :to_s, to: :@parameters
233
+ delegate :keys, :key?, :has_key?, :member?, :values, :has_value?, :value?, :empty?, :include?,
234
+ :as_json, :to_s, :each_key, to: :@parameters
209
235
 
210
236
  # By default, never raise an UnpermittedParameters exception if these
211
237
  # params are present. The default includes both 'controller' and 'action'
@@ -213,9 +239,15 @@ module ActionController
213
239
  # to change these is to specify `always_permitted_parameters` in your
214
240
  # config. For instance:
215
241
  #
216
- # config.always_permitted_parameters = %w( controller action format )
242
+ # config.action_controller.always_permitted_parameters = %w( controller action format )
217
243
  cattr_accessor :always_permitted_parameters, default: %w( controller action )
218
244
 
245
+ class << self
246
+ def nested_attribute?(key, value) # :nodoc:
247
+ /\A-?\d+\z/.match?(key) && (value.is_a?(Hash) || value.is_a?(Parameters))
248
+ end
249
+ end
250
+
219
251
  # Returns a new instance of <tt>ActionController::Parameters</tt>.
220
252
  # Also, sets the +permitted+ attribute to the default value of
221
253
  # <tt>ActionController::Parameters.permit_all_parameters</tt>.
@@ -232,8 +264,9 @@ module ActionController
232
264
  # params = ActionController::Parameters.new(name: "Francesco")
233
265
  # params.permitted? # => true
234
266
  # Person.new(params) # => #<Person id: nil, name: "Francesco">
235
- def initialize(parameters = {})
267
+ def initialize(parameters = {}, logging_context = {})
236
268
  @parameters = parameters.with_indifferent_access
269
+ @logging_context = logging_context
237
270
  @permitted = self.class.permit_all_parameters
238
271
  end
239
272
 
@@ -246,6 +279,11 @@ module ActionController
246
279
  @parameters == other
247
280
  end
248
281
  end
282
+ alias eql? ==
283
+
284
+ def hash
285
+ [@parameters.hash, @permitted].hash
286
+ end
249
287
 
250
288
  # Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
251
289
  # representation of the parameters with all unpermitted keys removed.
@@ -334,12 +372,26 @@ module ActionController
334
372
  # Convert all hashes in values into parameters, then yield each pair in
335
373
  # the same way as <tt>Hash#each_pair</tt>.
336
374
  def each_pair(&block)
375
+ return to_enum(__callee__) unless block_given?
337
376
  @parameters.each_pair do |key, value|
338
377
  yield [key, convert_hashes_to_parameters(key, value)]
339
378
  end
379
+
380
+ self
340
381
  end
341
382
  alias_method :each, :each_pair
342
383
 
384
+ # Convert all hashes in values into parameters, then yield each value in
385
+ # the same way as <tt>Hash#each_value</tt>.
386
+ def each_value(&block)
387
+ return to_enum(:each_value) unless block_given?
388
+ @parameters.each_pair do |key, value|
389
+ yield convert_hashes_to_parameters(key, value)
390
+ end
391
+
392
+ self
393
+ end
394
+
343
395
  # Attribute that keeps track of converted arrays, if any, to avoid double
344
396
  # looping in the common use case permit + mass-assignment. Defined in a
345
397
  # method to instantiate it only if needed.
@@ -390,7 +442,7 @@ module ActionController
390
442
  # either present or the singleton +false+, returns said value:
391
443
  #
392
444
  # ActionController::Parameters.new(person: { name: "Francesco" }).require(:person)
393
- # # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
445
+ # # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
394
446
  #
395
447
  # Otherwise raises <tt>ActionController::ParameterMissing</tt>:
396
448
  #
@@ -440,7 +492,7 @@ module ActionController
440
492
  if value.present? || value == false
441
493
  value
442
494
  else
443
- raise ParameterMissing.new(key)
495
+ raise ParameterMissing.new(key, @parameters.keys)
444
496
  end
445
497
  end
446
498
 
@@ -506,7 +558,7 @@ module ActionController
506
558
  #
507
559
  # Note that if you use +permit+ in a key that points to a hash,
508
560
  # it won't allow all the hash. You also need to specify which
509
- # attributes inside the hash should be whitelisted.
561
+ # attributes inside the hash should be permitted.
510
562
  #
511
563
  # params = ActionController::Parameters.new({
512
564
  # person: {
@@ -518,13 +570,48 @@ module ActionController
518
570
  # })
519
571
  #
520
572
  # params.require(:person).permit(:contact)
521
- # # => <ActionController::Parameters {} permitted: true>
573
+ # # => #<ActionController::Parameters {} permitted: true>
522
574
  #
523
575
  # params.require(:person).permit(contact: :phone)
524
- # # => <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>
525
577
  #
526
578
  # params.require(:person).permit(contact: [ :email, :phone ])
527
- # # => <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"}}}
528
615
  def permit(*filters)
529
616
  params = self.class.new
530
617
 
@@ -546,7 +633,7 @@ module ActionController
546
633
  # returns +nil+.
547
634
  #
548
635
  # params = ActionController::Parameters.new(person: { name: "Francesco" })
549
- # params[:person] # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
636
+ # params[:person] # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
550
637
  # params[:none] # => nil
551
638
  def [](key)
552
639
  convert_hashes_to_parameters(key, @parameters[key])
@@ -566,9 +653,9 @@ module ActionController
566
653
  # is given, then that will be run and its result returned.
567
654
  #
568
655
  # params = ActionController::Parameters.new(person: { name: "Francesco" })
569
- # params.fetch(:person) # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
656
+ # params.fetch(:person) # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
570
657
  # params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
571
- # params.fetch(:none, {}) # => <ActionController::Parameters {} permitted: false>
658
+ # params.fetch(:none, {}) # => #<ActionController::Parameters {} permitted: false>
572
659
  # params.fetch(:none, "Francesco") # => "Francesco"
573
660
  # params.fetch(:none) { "Francesco" } # => "Francesco"
574
661
  def fetch(key, *args)
@@ -577,26 +664,24 @@ module ActionController
577
664
  if block_given?
578
665
  yield
579
666
  else
580
- args.fetch(0) { raise ActionController::ParameterMissing.new(key) }
667
+ args.fetch(0) { raise ActionController::ParameterMissing.new(key, @parameters.keys) }
581
668
  end
582
669
  }
583
670
  )
584
671
  end
585
672
 
586
- if Hash.method_defined?(:dig)
587
- # Extracts the nested parameter from the given +keys+ by calling +dig+
588
- # at each step. Returns +nil+ if any intermediate step is +nil+.
589
- #
590
- # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
591
- # params.dig(:foo, :bar, :baz) # => 1
592
- # params.dig(:foo, :zot, :xyz) # => nil
593
- #
594
- # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
595
- # params2.dig(:foo, 1) # => 11
596
- def dig(*keys)
597
- convert_hashes_to_parameters(keys.first, @parameters[keys.first])
598
- @parameters.dig(*keys)
599
- end
673
+ # Extracts the nested parameter from the given +keys+ by calling +dig+
674
+ # at each step. Returns +nil+ if any intermediate step is +nil+.
675
+ #
676
+ # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
677
+ # params.dig(:foo, :bar, :baz) # => 1
678
+ # params.dig(:foo, :zot, :xyz) # => nil
679
+ #
680
+ # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
681
+ # params2.dig(:foo, 1) # => 11
682
+ def dig(*keys)
683
+ convert_hashes_to_parameters(keys.first, @parameters[keys.first])
684
+ @parameters.dig(*keys)
600
685
  end
601
686
 
602
687
  # Returns a new <tt>ActionController::Parameters</tt> instance that
@@ -604,8 +689,8 @@ module ActionController
604
689
  # don't exist, returns an empty hash.
605
690
  #
606
691
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
607
- # params.slice(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
608
- # 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>
609
694
  def slice(*keys)
610
695
  new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
611
696
  end
@@ -621,8 +706,8 @@ module ActionController
621
706
  # filters out the given +keys+.
622
707
  #
623
708
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
624
- # params.except(:a, :b) # => <ActionController::Parameters {"c"=>3} permitted: false>
625
- # 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>
626
711
  def except(*keys)
627
712
  new_instance_with_inherited_permitted_status(@parameters.except(*keys))
628
713
  end
@@ -630,8 +715,8 @@ module ActionController
630
715
  # Removes and returns the key/value pairs matching the given keys.
631
716
  #
632
717
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
633
- # params.extract!(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
634
- # 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>
635
720
  def extract!(*keys)
636
721
  new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
637
722
  end
@@ -641,7 +726,7 @@ module ActionController
641
726
  #
642
727
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
643
728
  # params.transform_values { |x| x * 2 }
644
- # # => <ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
729
+ # # => #<ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
645
730
  def transform_values
646
731
  return to_enum(:transform_values) unless block_given?
647
732
  new_instance_with_inherited_permitted_status(
@@ -660,22 +745,37 @@ module ActionController
660
745
  # Returns a new <tt>ActionController::Parameters</tt> instance with the
661
746
  # results of running +block+ once for every key. The values are unchanged.
662
747
  def transform_keys(&block)
663
- if block
664
- new_instance_with_inherited_permitted_status(
665
- @parameters.transform_keys(&block)
666
- )
667
- else
668
- @parameters.transform_keys
669
- end
748
+ return to_enum(:transform_keys) unless block_given?
749
+ new_instance_with_inherited_permitted_status(
750
+ @parameters.transform_keys(&block)
751
+ )
670
752
  end
671
753
 
672
754
  # Performs keys transformation and returns the altered
673
755
  # <tt>ActionController::Parameters</tt> instance.
674
756
  def transform_keys!(&block)
757
+ return to_enum(:transform_keys!) unless block_given?
675
758
  @parameters.transform_keys!(&block)
676
759
  self
677
760
  end
678
761
 
762
+ # Returns a new <tt>ActionController::Parameters</tt> instance with the
763
+ # results of running +block+ once for every key. This includes the keys
764
+ # from the root hash and from all nested hashes and arrays. The values are unchanged.
765
+ def deep_transform_keys(&block)
766
+ new_instance_with_inherited_permitted_status(
767
+ @parameters.deep_transform_keys(&block)
768
+ )
769
+ end
770
+
771
+ # Returns the <tt>ActionController::Parameters</tt> instance changing its keys.
772
+ # This includes the keys from the root hash and from all nested hashes and arrays.
773
+ # The values are unchanged.
774
+ def deep_transform_keys!(&block)
775
+ @parameters.deep_transform_keys!(&block)
776
+ self
777
+ end
778
+
679
779
  # Deletes a key-value pair from +Parameters+ and returns the value. If
680
780
  # +key+ is not found, returns +nil+ (or, with optional code block, yields
681
781
  # +key+ and returns the result). Cf. +#extract!+, which returns the
@@ -710,6 +810,28 @@ module ActionController
710
810
  end
711
811
  alias_method :delete_if, :reject!
712
812
 
813
+ # Returns a new instance of <tt>ActionController::Parameters</tt> with +nil+ values removed.
814
+ def compact
815
+ new_instance_with_inherited_permitted_status(@parameters.compact)
816
+ end
817
+
818
+ # Removes all +nil+ values in place and returns +self+, or +nil+ if no changes were made.
819
+ def compact!
820
+ self if @parameters.compact!
821
+ end
822
+
823
+ # Returns a new instance of <tt>ActionController::Parameters</tt> without the blank values.
824
+ # Uses Object#blank? for determining if a value is blank.
825
+ def compact_blank
826
+ reject { |_k, v| v.blank? }
827
+ end
828
+
829
+ # Removes all blank values in place and returns self.
830
+ # Uses Object#blank? for determining if a value is blank.
831
+ def compact_blank!
832
+ reject! { |_k, v| v.blank? }
833
+ end
834
+
713
835
  # Returns values that were assigned to the given +keys+. Note that all the
714
836
  # +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
715
837
  def values_at(*keys)
@@ -756,7 +878,7 @@ module ActionController
756
878
  end
757
879
 
758
880
  def inspect
759
- "<#{self.class} #{@parameters} permitted: #{@permitted}>"
881
+ "#<#{self.class} #{@parameters} permitted: #{@permitted}>"
760
882
  end
761
883
 
762
884
  def self.hook_into_yaml_loading # :nodoc:
@@ -781,14 +903,14 @@ module ActionController
781
903
  @permitted = coder.map["ivars"][:@permitted]
782
904
  when "!ruby/object:ActionController::Parameters"
783
905
  # YAML's Object format. Only needed because of the format
784
- # backwardscompability above, otherwise equivalent to YAML's initialization.
906
+ # backwards compatibility above, otherwise equivalent to YAML's initialization.
785
907
  @parameters, @permitted = coder.map["parameters"], coder.map["permitted"]
786
908
  end
787
909
  end
788
910
 
789
911
  # Returns duplicate of object including all parameters.
790
912
  def deep_dup
791
- self.class.new(@parameters.deep_dup).tap do |duplicate|
913
+ self.class.new(@parameters.deep_dup, @logging_context).tap do |duplicate|
792
914
  duplicate.permitted = @permitted
793
915
  end
794
916
  end
@@ -796,17 +918,21 @@ module ActionController
796
918
  protected
797
919
  attr_reader :parameters
798
920
 
799
- def permitted=(new_permitted)
800
- @permitted = new_permitted
921
+ attr_writer :permitted
922
+
923
+ def nested_attributes?
924
+ @parameters.any? { |k, v| Parameters.nested_attribute?(k, v) }
801
925
  end
802
926
 
803
- def fields_for_style?
804
- @parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
927
+ def each_nested_attribute
928
+ hash = self.class.new
929
+ self.each { |k, v| hash[k] = yield v if Parameters.nested_attribute?(k, v) }
930
+ hash
805
931
  end
806
932
 
807
933
  private
808
934
  def new_instance_with_inherited_permitted_status(hash)
809
- self.class.new(hash).tap do |new_instance|
935
+ self.class.new(hash, @logging_context).tap do |new_instance|
810
936
  new_instance.permitted = @permitted
811
937
  end
812
938
  end
@@ -837,24 +963,28 @@ module ActionController
837
963
  when Array
838
964
  return value if converted_arrays.member?(value)
839
965
  converted = value.map { |_| convert_value_to_parameters(_) }
840
- converted_arrays << converted
966
+ converted_arrays << converted.dup
841
967
  converted
842
968
  when Hash
843
- self.class.new(value)
969
+ self.class.new(value, @logging_context)
844
970
  else
845
971
  value
846
972
  end
847
973
  end
848
974
 
849
- def each_element(object)
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)
850
982
  case object
851
983
  when Array
852
- object.grep(Parameters).map { |el| yield el }.compact
984
+ object.grep(Parameters).filter_map(&block)
853
985
  when Parameters
854
- if object.fields_for_style?
855
- hash = object.class.new
856
- object.each { |k, v| hash[k] = yield v }
857
- hash
986
+ if object.nested_attributes? && !specify_numeric_keys?(filter)
987
+ object.each_nested_attribute(&block)
858
988
  else
859
989
  yield object
860
990
  end
@@ -867,7 +997,7 @@ module ActionController
867
997
  case self.class.action_on_unpermitted_parameters
868
998
  when :log
869
999
  name = "unpermitted_parameters.action_controller"
870
- ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys)
1000
+ ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys, context: @logging_context)
871
1001
  when :raise
872
1002
  raise ActionController::UnpermittedParameters.new(unpermitted_keys)
873
1003
  end
@@ -882,7 +1012,7 @@ module ActionController
882
1012
  # --- Filtering ----------------------------------------------------------
883
1013
  #
884
1014
 
885
- # This is a white list of permitted scalar types that includes the ones
1015
+ # This is a list of permitted scalar types that includes the ones
886
1016
  # supported in XML and JSON requests.
887
1017
  #
888
1018
  # This list is in particular used to filter ordinary requests, String goes
@@ -909,15 +1039,28 @@ module ActionController
909
1039
  PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) }
910
1040
  end
911
1041
 
912
- def permitted_scalar_filter(params, key)
913
- if has_key?(key) && permitted_scalar?(self[key])
914
- params[key] = self[key]
1042
+ # Adds existing keys to the params if their values are scalar.
1043
+ #
1044
+ # For example:
1045
+ #
1046
+ # puts self.keys #=> ["zipcode(90210i)"]
1047
+ # params = {}
1048
+ #
1049
+ # permitted_scalar_filter(params, "zipcode")
1050
+ #
1051
+ # puts params.keys # => ["zipcode"]
1052
+ def permitted_scalar_filter(params, permitted_key)
1053
+ permitted_key = permitted_key.to_s
1054
+
1055
+ if has_key?(permitted_key) && permitted_scalar?(self[permitted_key])
1056
+ params[permitted_key] = self[permitted_key]
915
1057
  end
916
1058
 
917
- keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k|
918
- if permitted_scalar?(self[k])
919
- params[k] = self[k]
920
- end
1059
+ each_key do |key|
1060
+ next unless key =~ /\(\d+[if]?\)\z/
1061
+ next unless $~.pre_match == permitted_key
1062
+
1063
+ params[key] = self[key] if permitted_scalar?(self[key])
921
1064
  end
922
1065
  end
923
1066
 
@@ -953,7 +1096,7 @@ module ActionController
953
1096
  end
954
1097
  elsif non_scalar?(value)
955
1098
  # Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
956
- params[key] = each_element(value) do |element|
1099
+ params[key] = each_element(value, filter[key]) do |element|
957
1100
  element.permit(*Array.wrap(filter[key]))
958
1101
  end
959
1102
  end
@@ -1002,8 +1145,8 @@ module ActionController
1002
1145
  #
1003
1146
  # It provides an interface for protecting attributes from end-user
1004
1147
  # assignment. This makes Action Controller parameters forbidden
1005
- # to be used in Active Model mass assignment until they have been
1006
- # whitelisted.
1148
+ # to be used in Active Model mass assignment until they have been explicitly
1149
+ # enumerated.
1007
1150
  #
1008
1151
  # In addition, parameters can be marked as required and flow through a
1009
1152
  # predefined raise/rescue flow to end up as a <tt>400 Bad Request</tt> with no
@@ -1039,7 +1182,7 @@ module ActionController
1039
1182
  # end
1040
1183
  #
1041
1184
  # In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you
1042
- # will need to specify which nested attributes should be whitelisted. You might want
1185
+ # will need to specify which nested attributes should be permitted. You might want
1043
1186
  # to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information.
1044
1187
  #
1045
1188
  # class Person
@@ -1057,7 +1200,7 @@ module ActionController
1057
1200
  # private
1058
1201
  #
1059
1202
  # def person_params
1060
- # # It's mandatory to specify the nested attributes that should be whitelisted.
1203
+ # # It's mandatory to specify the nested attributes that should be permitted.
1061
1204
  # # If you use `permit` with just the key that points to the nested attributes hash,
1062
1205
  # # it will return an empty hash.
1063
1206
  # params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
@@ -1067,13 +1210,18 @@ module ActionController
1067
1210
  # See ActionController::Parameters.require and ActionController::Parameters.permit
1068
1211
  # for more information.
1069
1212
  module StrongParameters
1070
- extend ActiveSupport::Concern
1071
- include ActiveSupport::Rescuable
1072
-
1073
1213
  # Returns a new ActionController::Parameters object that
1074
1214
  # has been instantiated with the <tt>request.parameters</tt>.
1075
1215
  def params
1076
- @_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
1077
1225
  end
1078
1226
 
1079
1227
  # Assigns the given +value+ to the +params+ hash. If +value+