actionpack 5.2.6 → 6.1.4.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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +327 -335
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/abstract_controller/base.rb +38 -4
  6. data/lib/abstract_controller/caching/fragments.rb +6 -22
  7. data/lib/abstract_controller/caching.rb +1 -1
  8. data/lib/abstract_controller/callbacks.rb +14 -2
  9. data/lib/abstract_controller/collector.rb +1 -2
  10. data/lib/abstract_controller/helpers.rb +106 -90
  11. data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
  12. data/lib/abstract_controller/rendering.rb +9 -9
  13. data/lib/abstract_controller/translation.rb +11 -5
  14. data/lib/abstract_controller.rb +1 -0
  15. data/lib/action_controller/api.rb +4 -3
  16. data/lib/action_controller/base.rb +6 -9
  17. data/lib/action_controller/caching.rb +1 -3
  18. data/lib/action_controller/log_subscriber.rb +10 -7
  19. data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
  20. data/lib/action_controller/metal/conditional_get.rb +19 -5
  21. data/lib/action_controller/metal/content_security_policy.rb +1 -2
  22. data/lib/action_controller/metal/cookies.rb +3 -1
  23. data/lib/action_controller/metal/data_streaming.rb +6 -7
  24. data/lib/action_controller/metal/default_headers.rb +17 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
  26. data/lib/action_controller/metal/exceptions.rb +56 -2
  27. data/lib/action_controller/metal/flash.rb +5 -5
  28. data/lib/action_controller/metal/head.rb +7 -4
  29. data/lib/action_controller/metal/helpers.rb +14 -5
  30. data/lib/action_controller/metal/http_authentication.rb +24 -23
  31. data/lib/action_controller/metal/implicit_render.rb +5 -15
  32. data/lib/action_controller/metal/instrumentation.rb +13 -14
  33. data/lib/action_controller/metal/live.rb +39 -32
  34. data/lib/action_controller/metal/logging.rb +20 -0
  35. data/lib/action_controller/metal/mime_responds.rb +19 -4
  36. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  37. data/lib/action_controller/metal/params_wrapper.rb +32 -22
  38. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  39. data/lib/action_controller/metal/redirecting.rb +6 -6
  40. data/lib/action_controller/metal/renderers.rb +4 -4
  41. data/lib/action_controller/metal/rendering.rb +8 -3
  42. data/lib/action_controller/metal/request_forgery_protection.rb +26 -49
  43. data/lib/action_controller/metal/rescue.rb +1 -1
  44. data/lib/action_controller/metal/streaming.rb +0 -1
  45. data/lib/action_controller/metal/strong_parameters.rb +167 -58
  46. data/lib/action_controller/metal/url_for.rb +1 -1
  47. data/lib/action_controller/metal.rb +10 -8
  48. data/lib/action_controller/railties/helpers.rb +1 -1
  49. data/lib/action_controller/renderer.rb +37 -13
  50. data/lib/action_controller/template_assertions.rb +1 -1
  51. data/lib/action_controller/test_case.rb +71 -63
  52. data/lib/action_controller.rb +7 -4
  53. data/lib/action_dispatch/http/cache.rb +31 -27
  54. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  55. data/lib/action_dispatch/http/content_security_policy.rb +33 -19
  56. data/lib/action_dispatch/http/filter_parameters.rb +9 -8
  57. data/lib/action_dispatch/http/filter_redirect.rb +2 -3
  58. data/lib/action_dispatch/http/headers.rb +4 -4
  59. data/lib/action_dispatch/http/mime_negotiation.rb +26 -13
  60. data/lib/action_dispatch/http/mime_type.rb +43 -24
  61. data/lib/action_dispatch/http/parameters.rb +14 -23
  62. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  63. data/lib/action_dispatch/http/request.rb +45 -22
  64. data/lib/action_dispatch/http/response.rb +45 -25
  65. data/lib/action_dispatch/http/upload.rb +9 -1
  66. data/lib/action_dispatch/http/url.rb +82 -82
  67. data/lib/action_dispatch/journey/formatter.rb +55 -31
  68. data/lib/action_dispatch/journey/gtg/builder.rb +22 -37
  69. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  70. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -5
  71. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  72. data/lib/action_dispatch/journey/nodes/node.rb +13 -11
  73. data/lib/action_dispatch/journey/parser.rb +13 -13
  74. data/lib/action_dispatch/journey/parser.y +1 -1
  75. data/lib/action_dispatch/journey/path/pattern.rb +19 -21
  76. data/lib/action_dispatch/journey/route.rb +10 -20
  77. data/lib/action_dispatch/journey/router/utils.rb +14 -12
  78. data/lib/action_dispatch/journey/router.rb +26 -34
  79. data/lib/action_dispatch/journey/routes.rb +0 -2
  80. data/lib/action_dispatch/journey/scanner.rb +10 -4
  81. data/lib/action_dispatch/journey/visitors.rb +1 -4
  82. data/lib/action_dispatch/journey.rb +0 -2
  83. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  84. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  85. data/lib/action_dispatch/middleware/cookies.rb +128 -109
  86. data/lib/action_dispatch/middleware/debug_exceptions.rb +43 -66
  87. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  88. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  89. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -30
  90. data/lib/action_dispatch/middleware/flash.rb +1 -1
  91. data/lib/action_dispatch/middleware/host_authorization.rb +141 -0
  92. data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
  93. data/lib/action_dispatch/middleware/remote_ip.rb +14 -16
  94. data/lib/action_dispatch/middleware/request_id.rb +5 -6
  95. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -3
  96. data/lib/action_dispatch/middleware/session/cookie_store.rb +3 -9
  97. data/lib/action_dispatch/middleware/show_exceptions.rb +3 -2
  98. data/lib/action_dispatch/middleware/ssl.rb +20 -15
  99. data/lib/action_dispatch/middleware/stack.rb +56 -2
  100. data/lib/action_dispatch/middleware/static.rb +153 -93
  101. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  103. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
  105. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  107. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  108. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  109. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  111. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  112. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +6 -3
  113. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
  114. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +104 -8
  115. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  118. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  119. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  120. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  121. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +24 -1
  122. data/lib/action_dispatch/railtie.rb +8 -2
  123. data/lib/action_dispatch/request/session.rb +11 -10
  124. data/lib/action_dispatch/request/utils.rb +26 -2
  125. data/lib/action_dispatch/routing/inspector.rb +100 -52
  126. data/lib/action_dispatch/routing/mapper.rb +155 -103
  127. data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -15
  128. data/lib/action_dispatch/routing/redirection.rb +4 -4
  129. data/lib/action_dispatch/routing/route_set.rb +71 -69
  130. data/lib/action_dispatch/routing/url_for.rb +2 -2
  131. data/lib/action_dispatch/routing.rb +21 -20
  132. data/lib/action_dispatch/system_test_case.rb +54 -11
  133. data/lib/action_dispatch/system_testing/browser.rb +53 -16
  134. data/lib/action_dispatch/system_testing/driver.rb +11 -3
  135. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +49 -7
  136. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -10
  137. data/lib/action_dispatch/testing/assertion_response.rb +0 -1
  138. data/lib/action_dispatch/testing/assertions/response.rb +4 -7
  139. data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
  140. data/lib/action_dispatch/testing/assertions.rb +1 -1
  141. data/lib/action_dispatch/testing/integration.rb +60 -28
  142. data/lib/action_dispatch/testing/request_encoder.rb +2 -2
  143. data/lib/action_dispatch/testing/test_process.rb +29 -4
  144. data/lib/action_dispatch/testing/test_request.rb +3 -3
  145. data/lib/action_dispatch/testing/test_response.rb +4 -32
  146. data/lib/action_dispatch.rb +9 -3
  147. data/lib/action_pack/gem_version.rb +4 -4
  148. data/lib/action_pack.rb +1 -1
  149. metadata +35 -23
  150. data/lib/action_controller/metal/force_ssl.rb +0 -99
  151. data/lib/action_dispatch/http/parameter_filter.rb +0 -86
  152. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  153. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
  154. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
  155. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -16,10 +16,11 @@ module ActionController
