actionpack 4.2.11.1 → 6.1.3.2

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,16 +1,16 @@
1
- require 'active_support/core_ext/hash/slice'
2
- require 'active_support/core_ext/hash/except'
3
- require 'active_support/core_ext/module/anonymous'
4
- require 'active_support/core_ext/struct'
5
- require 'action_dispatch/http/mime_type'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/slice"
4
+ require "active_support/core_ext/hash/except"
5
+ require "active_support/core_ext/module/anonymous"
6
+ require "action_dispatch/http/mime_type"
6
7
 
7
8
  module ActionController
8
9
  # Wraps the parameters hash into a nested hash. This will allow clients to
9
10
  # submit requests without having to specify any root elements.
10
11
  #
11
12
  # This functionality is enabled in +config/initializers/wrap_parameters.rb+
12
- # and can be customized. If you are upgrading to \Rails 3.1, this file will
13
- # need to be created for the functionality to be enabled.
13
+ # and can be customized.
14
14
  #
15
15
  # You could also turn it on per controller by setting the format array to
16
16
  # a non-empty array:
@@ -42,7 +42,7 @@ module ActionController
42
42
  # wrap_parameters :person, include: [:username, :password]
43
43
  # end
44
44
  #
45
- # On ActiveRecord models with no +:include+ or +:exclude+ option set,
45
+ # On Active Record models with no +:include+ or +:exclude+ option set,
46
46
  # it will only wrap the parameters returned by the class method
47
47
  # <tt>attribute_names</tt>.
48
48
  #
@@ -73,7 +73,7 @@ module ActionController
73
73
 
74
74
  EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
75
75
 
76
- require 'mutex_m'
76
+ require "mutex_m"
77
77
 
78
78
  class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc:
79
79
  include Mutex_m
@@ -86,14 +86,14 @@ module ActionController
86
86
  new name, format, include, exclude, nil, nil
87
87
  end
88
88
 
89
- def initialize(name, format, include, exclude, klass, model) # nodoc
89
+ def initialize(name, format, include, exclude, klass, model) # :nodoc:
90
90
  super
91
91
  @include_set = include
92
92
  @name_set = name
93
93
  end
94
94
 
95
95
  def model
96
- super || synchronize { super || self.model = _default_wrap_model }
96
+ super || self.model = _default_wrap_model
97
97
  end
98
98
 
99
99
  def include
@@ -108,6 +108,22 @@ module ActionController
108
108
  unless super || exclude
109
109
  if m.respond_to?(:attribute_names) && m.attribute_names.any?
110
110
  self.include = m.attribute_names
111
+
112
+ if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
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
118
+ end
119
+
120
+ if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
121
+ self.include += m.nested_attributes_options.keys.map do |key|
122
+ (+key.to_s).concat("_attributes")
123
+ end
124
+ end
125
+
126
+ self.include
111
127
  end
112
128
  end
113
129
  end
@@ -130,35 +146,34 @@ module ActionController
130
146
  end
131
147
 
132
148
  private
133
- # Determine the wrapper model from the controller's name. By convention,
134
- # this could be done by trying to find the defined model that has the
135
- # same singularize name as the controller. For example, +UsersController+
136
- # will try to find if the +User+ model exists.
137
- #
138
- # This method also does namespace lookup. Foo::Bar::UsersController will
139
- # try to find Foo::Bar::User, Foo::User and finally User.
140
- def _default_wrap_model #:nodoc:
141
- return nil if klass.anonymous?
142
- model_name = klass.name.sub(/Controller$/, '').classify
143
-
144
- begin
145
- if model_klass = model_name.safe_constantize
146
- model_klass
147
- else
148
- namespaces = model_name.split("::")
149
- namespaces.delete_at(-2)
150
- break if namespaces.last == model_name
151
- model_name = namespaces.join("::")
152
- end
153
- end until model_klass
149
+ # Determine the wrapper model from the controller's name. By convention,
150
+ # this could be done by trying to find the defined model that has the
151
+ # same singular name as the controller. For example, +UsersController+
152
+ # will try to find if the +User+ model exists.
153
+ #
154
+ # This method also does namespace lookup. Foo::Bar::UsersController will
155
+ # try to find Foo::Bar::User, Foo::User and finally User.
156
+ def _default_wrap_model
157
+ return nil if klass.anonymous?
158
+ model_name = klass.name.delete_suffix("Controller").classify
159
+
160
+ begin
161
+ if model_klass = model_name.safe_constantize
162
+ model_klass
163
+ else
164
+ namespaces = model_name.split("::")
165
+ namespaces.delete_at(-2)
166
+ break if namespaces.last == model_name
167
+ model_name = namespaces.join("::")
168
+ end
169
+ end until model_klass
154
170
 
