actionpack 6.0.5.1 → 6.1.7.1

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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +393 -253
  3. data/MIT-LICENSE +1 -2
  4. data/lib/abstract_controller/base.rb +35 -2
  5. data/lib/abstract_controller/callbacks.rb +2 -2
  6. data/lib/abstract_controller/collector.rb +4 -2
  7. data/lib/abstract_controller/helpers.rb +105 -90
  8. data/lib/abstract_controller/railties/routes_helpers.rb +17 -1
  9. data/lib/abstract_controller/rendering.rb +9 -9
  10. data/lib/abstract_controller/translation.rb +8 -2
  11. data/lib/abstract_controller.rb +1 -0
  12. data/lib/action_controller/api.rb +2 -2
  13. data/lib/action_controller/base.rb +4 -2
  14. data/lib/action_controller/caching.rb +0 -1
  15. data/lib/action_controller/log_subscriber.rb +3 -3
  16. data/lib/action_controller/metal/conditional_get.rb +11 -3
  17. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  18. data/lib/action_controller/metal/cookies.rb +3 -1
  19. data/lib/action_controller/metal/data_streaming.rb +1 -1
  20. data/lib/action_controller/metal/etag_with_template_digest.rb +3 -5
  21. data/lib/action_controller/metal/exceptions.rb +33 -0
  22. data/lib/action_controller/metal/head.rb +7 -4
  23. data/lib/action_controller/metal/helpers.rb +11 -1
  24. data/lib/action_controller/metal/http_authentication.rb +5 -2
  25. data/lib/action_controller/metal/implicit_render.rb +1 -1
  26. data/lib/action_controller/metal/instrumentation.rb +11 -9
  27. data/lib/action_controller/metal/live.rb +10 -1
  28. data/lib/action_controller/metal/logging.rb +20 -0
  29. data/lib/action_controller/metal/mime_responds.rb +6 -2
  30. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  31. data/lib/action_controller/metal/params_wrapper.rb +14 -8
  32. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  33. data/lib/action_controller/metal/redirecting.rb +1 -1
  34. data/lib/action_controller/metal/rendering.rb +6 -0
  35. data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
  36. data/lib/action_controller/metal/rescue.rb +1 -1
  37. data/lib/action_controller/metal/strong_parameters.rb +104 -16
  38. data/lib/action_controller/metal.rb +2 -2
  39. data/lib/action_controller/renderer.rb +23 -13
  40. data/lib/action_controller/test_case.rb +65 -56
  41. data/lib/action_controller.rb +2 -3
  42. data/lib/action_dispatch/http/cache.rb +18 -17
  43. data/lib/action_dispatch/http/content_security_policy.rb +6 -1
  44. data/lib/action_dispatch/http/filter_parameters.rb +1 -1
  45. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  46. data/lib/action_dispatch/http/headers.rb +3 -2
  47. data/lib/action_dispatch/http/mime_negotiation.rb +14 -8
  48. data/lib/action_dispatch/http/mime_type.rb +29 -16
  49. data/lib/action_dispatch/http/parameters.rb +1 -19
  50. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  51. data/lib/action_dispatch/http/request.rb +24 -8
  52. data/lib/action_dispatch/http/response.rb +17 -16
  53. data/lib/action_dispatch/http/url.rb +3 -2
  54. data/lib/action_dispatch/journey/formatter.rb +55 -30
  55. data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
  56. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  57. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
  58. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  59. data/lib/action_dispatch/journey/nodes/node.rb +4 -3
  60. data/lib/action_dispatch/journey/parser.rb +13 -13
  61. data/lib/action_dispatch/journey/parser.y +1 -1
  62. data/lib/action_dispatch/journey/path/pattern.rb +13 -18
  63. data/lib/action_dispatch/journey/route.rb +7 -18
  64. data/lib/action_dispatch/journey/router/utils.rb +6 -4
  65. data/lib/action_dispatch/journey/router.rb +26 -30
  66. data/lib/action_dispatch/journey/visitors.rb +1 -1
  67. data/lib/action_dispatch/journey.rb +0 -2
  68. data/lib/action_dispatch/middleware/actionable_exceptions.rb +1 -1
  69. data/lib/action_dispatch/middleware/cookies.rb +89 -46
  70. data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -15
  71. data/lib/action_dispatch/middleware/debug_view.rb +1 -1
  72. data/lib/action_dispatch/middleware/exception_wrapper.rb +28 -16
  73. data/lib/action_dispatch/middleware/host_authorization.rb +63 -14
  74. data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
  75. data/lib/action_dispatch/middleware/request_id.rb +4 -5
  76. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
  77. data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
  78. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -0
  79. data/lib/action_dispatch/middleware/ssl.rb +12 -7
  80. data/lib/action_dispatch/middleware/stack.rb +19 -1
  81. data/lib/action_dispatch/middleware/static.rb +154 -93
  82. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  83. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
  84. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
  85. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
  86. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +100 -8
  87. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  88. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +21 -1
  89. data/lib/action_dispatch/railtie.rb +3 -2
  90. data/lib/action_dispatch/request/session.rb +2 -8
  91. data/lib/action_dispatch/request/utils.rb +26 -2
  92. data/lib/action_dispatch/routing/inspector.rb +8 -7
  93. data/lib/action_dispatch/routing/mapper.rb +102 -71
  94. data/lib/action_dispatch/routing/polymorphic_routes.rb +12 -11
  95. data/lib/action_dispatch/routing/redirection.rb +4 -4
  96. data/lib/action_dispatch/routing/route_set.rb +49 -41
  97. data/lib/action_dispatch/system_test_case.rb +35 -24
  98. data/lib/action_dispatch/system_testing/browser.rb +33 -27
  99. data/lib/action_dispatch/system_testing/driver.rb +6 -7
  100. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
  101. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
  102. data/lib/action_dispatch/testing/assertions/response.rb +2 -4
  103. data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
  104. data/lib/action_dispatch/testing/assertions.rb +1 -1
  105. data/lib/action_dispatch/testing/integration.rb +40 -29
  106. data/lib/action_dispatch/testing/test_process.rb +32 -4
  107. data/lib/action_dispatch/testing/test_request.rb +3 -3
  108. data/lib/action_dispatch.rb +3 -2
  109. data/lib/action_pack/gem_version.rb +2 -2
  110. data/lib/action_pack.rb +1 -1
  111. metadata +18 -19
  112. data/lib/action_controller/metal/force_ssl.rb +0 -58
  113. data/lib/action_dispatch/http/parameter_filter.rb +0 -12
  114. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  115. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  116. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