16
16
 
17
17
  attr_internal :view_runtime
18
18
 
19
- def process_action(*args)
19
+ def process_action(*)
20
20
  raw_payload = {
21
21
  controller: self.class.name,
22
22
  action: action_name,
23
+ request: request,
23
24
  params: request.filtered_parameters,
24
25
  headers: request.headers,
25
26
  format: request.format.ref,
@@ -27,20 +28,19 @@ module ActionController
27
28
  path: request.fullpath
28
29
  }
29
30
 
30
- ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
31
+ ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
31
32
 
32
33
  ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
33
- begin
34
- result = super
35
- payload[:status] = response.status
36
- result
37
- ensure
38
- append_info_to_payload(payload)
39
- end
34
+ result = super
35
+ payload[:response] = response
36
+ payload[:status] = response.status
37
+ result
38
+ ensure
39
+ append_info_to_payload(payload)
40
40
  end
41
41
  end
42
42
 
43
- def render(*args)
43
+ def render(*)
44
44
  render_output = nil
45
45
  self.view_runtime = cleanup_view_runtime do
46
46
  Benchmark.ms { render_output = super }
@@ -61,8 +61,8 @@ module ActionController
61
61
  end
62
62
  end
63
63
 
64
- def redirect_to(*args)
65
- ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
64
+ def redirect_to(*)
65
+ ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
66
66
  result = super