155
- model_klass
156
- end
171
+ model_klass
172
+ end
157
173
  end
158
174
 
159
175
  included do
160
- class_attribute :_wrapper_options
161
- self._wrapper_options = Options.from_hash(format: [])
176
+ class_attribute :_wrapper_options, default: Options.from_hash(format: [])
162
177
  end
163
178
 
164
179
  module ClassMethods
@@ -178,7 +193,7 @@ module ActionController
178
193
  #
179
194
  # wrap_parameters Person
180
195
  # # wraps parameters by determining the wrapper key from Person class
181
- # (+person+, in this case) and the list of attribute names
196
+ # # (+person+, in this case) and the list of attribute names
182
197
  #
183
198
  # wrap_parameters include: [:username, :title]
184
199
  # # wraps only +:username+ and +:title+ attributes from parameters.
@@ -200,14 +215,14 @@ module ActionController
200
215
  when Hash
201
216
  options = name_or_model_or_options
202
217
  when false
203
- options = options.merge(:format => [])
218
+ options = options.merge(format: [])
204
219
  when Symbol, String
205
- options = options.merge(:name => name_or_model_or_options)
220
+ options = options.merge(name: name_or_model_or_options)
206
221
  else
207
222
  model = name_or_model_or_options
208
223
  end
209
224
 
210
- opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
225
+ opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
211
226
  opts.model = model
212
227
  opts.klass = self
213
228
 
@@ -215,7 +230,7 @@ module ActionController
215
230
  end
216
231
 
217
232
  # Sets the default wrapper key or model which will be used to determine
218
- # wrapper key and attribute names. Will be called automatically when the
233
+ # wrapper key and attribute names. Called automatically when the
219
234
  # module is inherited.
220
235
  def inherited(klass)
221
236
  if klass._wrapper_options.format.any?
@@ -227,32 +242,15 @@ module ActionController
227
242
  end
228
243
  end
229
244
 
230
- # Performs parameters wrapping upon the request. Will be called automatically
245
+ # Performs parameters wrapping upon the request. Called automatically
231
246
  # by the metal call stack.
232
- def process_action(*args)
233
- if _wrapper_enabled?
234
- if request.parameters[_wrapper_key].present?
235
- wrapped_hash = _extract_parameters(request.parameters)
236
- else
237
- wrapped_hash = _wrap_parameters request.request_parameters
238
- end
239
-
240
- wrapped_keys = request.request_parameters.keys
241
- wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
242
-
243
- # This will make the wrapped hash accessible from controller and view
244
- request.parameters.merge! wrapped_hash
245
- request.request_parameters.merge! wrapped_hash
246
-
247
- # This will display the wrapped hash in the log file
248
- request.filtered_parameters.merge! wrapped_filtered_hash
249
- end
247
+ def process_action(*)
248
+ _perform_parameter_wrapping if _wrapper_enabled?
250
249
  super
251
250
  end
252
251
 
253
252
  private
254
-
255
- # Returns the wrapper key which will be used to stored wrapped parameters.
253
+ # Returns the wrapper key which will be used to store wrapped parameters.
256
254
  def _wrapper_key