53
53
  #
54
54
  # Show a 404 page in the browser:
55
55
  #
56
- # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
56
+ # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', disposition: 'inline', status: 404
57
57
  #
58
58
  # Read about the other Content-* HTTP headers if you'd like to
59
59
  # provide the user with more information (such as Content-Description) in
@@ -26,10 +26,8 @@ module ActionController
26
26
  included do
27
27
  class_attribute :etag_with_template_digest, default: true
28
28
 
29
- ActiveSupport.on_load :action_view, yield: true do
30
- etag do |options|
31
- determine_template_etag(options) if etag_with_template_digest
32
- end
29
+ etag do |options|
30
+ determine_template_etag(options) if etag_with_template_digest
33
31
  end
34
32
  end
35
33
 
@@ -46,7 +44,7 @@ module ActionController
46
44
  # template digest from the ETag.
47
45
  def pick_template_for_etag(options)
48
46
  unless options[:template] == false
49
- options[:template] || "#{controller_path}/#{action_name}"
47
+ options[:template] || lookup_context.find_all(action_name, _prefixes).first&.virtual_path
50
48
  end
51
49
  end
52
50
 
@@ -23,6 +23,39 @@ module ActionController
23
23
  end
24
24
 
25
25
  class UrlGenerationError < ActionControllerError #:nodoc:
26
+ attr_reader :routes, :route_name, :method_name
27
+
28
+ def initialize(message, routes = nil, route_name = nil, method_name = nil)
29
+ @routes = routes
30
+ @route_name = route_name
31
+ @method_name = method_name
32
+
33
+ super(message)
34
+ end
35
+
36
+ class Correction
37
+ def initialize(error)
38
+ @error = error
39
+ end
40
+
41
+ def corrections
42
+ if @error.method_name
43
+ maybe_these = @error.routes.named_routes.helper_names.grep(/#{@error.route_name}/)
44
+ maybe_these -= [@error.method_name.to_s] # remove exact match
45
+
46
+ maybe_these.sort_by { |n|
47
+ DidYouMean::Jaro.distance(@error.route_name, n)
48
+ }.reverse.first(4)
49
+ else
50
+ []
51
+ end
52
+ end
53
+ end
54
+
55
+ # We may not have DYM, and DYM might not let us register error handlers
56
+ if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
57
+ DidYouMean.correct_error(self, Correction)
58
+ end
26
59
  end
27
60
 
28
61
  class MethodNotAllowed < ActionControllerError #:nodoc:
@@ -29,19 +29,22 @@ module ActionController
29
29
  content_type = options.delete(:content_type)
30
30
 
31
31
  options.each do |key, value|
32
- headers[key.to_s.dasherize.split("-").each { |v| v[0] = v[0].chr.upcase }.join("-")] = value.to_s
32
+ headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
33
33
  end
34
34
 
35
35
  self.status = status
36
36
  self.location = url_for(location) if location
37
37
 
38
- self.response_body = ""
39
-
40
38
  if include_content?(response_code)
41
- self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
39
+ unless self.media_type
40
+ self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
41
+ end
42
+
42
43
  response.charset = false
43
44
  end
44
45
 
46
+ self.response_body = ""
47
+
45
48
  true
46
49
  end
47
50
 
@@ -11,7 +11,12 @@ module ActionController
11
11
  #
12
12
  # In previous versions of \Rails the controller will include a helper which
13
13
  # matches the name of the controller, e.g., <tt>MyController</tt> will automatically
14
- # include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
14
+ # include <tt>MyHelper</tt>. You can revert to the old behavior with the following:
15
+ #
16
+ # # config/application.rb
17
+ # class Application < Rails::Application
18
+ # config.action_controller.include_all_helpers = false
19
+ # end
15
20
  #
16
21
  # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
17
22
  # controller which inherits from it.
@@ -73,6 +78,11 @@ module ActionController
73
78
  end
74
79
 
75
80
  # Provides a proxy to access helper methods from outside the view.
81
+ #
82
+ # Note that the proxy is rendered under a different view context.
83
+ # This may cause incorrect behaviour with capture methods. Consider
84
+ # using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper]
85
+ # instead when using +capture+.
76
86
  def helpers
77
87
  @helper_proxy ||= begin
78
88
  proxy = ActionView::Base.empty
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "base64"
4
4
  require "active_support/security_utils"
5
+ require "active_support/core_ext/array/access"
5
6
 
6
7
  module ActionController
7
8
  # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
@@ -76,6 +77,8 @@ module ActionController
76
77
 
77
78
  def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
78
79
  authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
80
+ # This comparison uses & so that it doesn't short circuit and
81
+ # uses `secure_compare` so that length information isn't leaked.
79
82
  ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
80
83
  ActiveSupport::SecurityUtils.secure_compare(given_password, password)
81
84
  end
@@ -136,7 +139,7 @@ module ActionController
136
139
  #
137
140
  # === Simple \Digest example
138
141
  #
139
- # require 'digest/md5'
142
+ # require "digest/md5"
140
143
  # class PostsController < ApplicationController
141
144
  # REALM = "SuperSecret"
142
145
  # USERS = {"dhh" => "secret", #plain text password
@@ -482,7 +485,7 @@ module ActionController
482
485
  def raw_params(auth)
483
486
  _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
484
487
 
485
- if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
488
+ if !_raw_params.first&.start_with?(TOKEN_KEY)
486
489
  _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
487
490
  end
488
491
 
@@ -22,7 +22,7 @@ module ActionController
22
22
  # Third, if we DON'T find a template AND the request is a page load in a web
23
23
  # browser (technically, a non-XHR GET request for an HTML response) where
24
24
  # you reasonably expect to have rendered a template, then we raise
25
- # <tt>ActionView::UnknownFormat</tt> with an explanation.
25
+ # <tt>ActionController::MissingExactTemplate</tt> with an explanation.
26
26
  #
27
27
  # Finally, if we DON'T find a template AND the request isn't a browser page
28
28
  # load, then we implicitly respond with <tt>204 No Content</tt>.
@@ -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,18 +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
- super.tap do
34
- payload[:status] = response.status
35
- end
34
+ result = super
35
+ payload[:response] = response
36
+ payload[:status] = response.status
37
+ result
36
38
  ensure
37
39
  append_info_to_payload(payload)
38
40
  end
39
41
  end
40
42
 
41
- def render(*args)
43
+ def render(*)
42
44
  render_output = nil
43
45
  self.view_runtime = cleanup_view_runtime do
44
46
  Benchmark.ms { render_output = super }
@@ -59,8 +61,8 @@ module ActionController
59
61
  end
60
62
  end
61
63
 
62
- def redirect_to(*args)
63
- 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|
64
66
  result = super
65
67
  payload[:status] = response.status
66
68
  payload[:location] = response.filtered_location
@@ -70,7 +72,7 @@ module ActionController
70
72
 
71
73
  private
72
74
  # A hook invoked every time a before callback is halted.
73
- def halted_callback_hook(filter)
75
+ def halted_callback_hook(filter, _)
74
76
  ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
75
77
  end
76
78
 
@@ -127,6 +127,11 @@ module ActionController
127
127
  class Buffer < ActionDispatch::Response::Buffer #:nodoc:
128
128
  include MonitorMixin
129
129
 
130
+ class << self
131
+ attr_accessor :queue_size
132
+ end
133
+ @queue_size = 10
134
+
130
135
  # Ignore that the client has disconnected.
131
136
  #
132
137
  # If this value is `true`, calling `write` after the client
@@ -136,11 +141,11 @@ module ActionController
136
141
  attr_accessor :ignore_disconnect
137
142
 
138
143
  def initialize(response)
144
+ super(response, build_queue(self.class.queue_size))
139
145
  @error_callback = lambda { true }
140
146
  @cv = new_cond
141
147
  @aborted = false
142
148
  @ignore_disconnect = false
143
- super(response, SizedQueue.new(10))
144
149
  end
145
150
 
146
151
  def write(string)
@@ -214,6 +219,10 @@ module ActionController
214
219
  yield str
215
220
  end
216
221
  end
222
+
223
+ def build_queue(queue_size)
224
+ queue_size ? SizedQueue.new(queue_size) : Queue.new
225
+ end
217
226
  end
218
227
 
219
228
  class Response < ActionDispatch::Response #:nodoc: all
@@ -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
@@ -142,7 +142,7 @@ module ActionController #:nodoc:
142
142
  #
143
143
  # You can set the variant in a +before_action+:
144
144
  #
145
- # request.variant = :tablet if request.user_agent =~ /iPad/
145
+ # request.variant = :tablet if /iPad/.match?(request.user_agent)
146
146
  #
147
147
  # Respond to variants in the action just like you respond to formats:
148
148
  #
@@ -209,7 +209,7 @@ module ActionController #:nodoc:
209
209
  raise ActionController::RespondToMismatchError
210
210
  end
211
211
  _process_format(format)
212
- _set_rendered_content_type format
212
+ _set_rendered_content_type(format) unless collector.any_response?
213
213
  response = collector.response
214
214
  response.call if response
215
215
  else
@@ -268,6 +268,10 @@ module ActionController #:nodoc:
268
268
  end
269
269
  end
270
270
 
271
+ def any_response?
272
+ !@responses.fetch(format, false) && @responses[Mime::ALL]
273
+ end
274
+
271
275
  def response
272
276
  response = @responses.fetch(format, @responses[Mime::ALL])
273
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,10 +107,14 @@ 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?
@@ -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,7 +244,7 @@ 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)
247
+ def process_action(*)
244
248
  _perform_parameter_wrapping if _wrapper_enabled?