67
67
  payload[:status] = response.status
68
68
  payload[:location] = response.filtered_location
@@ -71,9 +71,8 @@ module ActionController
71
71
  end
72
72
 
73
73
  private
74
-
75
74
  # A hook invoked every time a before callback is halted.
76
- def halted_callback_hook(filter)
75
+ def halted_callback_hook(filter, _)
77
76
  ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
78
77
  end
79
78
 
@@ -86,7 +86,7 @@ module ActionController
86
86
  # Note: SSEs are not currently supported by IE. However, they are supported
87
87
  # by Chrome, Firefox, Opera, and Safari.
88
88
  class SSE
89
- WHITELISTED_OPTIONS = %w( retry event id )
89
+ PERMITTED_OPTIONS = %w( retry event id )
90
90
 
91
91
  def initialize(stream, options = {})
92
92
  @stream = stream
@@ -107,17 +107,16 @@ module ActionController
107
107
  end
108
108
 
109
109
  private
110
-
111
110
  def perform_write(json, options)
112
111
  current_options = @options.merge(options).stringify_keys
113
112
 
114
- WHITELISTED_OPTIONS.each do |option_name|
113
+ PERMITTED_OPTIONS.each do |option_name|
115
114
  if (option_value = current_options[option_name])
116
115
  @stream.write "#{option_name}: #{option_value}\n"
117
116
  end
118
117
  end
119
118
 
120
- message = json.gsub("\n".freeze, "\ndata: ".freeze)
119
+ message = json.gsub("\n", "\ndata: ")
121
120
  @stream.write "data: #{message}\n\n"
122
121
  end
123
122
  end
@@ -128,6 +127,11 @@ module ActionController
128
127
  class Buffer < ActionDispatch::Response::Buffer #:nodoc:
129
128
  include MonitorMixin
130
129
 
130
+ class << self
131
+ attr_accessor :queue_size
132
+ end
133
+ @queue_size = 10
134
+
131
135
  # Ignore that the client has disconnected.
132
136
  #
133
137
  # If this value is `true`, calling `write` after the client
@@ -137,16 +141,16 @@ module ActionController
137
141
  attr_accessor :ignore_disconnect
138
142
 
139
143
  def initialize(response)
144
+ super(response, build_queue(self.class.queue_size))
140
145
  @error_callback = lambda { true }
141
146
  @cv = new_cond
142
147
  @aborted = false
143
148
  @ignore_disconnect = false
144
- super(response, SizedQueue.new(10))
145
149
  end
146
150
 
147
151
  def write(string)
148
152
  unless @response.committed?
149
- @response.set_header "Cache-Control", "no-cache"
153
+ @response.headers["Cache-Control"] ||= "no-cache"
150
154
  @response.delete_header "Content-Length"
151
155
  end
152
156
 
@@ -205,7 +209,6 @@ module ActionController
205
209
  end
206
210
 
207
211
  private
208
-
209
212
  def each_chunk(&block)
210
213
  loop do
211
214
  str = nil
@@ -216,11 +219,14 @@ module ActionController
216
219
  yield str
217
220
  end
218
221
  end
222
+
223
+ def build_queue(queue_size)
224
+ queue_size ? SizedQueue.new(queue_size) : Queue.new
225
+ end
219
226
  end