257
255
  _wrapper_options.name
258
256
  end
@@ -270,16 +268,35 @@ module ActionController
270
268
  def _extract_parameters(parameters)
271
269
  if include_only = _wrapper_options.include
272
270
  parameters.slice(*include_only)
271
+ elsif _wrapper_options.exclude
272
+ exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
273
+ parameters.except(*exclude)
273
274
  else
274
- exclude = _wrapper_options.exclude || []
275
- parameters.except(*(exclude + EXCLUDE_PARAMETERS))
275
+ parameters.except(*EXCLUDE_PARAMETERS)
276
276
  end
277
277
  end
278
278
 
279
279
  # Checks if we should perform parameters wrapping.
280
280
  def _wrapper_enabled?
281
- ref = request.content_mime_type.try(:ref)
282
- _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
281
+ return false unless request.has_content_type?
282
+
283
+ ref = request.content_mime_type.ref
284
+ _wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
285
+ end
286
+
287
+ def _perform_parameter_wrapping
288
+ wrapped_hash = _wrap_parameters request.request_parameters
289
+ wrapped_keys = request.request_parameters.keys
290
+ wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
291
+
292
+ # This will make the wrapped hash accessible from controller and view.
293
+ request.parameters.merge! wrapped_hash
294
+ request.request_parameters.merge! wrapped_hash
295
+
296
+ # This will display the wrapped hash in the log file.
297
+ request.filtered_parameters.merge! wrapped_filtered_hash
298
+ rescue ActionDispatch::Http::Parameters::ParseError
299
+ # swallow parse error exception
283
300
  end
284
301
  end
285
302
  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
@@ -1,17 +1,10 @@
1
- module ActionController
2
- class RedirectBackError < AbstractController::Error #:nodoc:
3
- DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
4
-
5
- def initialize(message = nil)
6
- super(message || DEFAULT_MESSAGE)
7
- end
8
- end
1
+ # frozen_string_literal: true
9
2
 
3
+ module ActionController
10
4
  module Redirecting
11
5
  extend ActiveSupport::Concern
12
6
 
13
7
  include AbstractController::Logger
14
- include ActionController::RackDelegation
15
8
  include ActionController::UrlFor
16
9
 
17
10
  # Redirects the browser to the target specified in +options+. This parameter can be any one of:
@@ -21,34 +14,31 @@ module ActionController
21
14
  # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
22
15
  # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
23
16
  # * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
24
- # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
25
- # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
26
17
  #
27
18
  # === Examples:
28
19
  #
29
20
  # redirect_to action: "show", id: 5
30
- # redirect_to post
21
+ # redirect_to @post
31
22
  # redirect_to "http://www.rubyonrails.org"
32
23
  # redirect_to "/images/screenshot.jpg"
33
- # redirect_to articles_url
34
- # redirect_to :back
24
+ # redirect_to posts_url
35
25
  # redirect_to proc { edit_post_url(@post) }
36
26
  #
37
- # The redirection happens as a "302 Found" header unless otherwise specified using the <tt>:status</tt> option:
27
+ # The redirection happens as a <tt>302 Found</tt> header unless otherwise specified using the <tt>:status</tt> option:
38
28
  #
39
29
  # redirect_to post_url(@post), status: :found
40
30
  # redirect_to action: 'atom', status: :moved_permanently
41
31
  # redirect_to post_url(@post), status: 301
42
32
  # redirect_to action: 'atom', status: 302
43
33
  #
44
- # The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
34
+ # The status code can either be a standard {HTTP Status code}[https://www.iana.org/assignments/http-status-codes] as an
45
35
  # integer, or a symbol representing the downcased, underscored and symbolized description.
46
36
  # Note that the status code must be a 3xx HTTP code, or redirection will not occur.
47
37
  #
48
38
  # If you are using XHR requests other than GET or POST and redirecting after the
49
39
  # request then some browsers will follow the redirect using the original request
50
40
  # method. This may lead to undesirable behavior such as a double DELETE. To work