245
249
  super
246
250
  end
@@ -264,9 +268,11 @@ module ActionController
264
268
  def _extract_parameters(parameters)
265
269
  if include_only = _wrapper_options.include
266
270
  parameters.slice(*include_only)
271
+ elsif _wrapper_options.exclude
272
+ exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
273
+ parameters.except(*exclude)
267
274
  else
268
- exclude = _wrapper_options.exclude || []
269
- parameters.except(*(exclude + EXCLUDE_PARAMETERS))
275
+ parameters.except(*EXCLUDE_PARAMETERS)
270
276
  end
271
277
  end
272
278
 
@@ -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
@@ -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"]
@@ -77,6 +77,12 @@ module ActionController
77
77
  end
78
78
  end
79
79
 
80
+ def _set_vary_header
81
+ if response.headers["Vary"].blank? && request.should_apply_vary_header?
82
+ response.headers["Vary"] = "Accept"
83
+ end
84
+ end
85
+
80
86
  # Normalize arguments by catching blocks and setting them on :update.
81
87
  def _normalize_args(action = nil, options = {}, &blk)
82
88
  options = super
@@ -386,7 +386,7 @@ module ActionController #:nodoc:
386
386
  if per_form_csrf_tokens
387
387
  correct_token = per_form_csrf_token(
388
388
  session,
389
- normalize_action_path(request.fullpath),
389
+ request.path.chomp("/"),
390
390
  request.request_method
391
391
  )
392
392
 
@@ -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?