220
227
 
221
228
  class Response < ActionDispatch::Response #:nodoc: all
222
229
  private
223
-
224
230
  def before_committed
225
231
  super
226
232
  jar = request.cookie_jar
@@ -280,33 +286,34 @@ module ActionController
280
286
  raise error if error
281
287
  end
282
288
 
283
- # Spawn a new thread to serve up the controller in. This is to get
284
- # around the fact that Rack isn't based around IOs and we need to use
285
- # a thread to stream data from the response bodies. Nobody should call
286
- # this method except in Rails internals. Seriously!
287
- def new_controller_thread # :nodoc:
288
- Thread.new {
289
- t2 = Thread.current
290
- t2.abort_on_exception = true
291
- yield
292
- }
293
- end
294
-
295
- def log_error(exception)
296
- logger = ActionController::Base.logger
297
- return unless logger
298
-
299
- logger.fatal do
300
- message = "\n#{exception.class} (#{exception.message}):\n".dup
301
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
302
- message << " " << exception.backtrace.join("\n ")
303
- "#{message}\n\n"
304
- end
305
- end
306
-
307
289
  def response_body=(body)
308
290
  super
309
291
  response.close if response
310
292
  end
293
+
294
+ private
295
+ # Spawn a new thread to serve up the controller in. This is to get
296
+ # around the fact that Rack isn't based around IOs and we need to use
297
+ # a thread to stream data from the response bodies. Nobody should call
298
+ # this method except in Rails internals. Seriously!
299
+ def new_controller_thread # :nodoc:
300
+ Thread.new {
301
+ t2 = Thread.current
302
+ t2.abort_on_exception = true
303
+ yield
304
+ }
305
+ end
306
+
307
+ def log_error(exception)
308
+ logger = ActionController::Base.logger
309
+ return unless logger
310
+
311
+ logger.fatal do
312
+ message = +"\n#{exception.class} (#{exception.message}):\n"
313
+ message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
314
+ message << " " << exception.backtrace.join("\n ")
315
+ "#{message}\n\n"
316
+ end
317
+ end
311
318
  end
312
319
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ module Logging
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Set a different log level per request.
9
+ #
10
+ # # Use the debug log level if a particular cookie is set.
11
+ # class ApplicationController < ActionController::Base
12
+ # log_at :debug, if: -> { cookies[:debug] }
13
+ # end
14
+ #
15
+ def log_at(level, **options)
16
+ around_action ->(_, action) { logger.log_at(level, &action) }, **options
17
+ end
18
+ end
19
+ end
20
+ end
@@ -11,7 +11,7 @@ module ActionController #:nodoc:
11
11
  # @people = Person.all
12
12
  # end
13
13
  #
14
- # That action implicitly responds to all formats, but formats can also be whitelisted:
14
+ # That action implicitly responds to all formats, but formats can also be explicitly enumerated:
15
15
  #
16
16
  # def index
17
17
  # @people = Person.all
@@ -105,7 +105,7 @@ module ActionController #:nodoc:
105
105
  #
106
106
  # Mime::Type.register "image/jpg", :jpg
107
107
  #
108
- # Respond to also allows you to specify a common block for different formats by using +any+:
108
+ # +respond_to+ also allows you to specify a common block for different formats by using +any+:
109
109
  #
110
110
  # def index
111
111
  # @people = Person.all
@@ -124,6 +124,14 @@ module ActionController #:nodoc:
124
124
  #
125
125
  # render json: @people
126
126
  #
127
+ # +any+ can also be used with no arguments, in which case it will be used for any format requested by
128
+ # the user:
129
+ #
130
+ # respond_to do |format|
131
+ # format.html
132
+ # format.any { redirect_to support_path }
133
+ # end
134
+ #
127
135
  # Formats can have different variants.
128
136
  #
129
137
  # The request variant is a specialization of the request format, like <tt>:tablet</tt>,
@@ -134,7 +142,7 @@ module ActionController #:nodoc:
134
142
  #
135
143
  # You can set the variant in a +before_action+:
136
144
  #
137
- # request.variant = :tablet if request.user_agent =~ /iPad/
145
+ # request.variant = :tablet if /iPad/.match?(request.user_agent)
138
146
  #