51
- # around this you can return a <tt>303 See Other</tt> status code which will be
41
+ # around this you can return a <tt>303 See Other</tt> status code which will be
52
42
  # followed using a GET request.
53
43
  #
54
44
  # redirect_to posts_url, status: :see_other
@@ -62,18 +52,45 @@ module ActionController
62
52
  # redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
63
53
  # redirect_to({ action: 'atom' }, alert: "Something serious happened")
64
54
  #
65
- # When using <tt>redirect_to :back</tt>, if there is no referrer,
66
- # <tt>ActionController::RedirectBackError</tt> will be raised. You
67
- # may specify some fallback behavior for this case by rescuing
68
- # <tt>ActionController::RedirectBackError</tt>.
69
- def redirect_to(options = {}, response_status = {}) #:doc:
55
+ # Statements after +redirect_to+ in our controller get executed, so +redirect_to+ doesn't stop the execution of the function.
56
+ # To terminate the execution of the function immediately after the +redirect_to+, use return.
57
+ # redirect_to post_url(@post) and return
58
+ def redirect_to(options = {}, response_options = {})
70
59
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
71
- raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
72
60
  raise AbstractController::DoubleRenderError if response_body
73
61
 
74
- self.status = _extract_redirect_to_status(options, response_status)
62
+ self.status = _extract_redirect_to_status(options, response_options)
75
63
  self.location = _compute_redirect_to_location(request, options)
76
- self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
64
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
65
+ end
66
+
67
+ # Redirects the browser to the page that issued the request (the referrer)
68
+ # if possible, otherwise redirects to the provided default fallback
69
+ # location.
70
+ #
71
+ # The referrer information is pulled from the HTTP +Referer+ (sic) header on
72
+ # the request. This is an optional header and its presence on the request is
73
+ # subject to browser security settings and user preferences. If the request
74
+ # is missing this header, the <tt>fallback_location</tt> will be used.
75
+ #
76
+ # redirect_back fallback_location: { action: "show", id: 5 }
77
+ # redirect_back fallback_location: @post
78
+ # redirect_back fallback_location: "http://www.rubyonrails.org"
79
+ # redirect_back fallback_location: "/images/screenshot.jpg"
80
+ # redirect_back fallback_location: posts_url
81
+ # redirect_back fallback_location: proc { edit_post_url(@post) }
82
+ # redirect_back fallback_location: '/', allow_other_host: false
83
+ #
84
+ # ==== Options
85
+ # * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
86
+ # * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
87
+ #
88
+ # All other options that can be passed to #redirect_to are accepted as
89
+ # options and the behavior is identical.
90
+ def redirect_back(fallback_location:, allow_other_host: true, **args)
91
+ referer = request.headers["Referer"]
92
+ redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer))
93
+ redirect_to redirect_to_referer ? referer : fallback_location, **args
77
94
  end
78
95
 
79
96
  def _compute_redirect_to_location(request, options) #:nodoc:
@@ -81,16 +98,14 @@ module ActionController
81
98
  # The scheme name consist of a letter followed by any combination of
82
99
  # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
83
100
  # characters; and is terminated by a colon (":").
84
- # See http://tools.ietf.org/html/rfc3986#section-3.1
101
+ # See https://tools.ietf.org/html/rfc3986#section-3.1
85
102
  # The protocol relative scheme starts with a double slash "//".
86
103
  when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
87
104
  options
88
105
  when String
89
106
  request.protocol + request.host_with_port + options
90
- when :back
91
- request.headers["Referer"] or raise RedirectBackError
92
107
  when Proc
93
- _compute_redirect_to_location request, options.call
108
+ _compute_redirect_to_location request, instance_eval(&options)
94
109
  else
95
110
  url_for(options)
96
111
  end.delete("\0\r\n")
@@ -99,14 +114,20 @@ module ActionController
99
114
  public :_compute_redirect_to_location
100
115
 
101
116
  private
102
- def _extract_redirect_to_status(options, response_status)
117
+ def _extract_redirect_to_status(options, response_options)
103
118
  if options.is_a?(Hash) && options.key?(:status)
104
119
  Rack::Utils.status_code(options.delete(:status))
105
- elsif response_status.key?(:status)
106
- Rack::Utils.status_code(response_status[:status])
120
+ elsif response_options.key?(:status)
121
+ Rack::Utils.status_code(response_options[:status])
107
122
  else
108
123
  302
109
124
  end
110
125
  end
126
+
127
+ def _url_host_allowed?(url)
128
+ URI(url.to_s).host == request.host
129
+ rescue ArgumentError, URI::Error
130
+ false
131
+ end
111
132
  end
112
133
  end
@@ -1,4 +1,6 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
2
4
 
3
5
  module ActionController
4
6
  # See <tt>Renderers.add</tt>
@@ -11,6 +13,7 @@ module ActionController
11
13
  Renderers.remove(key)
12
14
  end
13
15
 
16
+ # See <tt>Responder#api_behavior</tt>
14
17
  class MissingRenderer < LoadError
15
18
  def initialize(format)
16
19
  super "No renderer defined for format: #{format}"
@@ -20,40 +23,24 @@ module ActionController
20
23
  module Renderers
21
24
  extend ActiveSupport::Concern
22
25
 
23
- included do
24
- class_attribute :_renderers
25
- self._renderers = Set.new.freeze
26
- end
26
+ # A Set containing renderer names that correspond to available renderer procs.
27
+ # Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
28
+ RENDERERS = Set.new
27
29
 
28
- module ClassMethods
29
- def use_renderers(*args)
30
- renderers = _renderers + args
31
- self._renderers = renderers.freeze
32
- end
33
- alias use_renderer use_renderers
30
+ included do
31
+ class_attribute :_renderers, default: Set.new.freeze
34
32
  end
35
33
 
36
- def render_to_body(options)
37
- _render_to_body_with_renderer(options) || super
38
- end
34
+ # Used in <tt>ActionController::Base</tt>
35
+ # and <tt>ActionController::API</tt> to include all
36
+ # renderers by default.
37
+ module All
38
+ extend ActiveSupport::Concern
39
+ include Renderers
39
40
 
40
- def _render_to_body_with_renderer(options)
41
- _renderers.each do |name|
42
- if options.key?(name)
43
- _process_options(options)
44
- method_name = Renderers._render_with_renderer_method_name(name)
45
- return send(method_name, options.delete(name), options)
46
- end
41
+ included do
42
+ self._renderers = RENDERERS
47
43
  end
48
- nil
49
- end
50
-
51
- # A Set containing renderer names that correspond to available renderer procs.
52
- # Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
53
- RENDERERS = Set.new
54
-
55
- def self._render_with_renderer_method_name(key)
56
- "_render_with_renderer_#{key}"
57
44
  end
58
45
 
59
46
  # Adds a new renderer to call within controller actions.
@@ -68,11 +55,11 @@ module ActionController
68
55
  # ActionController::Renderers.add :csv do |obj, options|
69
56
  # filename = options[:filename] || 'data'
70
57
  # str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
71
- # send_data str, type: Mime::CSV,
58
+ # send_data str, type: Mime[:csv],
72
59
  # disposition: "attachment; filename=#{filename}.csv"
73
60
  # end
74
61
  #
75
- # Note that we used Mime::CSV for the csv mime type as it comes with Rails.
62
+ # Note that we used Mime[:csv] for the csv mime type as it comes with Rails.
76
63
  # For a custom renderer, you'll need to register a mime type with
77
64
  # <tt>Mime::Type.register</tt>.
78
65
  #
@@ -92,46 +79,102 @@ module ActionController
92
79
 
93
80
  # This method is the opposite of add method.
94
81
  #
95
- # Usage:
82
+ # To remove a csv renderer:
96
83
  #