139
147
  # Respond to variants in the action just like you respond to formats:
140
148
  #
@@ -197,8 +205,11 @@ module ActionController #:nodoc:
197
205
  yield collector if block_given?
198
206
 
199
207
  if format = collector.negotiate_format(request)
208
+ if media_type && media_type != format
209
+ raise ActionController::RespondToMismatchError
210
+ end
200
211
  _process_format(format)
201
- _set_rendered_content_type format
212
+ _set_rendered_content_type(format) unless collector.any_response?
202
213
  response = collector.response
203
214
  response.call if response
204
215
  else
@@ -257,6 +268,10 @@ module ActionController #:nodoc:
257
268
  end
258
269
  end
259
270
 
271
+ def any_response?
272
+ !@responses.fetch(format, false) && @responses[Mime::ALL]
273
+ end
274
+
260
275
  def response
261
276
  response = @responses.fetch(format, @responses[Mime::ALL])
262
277
  if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
@@ -12,11 +12,13 @@ module ActionController
12
12
  end
13
13
 
14
14
  def setup_param_encode # :nodoc:
15
- @_parameter_encodings = {}
15
+ @_parameter_encodings = Hash.new { |h, k| h[k] = {} }
16
16
  end
17
17
 
18
- def binary_params_for?(action) # :nodoc:
19
- @_parameter_encodings[action.to_s]
18
+ def action_encoding_template(action) # :nodoc:
19
+ if @_parameter_encodings.has_key?(action.to_s)
20
+ @_parameter_encodings[action.to_s]
21
+ end
20
22
  end
21
23
 
22
24
  # Specify that a given action's parameters should all be encoded as
@@ -44,7 +46,36 @@ module ActionController
44
46
  # encoded as ASCII-8BIT. This is useful in the case where an application
45
47
  # must handle data but encoding of the data is unknown, like file system data.
46
48
  def skip_parameter_encoding(action)
47
- @_parameter_encodings[action.to_s] = true
49
+ @_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
50
+ end
51
+
52
+ # Specify the encoding for a parameter on an action.
53
+ # If not specified the default is UTF-8.
54
+ #
55
+ # You can specify a binary (ASCII_8BIT) parameter with:
56
+ #
57
+ # class RepositoryController < ActionController::Base
58
+ # # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT
59
+ # param_encoding :show, :file_path, Encoding::ASCII_8BIT
60
+ #
61
+ # def show
62
+ # @repo = Repository.find_by_filesystem_path params[:file_path]
63
+ #
64
+ # # params[:repo_name] remains UTF-8 encoded
65
+ # @repo_name = params[:repo_name]
66
+ # end
67
+ #
68
+ # def index
69
+ # @repositories = Repository.all
70
+ # end
71
+ # end
72
+ #
73
+ # The file_path parameter on the show action would be encoded as ASCII-8BIT,
74
+ # but all other arguments will remain UTF-8 encoded.
75
+ # This is useful in the case where an application must handle data
76
+ # but encoding of the data is unknown, like file system data.
77
+ def param_encoding(action, param, encoding)
78
+ @_parameter_encodings[action.to_s][param.to_s] = encoding
48
79
  end
49
80
  end
50
81
  end
@@ -107,15 +107,19 @@ module ActionController
107
107
 
108
108
  unless super || exclude
109
109
  if m.respond_to?(:attribute_names) && m.attribute_names.any?
110
+ self.include = m.attribute_names
111
+
110
112
  if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
111
- self.include = m.attribute_names + m.stored_attributes.values.flatten.map(&:to_s)
112
- else
113
- self.include = m.attribute_names
113
+ self.include += m.stored_attributes.values.flatten.map(&:to_s)
114
+ end
115
+
116
+ if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any?
117
+ self.include += m.attribute_aliases.keys
114
118
  end
115
119
 
116
120
  if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
117
121
  self.include += m.nested_attributes_options.keys.map do |key|
118
- key.to_s.dup.concat("_attributes")
122
+ (+key.to_s).concat("_attributes")
119
123
  end
120
124
  end
121
125
 
@@ -151,7 +155,7 @@ module ActionController
151
155
  # try to find Foo::Bar::User, Foo::User and finally User.
152
156
  def _default_wrap_model
153
157
  return nil if klass.anonymous?
154
- model_name = klass.name.sub(/Controller$/, "").classify
158
+ model_name = klass.name.delete_suffix("Controller").classify
155
159
 
156
160
  begin
157
161
  if model_klass = model_name.safe_constantize
@@ -189,7 +193,7 @@ module ActionController
189
193
  #
190
194
  # wrap_parameters Person
191
195
  # # wraps parameters by determining the wrapper key from Person class
192
- # (+person+, in this case) and the list of attribute names
196
+ # # (+person+, in this case) and the list of attribute names
193
197
  #
194
198
  # wrap_parameters include: [:username, :title]
195
199
  # # wraps only +:username+ and +:title+ attributes from parameters.
@@ -240,24 +244,12 @@ module ActionController
240
244
 
241
245
  # Performs parameters wrapping upon the request. Called automatically
242
246
  # by the metal call stack.
243
- def process_action(*args)
244
- if _wrapper_enabled?
245
- wrapped_hash = _wrap_parameters request.request_parameters
246
- wrapped_keys = request.request_parameters.keys
247
- wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
248
-
249
- # This will make the wrapped hash accessible from controller and view.
250
- request.parameters.merge! wrapped_hash
251
- request.request_parameters.merge! wrapped_hash
252
-
253
- # This will display the wrapped hash in the log file.
254
- request.filtered_parameters.merge! wrapped_filtered_hash
255
- end
247
+ def process_action(*)
248
+ _perform_parameter_wrapping if _wrapper_enabled?
256
249
  super
257
250
  end
258
251
 
259
252
  private
260
-
261
253
  # Returns the wrapper key which will be used to store wrapped parameters.
262
254
  def _wrapper_key
263
255
  _wrapper_options.name
@@ -276,9 +268,11 @@ module ActionController
276
268
  def _extract_parameters(parameters)
277
269
  if include_only = _wrapper_options.include
278
270
  parameters.slice(*include_only)
271
+ elsif _wrapper_options.exclude
272
+ exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
273
+ parameters.except(*exclude)
279
274
  else
280
- exclude = _wrapper_options.exclude || []
281
- parameters.except(*(exclude + EXCLUDE_PARAMETERS))
275
+ parameters.except(*EXCLUDE_PARAMETERS)
282
276
  end
283
277
  end
284
278
 
@@ -287,7 +281,23 @@ module ActionController
287
281
  return false unless request.has_content_type?
288
282
 
289
283
  ref = request.content_mime_type.ref
284
+
290
285
  _wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
286
+ rescue ActionDispatch::Http::Parameters::ParseError
287
+ false
288
+ end
289
+
290
+ def _perform_parameter_wrapping
291
+ wrapped_hash = _wrap_parameters request.request_parameters
292
+ wrapped_keys = request.request_parameters.keys
293
+ wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
294
+
295
+ # This will make the wrapped hash accessible from controller and view.
296
+ request.parameters.merge! wrapped_hash
297
+ request.request_parameters.merge! wrapped_hash
298
+
299
+ # This will display the wrapped hash in the log file.
300
+ request.filtered_parameters.merge! wrapped_filtered_hash
291
301
  end
292
302
  end
293
303
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController #:nodoc:
4
+ # HTTP Permissions Policy is a web standard for defining a mechanism to
5
+ # allow and deny the use of browser permissions in its own context, and
6
+ # in content within any <iframe> elements in the document.
7
+ #
8
+ # Full details of HTTP Permissions Policy specification and guidelines can
9
+ # be found at MDN:
10
+ #
11
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
12
+ #
13
+ # Examples of usage:
14
+ #
15
+ # # Global policy
16
+ # Rails.application.config.permissions_policy do |f|
17
+ # f.camera :none
18
+ # f.gyroscope :none
19
+ # f.microphone :none
20
+ # f.usb :none
21
+ # f.fullscreen :self
22
+ # f.payment :self, "https://secure.example.com"
23
+ # end
24
+ #
25
+ # # Controller level policy
26
+ # class PagesController < ApplicationController
27
+ # permissions_policy do |p|
28
+ # p.geolocation "https://example.com"
29
+ # end
30
+ # end
31
+ module PermissionsPolicy
32
+ extend ActiveSupport::Concern
33
+
34
+ module ClassMethods
35
+ def permissions_policy(**options, &block)
36
+ before_action(options) do
37
+ if block_given?
38
+ policy = request.permissions_policy.clone
39
+ yield policy
40
+ request.permissions_policy = policy
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -55,11 +55,11 @@ module ActionController
55
55
  # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