97
84
  # ActionController::Renderers.remove(:csv)
98
85
  def self.remove(key)
99
86
  RENDERERS.delete(key.to_sym)
100
87
  method_name = _render_with_renderer_method_name(key)
101
- remove_method(method_name) if method_defined?(method_name)
88
+ remove_possible_method(method_name)
102
89
  end
103
90
 
104
- module All
105
- extend ActiveSupport::Concern
106
- include Renderers
91
+ def self._render_with_renderer_method_name(key)
92
+ "_render_with_renderer_#{key}"
93
+ end
107
94
 
108
- included do
109
- self._renderers = RENDERERS
95
+ module ClassMethods
96
+ # Adds, by name, a renderer or renderers to the +_renderers+ available
97
+ # to call within controller actions.
98
+ #
99
+ # It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
100
+ # otherwise to add an available renderer proc to a specific controller.
101
+ #
102
+ # Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
103
+ # include <tt>ActionController::Renderers::All</tt>, making all renderers
104
+ # available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
105
+ #
106
+ # Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
107
+ # must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
108
+ # and <tt>ActionController::Renderers</tt>, and have at least one renderer.
109
+ #
110
+ # Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
111
+ # you may specify which renderers to include by passing the renderer name or names to
112
+ # +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
113
+ # (+_render_with_renderer_json+) might look like:
114
+ #
115
+ # class MetalRenderingController < ActionController::Metal
116
+ # include AbstractController::Rendering
117
+ # include ActionController::Rendering
118
+ # include ActionController::Renderers
119
+ #
120
+ # use_renderers :json
121
+ #
122
+ # def show
123
+ # render json: record
124
+ # end
125
+ # end
126
+ #
127
+ # You must specify a +use_renderer+, else the +controller.renderer+ and
128
+ # +controller._renderers+ will be <tt>nil</tt>, and the action will fail.
129
+ def use_renderers(*args)
130
+ renderers = _renderers + args
131
+ self._renderers = renderers.freeze
110
132
  end
133
+ alias use_renderer use_renderers
134
+ end
135
+
136
+ # Called by +render+ in <tt>AbstractController::Rendering</tt>
137
+ # which sets the return value as the +response_body+.
138
+ #
139
+ # If no renderer is found, +super+ returns control to
140
+ # <tt>ActionView::Rendering.render_to_body</tt>, if present.
141
+ def render_to_body(options)
142
+ _render_to_body_with_renderer(options) || super
143
+ end
144
+
145
+ def _render_to_body_with_renderer(options)
146
+ _renderers.each do |name|
147
+ if options.key?(name)
148
+ _process_options(options)
149
+ method_name = Renderers._render_with_renderer_method_name(name)
150
+ return send(method_name, options.delete(name), options)
151
+ end
152
+ end
153
+ nil
111
154
  end
112
155
 
113
156
  add :json do |json, options|
114
157
  json = json.to_json(options) unless json.kind_of?(String)
115
158
 
116
159
  if options[:callback].present?
117
- if content_type.nil? || content_type == Mime::JSON
118
- self.content_type = Mime::JS
160
+ if media_type.nil? || media_type == Mime[:json]
161
+ self.content_type = Mime[:js]
119
162
  end
120
163
 
121
164
  "/**/#{options[:callback]}(#{json})"
122
165
  else
123
- self.content_type ||= Mime::JSON
166
+ self.content_type = Mime[:json] if media_type.nil?
124
167
  json
125
168
  end
126
169
  end
127
170
 
128
171
  add :js do |js, options|
129
- self.content_type ||= Mime::JS
172
+ self.content_type = Mime[:js] if media_type.nil?
130
173
  js.respond_to?(:to_js) ? js.to_js(options) : js
131
174
  end
132
175
 
133
176
  add :xml do |xml, options|
134
- self.content_type ||= Mime::XML
177
+ self.content_type = Mime[:xml] if media_type.nil?
135
178
  xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
136
179
  end
137
180
  end