56
56
  # To terminate the execution of the function immediately after the +redirect_to+, use return.
57
57
  # redirect_to post_url(@post) and return
58
- def redirect_to(options = {}, response_status = {})
58
+ def redirect_to(options = {}, response_options = {})
59
59
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
60
60
  raise AbstractController::DoubleRenderError if response_body
61
61
 
62
- self.status = _extract_redirect_to_status(options, response_status)
62
+ self.status = _extract_redirect_to_status(options, response_options)
63
63
  self.location = _compute_redirect_to_location(request, options)
64
64
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
65
65
  end
@@ -85,7 +85,7 @@ module ActionController
85
85
  # * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
86
86
  # * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
87
87
  #
88
- # All other options that can be passed to <tt>redirect_to</tt> are accepted as
88
+ # All other options that can be passed to #redirect_to are accepted as
89
89
  # options and the behavior is identical.
90
90
  def redirect_back(fallback_location:, allow_other_host: true, **args)
91
91
  referer = request.headers["Referer"]
@@ -114,11 +114,11 @@ module ActionController
114
114
  public :_compute_redirect_to_location
115
115
 
116
116
  private
117
- def _extract_redirect_to_status(options, response_status)
117
+ def _extract_redirect_to_status(options, response_options)
118
118
  if options.is_a?(Hash) && options.key?(:status)
119
119
  Rack::Utils.status_code(options.delete(:status))
120
- elsif response_status.key?(:status)
121
- Rack::Utils.status_code(response_status[:status])
120
+ elsif response_options.key?(:status)
121
+ Rack::Utils.status_code(response_options[:status])
122
122
  else
123
123
  302
124
124
  end
@@ -157,24 +157,24 @@ module ActionController
157
157
  json = json.to_json(options) unless json.kind_of?(String)
158
158
 
159
159
  if options[:callback].present?
160
- if content_type.nil? || content_type == Mime[:json]
160
+ if media_type.nil? || media_type == Mime[:json]
161
161
  self.content_type = Mime[:js]
162
162
  end
163
163
 
164
164
  "/**/#{options[:callback]}(#{json})"
165
165
  else
166
- self.content_type ||= Mime[:json]
166
+ self.content_type = Mime[:json] if media_type.nil?
167
167
  json
168
168
  end
169
169
  end
170
170
 
171
171
  add :js do |js, options|
172
- self.content_type ||= Mime[:js]
172
+ self.content_type = Mime[:js] if media_type.nil?
173
173
  js.respond_to?(:to_js) ? js.to_js(options) : js
174
174
  end
175
175
 
176
176
  add :xml do |xml, options|
177
- self.content_type ||= Mime[:xml]
177
+ self.content_type = Mime[:xml] if media_type.nil?
178
178
  xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
179
179
  end
180
180
  end
@@ -40,7 +40,7 @@ module ActionController
40
40
  def render_to_string(*)
41
41
  result = super
42
42
  if result.respond_to?(:each)
43
- string = "".dup
43
+ string = +""
44
44
  result.each { |r| string << r }
45
45
  string
46
46
  else
@@ -53,7 +53,6 @@ module ActionController
53
53
  end
54
54
 
55
55
  private
56
-
57
56
  def _process_variant(options)
58
57
  if defined?(request) && !request.nil? && request.variant.present?
59
58
  options[:variant] = request.variant
@@ -73,11 +72,17 @@ module ActionController
73
72
  end
74
73
 
75
74
  def _set_rendered_content_type(format)
76
- if format && !response.content_type
75
+ if format && !response.media_type
77
76
  self.content_type = format.to_s
78
77
  end
79
78
  end
80
79
 
80
+ def _set_vary_header
81
+ if self.headers["Vary"].blank? && request.should_apply_vary_header?
82
+ self.headers["Vary"] = "Accept"
83
+ end
84
+ end
85
+
81
86
  # Normalize arguments by catching blocks and setting them on :update.
82
87
  def _normalize_args(action = nil, options = {}, &blk)
83
88
  options = super