actionpack 4.0.13 → 4.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +131 -1636
  3. data/README.rdoc +1 -6
  4. data/lib/abstract_controller.rb +1 -2
  5. data/lib/abstract_controller/base.rb +3 -25
  6. data/lib/abstract_controller/callbacks.rb +4 -2
  7. data/lib/abstract_controller/collector.rb +11 -1
  8. data/lib/abstract_controller/helpers.rb +18 -15
  9. data/lib/abstract_controller/rendering.rb +48 -127
  10. data/lib/action_controller.rb +1 -17
  11. data/lib/action_controller/base.rb +14 -6
  12. data/lib/action_controller/caching.rb +1 -11
  13. data/lib/action_controller/log_subscriber.rb +1 -1
  14. data/lib/action_controller/metal.rb +0 -4
  15. data/lib/action_controller/metal/flash.rb +17 -0
  16. data/lib/action_controller/metal/force_ssl.rb +1 -1
  17. data/lib/action_controller/metal/head.rb +1 -3
  18. data/lib/action_controller/metal/helpers.rb +6 -2
  19. data/lib/action_controller/metal/http_authentication.rb +7 -14
  20. data/lib/action_controller/metal/instrumentation.rb +1 -1
  21. data/lib/action_controller/metal/live.rb +74 -0
  22. data/lib/action_controller/metal/mime_responds.rb +93 -16
  23. data/lib/action_controller/metal/params_wrapper.rb +4 -11
  24. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  25. data/lib/action_controller/metal/redirecting.rb +20 -20
  26. data/lib/action_controller/metal/renderers.rb +8 -5
  27. data/lib/action_controller/metal/rendering.rb +14 -11
  28. data/lib/action_controller/metal/request_forgery_protection.rb +67 -13
  29. data/lib/action_controller/metal/responder.rb +12 -2
  30. data/lib/action_controller/metal/streaming.rb +18 -20
  31. data/lib/action_controller/metal/strong_parameters.rb +22 -34
  32. data/lib/action_controller/railtie.rb +0 -1
  33. data/lib/action_controller/test_case.rb +0 -15
  34. data/lib/action_dispatch.rb +1 -0
  35. data/lib/action_dispatch/http/headers.rb +1 -3
  36. data/lib/action_dispatch/http/mime_negotiation.rb +16 -2
  37. data/lib/action_dispatch/http/mime_type.rb +4 -22
  38. data/lib/action_dispatch/http/mime_types.rb +1 -0
  39. data/lib/action_dispatch/http/parameters.rb +18 -19
  40. data/lib/action_dispatch/http/request.rb +16 -25
  41. data/lib/action_dispatch/http/response.rb +21 -8
  42. data/lib/action_dispatch/http/upload.rb +0 -13
  43. data/lib/action_dispatch/http/url.rb +10 -18
  44. data/lib/action_dispatch/journey/formatter.rb +3 -3
  45. data/lib/action_dispatch/journey/gtg/transition_table.rb +3 -5
  46. data/lib/action_dispatch/journey/parser.rb +1 -1
  47. data/lib/action_dispatch/journey/parser.y +1 -0
  48. data/lib/action_dispatch/journey/router.rb +7 -1
  49. data/lib/action_dispatch/journey/router/utils.rb +1 -1
  50. data/lib/action_dispatch/journey/visitors.rb +26 -47
  51. data/lib/action_dispatch/middleware/callbacks.rb +6 -6
  52. data/lib/action_dispatch/middleware/cookies.rb +15 -15
  53. data/lib/action_dispatch/middleware/debug_exceptions.rb +21 -13
  54. data/lib/action_dispatch/middleware/exception_wrapper.rb +1 -1
  55. data/lib/action_dispatch/middleware/flash.rb +5 -11
  56. data/lib/action_dispatch/middleware/params_parser.rb +1 -1
  57. data/lib/action_dispatch/middleware/public_exceptions.rb +1 -5
  58. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  59. data/lib/action_dispatch/middleware/session/cookie_store.rb +4 -3
  60. data/lib/action_dispatch/middleware/show_exceptions.rb +5 -2
  61. data/lib/action_dispatch/middleware/ssl.rb +1 -1
  62. data/lib/action_dispatch/middleware/static.rb +5 -25
  63. data/lib/action_dispatch/middleware/templates/rescues/{_request_and_response.erb → _request_and_response.html.erb} +0 -0
  64. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  65. data/lib/action_dispatch/middleware/templates/rescues/{_trace.erb → _trace.html.erb} +0 -0
  66. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +15 -0
  67. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +1 -1
  68. data/lib/action_dispatch/middleware/templates/rescues/{missing_template.erb → missing_template.html.erb} +1 -1
  69. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  70. data/lib/action_dispatch/middleware/templates/rescues/{routing_error.erb → routing_error.html.erb} +1 -1
  71. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  72. data/lib/action_dispatch/middleware/templates/rescues/{template_error.erb → template_error.html.erb} +1 -1
  73. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +8 -0
  74. data/lib/action_dispatch/middleware/templates/rescues/{unknown_action.erb → unknown_action.html.erb} +1 -1
  75. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  76. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -3
  77. data/lib/action_dispatch/railtie.rb +1 -2
  78. data/lib/action_dispatch/request/session.rb +12 -0
  79. data/lib/action_dispatch/request/utils.rb +24 -0
  80. data/lib/action_dispatch/routing.rb +7 -6
  81. data/lib/action_dispatch/routing/inspector.rb +4 -4
  82. data/lib/action_dispatch/routing/mapper.rb +81 -138
  83. data/lib/action_dispatch/routing/polymorphic_routes.rb +13 -0
  84. data/lib/action_dispatch/routing/redirection.rb +34 -27
  85. data/lib/action_dispatch/routing/route_set.rb +43 -37
  86. data/lib/action_dispatch/routing/url_for.rb +3 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -15
  88. data/lib/action_dispatch/testing/assertions/selector.rb +4 -4
  89. data/lib/action_dispatch/testing/integration.rb +1 -7
  90. data/lib/action_pack/version.rb +1 -1
  91. metadata +43 -167
  92. data/lib/abstract_controller/layouts.rb +0 -423
  93. data/lib/abstract_controller/view_paths.rb +0 -96
  94. data/lib/action_controller/deprecated.rb +0 -7
  95. data/lib/action_controller/deprecated/integration_test.rb +0 -5
  96. data/lib/action_controller/record_identifier.rb +0 -31
  97. data/lib/action_controller/vendor/html-scanner.rb +0 -5
  98. data/lib/action_view.rb +0 -93
  99. data/lib/action_view/base.rb +0 -205
  100. data/lib/action_view/buffers.rb +0 -49
  101. data/lib/action_view/context.rb +0 -36
  102. data/lib/action_view/dependency_tracker.rb +0 -93
  103. data/lib/action_view/digestor.rb +0 -113
  104. data/lib/action_view/flows.rb +0 -76
  105. data/lib/action_view/helpers.rb +0 -58
  106. data/lib/action_view/helpers/active_model_helper.rb +0 -49
  107. data/lib/action_view/helpers/asset_tag_helper.rb +0 -320
  108. data/lib/action_view/helpers/asset_url_helper.rb +0 -355
  109. data/lib/action_view/helpers/atom_feed_helper.rb +0 -203
  110. data/lib/action_view/helpers/cache_helper.rb +0 -196
  111. data/lib/action_view/helpers/capture_helper.rb +0 -216
  112. data/lib/action_view/helpers/controller_helper.rb +0 -25
  113. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  114. data/lib/action_view/helpers/date_helper.rb +0 -1087
  115. data/lib/action_view/helpers/debug_helper.rb +0 -39
  116. data/lib/action_view/helpers/form_helper.rb +0 -1882
  117. data/lib/action_view/helpers/form_options_helper.rb +0 -838
  118. data/lib/action_view/helpers/form_tag_helper.rb +0 -785
  119. data/lib/action_view/helpers/javascript_helper.rb +0 -117
  120. data/lib/action_view/helpers/number_helper.rb +0 -451
  121. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  122. data/lib/action_view/helpers/record_tag_helper.rb +0 -106
  123. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  124. data/lib/action_view/helpers/sanitize_helper.rb +0 -256
  125. data/lib/action_view/helpers/tag_helper.rb +0 -173
  126. data/lib/action_view/helpers/tags.rb +0 -39
  127. data/lib/action_view/helpers/tags/base.rb +0 -148
  128. data/lib/action_view/helpers/tags/check_box.rb +0 -64
  129. data/lib/action_view/helpers/tags/checkable.rb +0 -16
  130. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -53
  131. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -84
  132. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -36
  133. data/lib/action_view/helpers/tags/collection_select.rb +0 -28
  134. data/lib/action_view/helpers/tags/color_field.rb +0 -25
  135. data/lib/action_view/helpers/tags/date_field.rb +0 -13
  136. data/lib/action_view/helpers/tags/date_select.rb +0 -72
  137. data/lib/action_view/helpers/tags/datetime_field.rb +0 -22
  138. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -19
  139. data/lib/action_view/helpers/tags/datetime_select.rb +0 -8
  140. data/lib/action_view/helpers/tags/email_field.rb +0 -8
  141. data/lib/action_view/helpers/tags/file_field.rb +0 -8
  142. data/lib/action_view/helpers/tags/grouped_collection_select.rb +0 -29
  143. data/lib/action_view/helpers/tags/hidden_field.rb +0 -8
  144. data/lib/action_view/helpers/tags/label.rb +0 -65
  145. data/lib/action_view/helpers/tags/month_field.rb +0 -13
  146. data/lib/action_view/helpers/tags/number_field.rb +0 -18
  147. data/lib/action_view/helpers/tags/password_field.rb +0 -12
  148. data/lib/action_view/helpers/tags/radio_button.rb +0 -31
  149. data/lib/action_view/helpers/tags/range_field.rb +0 -8
  150. data/lib/action_view/helpers/tags/search_field.rb +0 -22
  151. data/lib/action_view/helpers/tags/select.rb +0 -40
  152. data/lib/action_view/helpers/tags/tel_field.rb +0 -8
  153. data/lib/action_view/helpers/tags/text_area.rb +0 -18
  154. data/lib/action_view/helpers/tags/text_field.rb +0 -30
  155. data/lib/action_view/helpers/tags/time_field.rb +0 -13
  156. data/lib/action_view/helpers/tags/time_select.rb +0 -8
  157. data/lib/action_view/helpers/tags/time_zone_select.rb +0 -20
  158. data/lib/action_view/helpers/tags/url_field.rb +0 -8
  159. data/lib/action_view/helpers/tags/week_field.rb +0 -13
  160. data/lib/action_view/helpers/text_helper.rb +0 -448
  161. data/lib/action_view/helpers/translation_helper.rb +0 -112
  162. data/lib/action_view/helpers/url_helper.rb +0 -635
  163. data/lib/action_view/locale/en.yml +0 -56
  164. data/lib/action_view/log_subscriber.rb +0 -30
  165. data/lib/action_view/lookup_context.rb +0 -248
  166. data/lib/action_view/model_naming.rb +0 -12
  167. data/lib/action_view/path_set.rb +0 -77
  168. data/lib/action_view/railtie.rb +0 -43
  169. data/lib/action_view/record_identifier.rb +0 -84
  170. data/lib/action_view/renderer/abstract_renderer.rb +0 -47
  171. data/lib/action_view/renderer/partial_renderer.rb +0 -500
  172. data/lib/action_view/renderer/renderer.rb +0 -50
  173. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -103
  174. data/lib/action_view/renderer/template_renderer.rb +0 -96
  175. data/lib/action_view/routing_url_for.rb +0 -107
  176. data/lib/action_view/tasks/dependencies.rake +0 -17
  177. data/lib/action_view/template.rb +0 -339
  178. data/lib/action_view/template/error.rb +0 -138
  179. data/lib/action_view/template/handlers.rb +0 -53
  180. data/lib/action_view/template/handlers/builder.rb +0 -26
  181. data/lib/action_view/template/handlers/erb.rb +0 -146
  182. data/lib/action_view/template/handlers/raw.rb +0 -11
  183. data/lib/action_view/template/resolver.rb +0 -340
  184. data/lib/action_view/template/text.rb +0 -34
  185. data/lib/action_view/template/types.rb +0 -57
  186. data/lib/action_view/test_case.rb +0 -270
  187. data/lib/action_view/testing/resolvers.rb +0 -50
  188. data/lib/action_view/vendor/html-scanner.rb +0 -20
  189. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  190. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  191. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  192. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  193. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  194. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
@@ -231,12 +231,7 @@ module ActionController
231
231
  # by the metal call stack.
232
232
  def process_action(*args)
233
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
-
234
+ wrapped_hash = _wrap_parameters request.request_parameters
240
235
  wrapped_keys = request.request_parameters.keys
241
236
  wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
242
237
 
@@ -264,16 +259,14 @@ module ActionController
264
259
 
265
260
  # Returns the list of parameters which will be selected for wrapped.
266
261
  def _wrap_parameters(parameters)
267
- { _wrapper_key => _extract_parameters(parameters) }
268
- end
269
-
270
- def _extract_parameters(parameters)
271
- if include_only = _wrapper_options.include
262
+ value = if include_only = _wrapper_options.include
272
263
  parameters.slice(*include_only)
273
264
  else
274
265
  exclude = _wrapper_options.exclude || []
275
266
  parameters.except(*(exclude + EXCLUDE_PARAMETERS))
276
267
  end
268
+
269
+ { _wrapper_key => value }
277
270
  end
278
271
 
279
272
  # Checks if we should perform parameters wrapping.
@@ -6,7 +6,7 @@ module ActionController
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  delegate :headers, :status=, :location=, :content_type=,
9
- :status, :location, :content_type, :_status_code, :to => "@_response"
9
+ :status, :location, :content_type, :to => "@_response"
10
10
 
11
11
  def dispatch(action, request)
12
12
  set_response!(request)
@@ -64,7 +64,6 @@ module ActionController
64
64
  # behavior for this case by rescuing ActionController::RedirectBackError.
65
65
  def redirect_to(options = {}, response_status = {}) #:doc:
66
66
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
67
- raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
68
67
  raise AbstractController::DoubleRenderError if response_body
69
68
 
70
69
  self.status = _extract_redirect_to_status(options, response_status)
@@ -72,6 +71,26 @@ module ActionController
72
71
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
73
72
  end
74
73
 
74
+ def _compute_redirect_to_location(options) #:nodoc:
75
+ case options
76
+ # The scheme name consist of a letter followed by any combination of
77
+ # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
78
+ # characters; and is terminated by a colon (":").
79
+ # See http://tools.ietf.org/html/rfc3986#section-3.1
80
+ # The protocol relative scheme starts with a double slash "//".
81
+ when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
82
+ options
83
+ when String
84
+ request.protocol + request.host_with_port + options
85
+ when :back
86
+ request.headers["Referer"] or raise RedirectBackError
87
+ when Proc
88
+ _compute_redirect_to_location options.call
89
+ else
90
+ url_for(options)
91
+ end.delete("\0\r\n")
92
+ end
93
+
75
94
  private
76
95
  def _extract_redirect_to_status(options, response_status)
77
96
  if options.is_a?(Hash) && options.key?(:status)
@@ -82,24 +101,5 @@ module ActionController
82
101
  302
83
102
  end
84
103
  end
85
-
86
- def _compute_redirect_to_location(options)
87
- case options
88
- # The scheme name consist of a letter followed by any combination of
89
- # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
90
- # characters; and is terminated by a colon (":").
91
- # The protocol relative scheme starts with a double slash "//"
92
- when %r{\A(\w[\w+.-]*:|//).*}
93
- options
94
- when String
95
- request.protocol + request.host_with_port + options
96
- when :back
97
- request.headers["Referer"] or raise RedirectBackError
98
- when Proc
99
- _compute_redirect_to_location options.call
100
- else
101
- url_for(options)
102
- end.delete("\0\r\n")
103
- end
104
104
  end
105
105
  end
@@ -6,6 +6,12 @@ module ActionController
6
6
  Renderers.add(key, &block)
7
7
  end
8
8
 
9
+ class MissingRenderer < LoadError
10
+ def initialize(format)
11
+ super "No renderer defined for format: #{format}"
12
+ end
13
+ end
14
+
9
15
  module Renderers
10
16
  extend ActiveSupport::Concern
11
17
 
@@ -90,11 +96,8 @@ module ActionController
90
96
  json = json.to_json(options) unless json.kind_of?(String)
91
97
 
92
98
  if options[:callback].present?
93
- if self.content_type.nil? || self.content_type == Mime::JSON
94
- self.content_type = Mime::JS
95
- end
96
-
97
- "/**/#{options[:callback]}(#{json})"
99
+ self.content_type ||= Mime::JS
100
+ "#{options[:callback]}(#{json})"
98
101
  else
99
102
  self.content_type ||= Mime::JSON
100
103
  json
@@ -2,8 +2,6 @@ module ActionController
2
2
  module Rendering
3
3
  extend ActiveSupport::Concern
4
4
 
5
- include AbstractController::Rendering
6
-
7
5
  # Before processing, set the request formats in current controller formats.
8
6
  def process_action(*) #:nodoc:
9
7
  self.formats = request.formats.map(&:ref).compact
@@ -12,29 +10,34 @@ module ActionController
12
10
 
13
11
  # Check for double render errors and set the content_type after rendering.
14
12
  def render(*args) #:nodoc:
15
- raise ::AbstractController::DoubleRenderError if response_body
13
+ raise ::AbstractController::DoubleRenderError if self.response_body
16
14
  super
17
- self.content_type ||= Mime[lookup_context.rendered_format].to_s
18
- response_body
19
15
  end
20
16
 
21
17
  # Overwrite render_to_string because body can now be set to a rack body.
22
18
  def render_to_string(*)
23
- if self.response_body = super
19
+ result = super
20
+ if result.respond_to?(:each)
24
21
  string = ""
25
- response_body.each { |r| string << r }
22
+ result.each { |r| string << r }
26
23
  string
24
+ else
25
+ result
27
26
  end
28
- ensure
29
- self.response_body = nil
30
27
  end
31
28
 
32
- def render_to_body(*)
33
- super || " "
29
+ def render_to_body(options = {})
30
+ super || options[:text].presence || ' '
34
31
  end
35
32
 
36
33
  private
37
34
 
35
+ def _process_format(format)
36
+ super
37
+ # format is a Mime::NullType instance here then this condition can't be changed to `if format`
38
+ self.content_type ||= format.to_s unless format.nil?
39
+ end
40
+
38
41
  # Normalize arguments by catching blocks and setting them on :update.
39
42
  def _normalize_args(action=nil, options={}, &blk) #:nodoc:
40
43
  options = super
@@ -5,14 +5,24 @@ module ActionController #:nodoc:
5
5
  class InvalidAuthenticityToken < ActionControllerError #:nodoc:
6
6
  end
7
7
 
8
+ class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
9
+ end
10
+
8
11
  # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
9
12
  # by including a token in the rendered html for your application. This token is
10
13
  # stored as a random string in the session, to which an attacker does not have
11
14
  # access. When a request reaches your application, \Rails verifies the received
12
15
  # token with the token in the session. Only HTML and JavaScript requests are checked,
13
16
  # so this will not protect your XML API (presumably you'll have a different
14
- # authentication scheme there anyway). Also, GET requests are not protected as these
15
- # should be idempotent.
17
+ # authentication scheme there anyway).
18
+ #
19
+ # GET requests are not protected since they don't have side effects like writing
20
+ # to the database and don't leak sensitive information. JavaScript requests are
21
+ # an exception: a third-party site can use a <script> tag to reference a JavaScript
22
+ # URL on your site. When your JavaScript response loads on their site, it executes.
23
+ # With carefully crafted JavaScript on their end, sensitive data in your JavaScript
24
+ # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
25
+ # Ajax) requests are allowed to make GET requests for JavaScript responses.
16
26
  #
17
27
  # It's important to remember that XML or JSON requests are also affected and if
18
28
  # you're building an API you'll need something like:
@@ -65,17 +75,16 @@ module ActionController #:nodoc:
65
75
  module ClassMethods
66
76
  # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
67
77
  #
78
+ # class ApplicationController < ActionController::Base
79
+ # protect_from_forgery
80
+ # end
81
+ #
68
82
  # class FooController < ApplicationController
69
83
  # protect_from_forgery except: :index
70
84
  #
71
- # You can disable csrf protection on controller-by-controller basis:
72
- #
85
+ # You can disable CSRF protection on controller by skipping the verification before_action:
73
86
  # skip_before_action :verify_authenticity_token
74
87
  #
75
- # It can also be disabled for specific controller actions:
76
- #
77
- # skip_before_action :verify_authenticity_token, except: [:create]
78
- #
79
88
  # Valid Options:
80
89
  #
81
90
  # * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified.
@@ -89,6 +98,7 @@ module ActionController #:nodoc:
89
98
  self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
90
99
  self.request_forgery_protection_token ||= :authenticity_token
91
100
  prepend_before_action :verify_authenticity_token, options
101
+ append_after_action :verify_same_origin_request
92
102
  end
93
103
 
94
104
  private
@@ -169,18 +179,61 @@ module ActionController #:nodoc:
169
179
  end
170
180
 
171
181
  protected
182
+ # The actual before_action that is used to verify the CSRF token.
183
+ # Don't override this directly. Provide your own forgery protection
184
+ # strategy instead. If you override, you'll disable same-origin
185
+ # `<script>` verification.
186
+ #
187
+ # Lean on the protect_from_forgery declaration to mark which actions are
188
+ # due for same-origin request verification. If protect_from_forgery is
189
+ # enabled on an action, this before_action flags its after_action to
190
+ # verify that JavaScript responses are for XHR requests, ensuring they
191
+ # follow the browser's same-origin policy.
192
+ def verify_authenticity_token
193
+ mark_for_same_origin_verification!
194
+
195
+ if !verified_request?
196
+ logger.warn "Can't verify CSRF token authenticity" if logger
197
+ handle_unverified_request
198
+ end
199
+ end
200
+
172
201
  def handle_unverified_request
173
202
  forgery_protection_strategy.new(self).handle_unverified_request
174
203
  end
175
204
 
176
- # The actual before_action that is used. Modify this to change how you handle unverified requests.
177
- def verify_authenticity_token
178
- unless verified_request?
179
- logger.warn "Can't verify CSRF token authenticity" if logger
180
- handle_unverified_request
205
+ CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
206
+ "<script> tag on another site requested protected JavaScript. " \
207
+ "If you know what you're doing, go ahead and disable forgery " \
208
+ "protection on this action to permit cross-origin JavaScript embedding."
209
+ private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
210
+
211
+ # If `verify_authenticity_token` was run (indicating that we have
212
+ # forgery protection enabled for this request) then also verify that
213
+ # we aren't serving an unauthorized cross-origin response.
214
+ def verify_same_origin_request
215
+ if marked_for_same_origin_verification? && non_xhr_javascript_response?
216
+ logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING if logger
217
+ raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING
181
218
  end
182
219
  end
183
220
 
221
+ # GET requests are checked for cross-origin JavaScript after rendering.
222
+ def mark_for_same_origin_verification!
223
+ @marked_for_same_origin_verification = request.get?
224
+ end
225
+
226
+ # If the `verify_authenticity_token` before_action ran, verify that
227
+ # JavaScript responses are only served to same-origin GET requests.
228
+ def marked_for_same_origin_verification?
229
+ @marked_for_same_origin_verification ||= false
230
+ end
231
+
232
+ # Check for cross-origin JavaScript responses.
233
+ def non_xhr_javascript_response?
234
+ content_type =~ %r(\Atext/javascript) && !request.xhr?
235
+ end
236
+
184
237
  # Returns true or false if a request is verified. Checks:
185
238
  #
186
239
  # * is it a GET or HEAD request? Gets should be safe and idempotent
@@ -202,6 +255,7 @@ module ActionController #:nodoc:
202
255
  params[request_forgery_protection_token]
203
256
  end
204
257
 
258
+ # Checks if the controller allows forgery protection.
205
259
  def protect_against_forgery?
206
260
  allow_forgery_protection
207
261
  end
@@ -97,8 +97,12 @@ module ActionController #:nodoc:
97
97
  #
98
98
  # This will return status 201 if the task was saved successfully. If not,
99
99
  # it will simply ignore the given options and return status 422 and the
100
- # resource errors. To customize the failure scenario, you can pass a
101
- # a block to <code>respond_with</code>:
100
+ # resource errors. You can also override the location to redirect to:
101
+ #
102
+ # respond_with(@project, location: root_path)
103
+ #
104
+ # To customize the failure scenario, you can pass a block to
105
+ # <code>respond_with</code>:
102
106
  #
103
107
  # def create
104
108
  # @project = Project.find(params[:project_id])
@@ -198,6 +202,7 @@ module ActionController #:nodoc:
198
202
  # This is the common behavior for formats associated with APIs, such as :xml and :json.
199
203
  def api_behavior(error)
200
204
  raise error unless resourceful?
205
+ raise MissingRenderer.new(format) unless has_renderer?
201
206
 
202
207
  if get?
203
208
  display resource
@@ -265,6 +270,11 @@ module ActionController #:nodoc:
265
270
  resource.respond_to?(:errors) && !resource.errors.empty?
266
271
  end
267
272
 
273
+ # Check whether the necessary Renderer is available
274
+ def has_renderer?
275
+ Renderers::RENDERERS.include?(format)
276
+ end
277
+
268
278
  # By default, render the <code>:edit</code> action for HTML requests with errors, unless
269
279
  # the verb was POST.
270
280
  #
@@ -193,31 +193,29 @@ module ActionController #:nodoc:
193
193
  module Streaming
194
194
  extend ActiveSupport::Concern
195
195
 
196
- include AbstractController::Rendering
197
-
198
196
  protected
199
197
 
200
- # Set proper cache control and transfer encoding when streaming
201
- def _process_options(options) #:nodoc:
202
- super
203
- if options[:stream]
204
- if env["HTTP_VERSION"] == "HTTP/1.0"
205
- options.delete(:stream)
206
- else
207
- headers["Cache-Control"] ||= "no-cache"
208
- headers["Transfer-Encoding"] = "chunked"
209
- headers.delete("Content-Length")
198
+ # Set proper cache control and transfer encoding when streaming
199
+ def _process_options(options) #:nodoc:
200
+ super
201
+ if options[:stream]
202
+ if env["HTTP_VERSION"] == "HTTP/1.0"
203
+ options.delete(:stream)
204
+ else
205
+ headers["Cache-Control"] ||= "no-cache"
206
+ headers["Transfer-Encoding"] = "chunked"
207
+ headers.delete("Content-Length")
208
+ end
210
209
  end
211
210
  end
212
- end
213
211
 
214
- # Call render_body if we are streaming instead of usual +render+.
215
- def _render_template(options) #:nodoc:
216
- if options.delete(:stream)
217
- Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
218
- else
219
- super
212
+ # Call render_body if we are streaming instead of usual +render+.
213
+ def _render_template(options) #:nodoc:
214
+ if options.delete(:stream)
215
+ Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
216
+ else
217
+ super
218
+ end
220
219
  end
221
- end
222
220
  end
223
221
  end
@@ -3,7 +3,6 @@ require 'active_support/core_ext/array/wrap'
3
3
  require 'active_support/rescuable'
4
4
  require 'action_dispatch/http/upload'
5
5
  require 'stringio'
6
- require 'set'
7
6
 
8
7
  module ActionController
9
8
  # Raised when a required parameter is missing.
@@ -126,13 +125,6 @@ module ActionController
126
125
  @permitted = self.class.permit_all_parameters
127
126
  end
128
127
 
129
- # Attribute that keeps track of converted arrays, if any, to avoid double
130
- # looping in the common use case permit + mass-assignment. Defined in a
131
- # method to instantiate it only if needed.
132
- def converted_arrays
133
- @converted_arrays ||= Set.new
134
- end
135
-
136
128
  # Returns +true+ if the parameter is permitted, +false+ otherwise.
137
129
  #
138
130
  # params = ActionController::Parameters.new
@@ -157,10 +149,8 @@ module ActionController
157
149
  # Person.new(params) # => #<Person id: nil, name: "Francesco">
158
150
  def permit!
159
151
  each_pair do |key, value|
160
- value = convert_hashes_to_parameters(key, value)
161
- Array.wrap(value).each do |_|
162
- _.permit! if _.respond_to? :permit!
163
- end
152
+ convert_hashes_to_parameters(key, value)
153
+ self[key].permit! if self[key].respond_to? :permit!
164
154
  end
165
155
 
166
156
  @permitted = true
@@ -180,12 +170,7 @@ module ActionController
180
170
  # ActionController::Parameters.new(person: {}).require(:person)
181
171
  # # => ActionController::ParameterMissing: param not found: person
182
172
  def require(key)
183
- value = self[key]
184
- if value.present? || value == false
185
- value
186
- else
187
- raise ParameterMissing.new(key)
188
- end
173
+ self[key].presence || raise(ParameterMissing.new(key))
189
174
  end
190
175
 
191
176
  # Alias of #require.
@@ -299,7 +284,14 @@ module ActionController
299
284
  # params.fetch(:none, 'Francesco') # => "Francesco"
300
285
  # params.fetch(:none) { 'Francesco' } # => "Francesco"
301
286
  def fetch(key, *args)
302
- convert_hashes_to_parameters(key, super, false)
287
+ value = super
288
+ # Don't rely on +convert_hashes_to_parameters+
289
+ # so as to not mutate via a +fetch+
290
+ if value.is_a?(Hash)
291
+ value = self.class.new(value)
292
+ value.permit! if permitted?
293
+ end
294
+ value
303
295
  rescue KeyError
304
296
  raise ActionController::ParameterMissing.new(key)
305
297
  end
@@ -313,7 +305,7 @@ module ActionController
313
305
  # params.slice(:d) # => {}
314
306
  def slice(*keys)
315
307
  self.class.new(super).tap do |new_instance|
316
- new_instance.instance_variable_set :@permitted, @permitted
308
+ new_instance.permitted = @permitted
317
309
  end
318
310
  end
319
311
 
@@ -327,26 +319,22 @@ module ActionController
327
319
  # copy_params.permitted? # => true
328
320
  def dup
329
321
  super.tap do |duplicate|
330
- duplicate.instance_variable_set :@permitted, @permitted
322
+ duplicate.permitted = @permitted
331
323
  end
332
324
  end
333
325
 
334
- private
335
- def convert_hashes_to_parameters(key, value, assign_if_converted=true)
336
- converted = convert_value_to_parameters(value)
337
- self[key] = converted if assign_if_converted && !converted.equal?(value)
338
- converted
326
+ protected
327
+ def permitted=(new_permitted)
328
+ @permitted = new_permitted
339
329
  end
340
330
 
341
- def convert_value_to_parameters(value)
342
- if value.is_a?(Array) && !converted_arrays.member?(value)
343
- converted = value.map { |_| convert_value_to_parameters(_) }
344
- converted_arrays << converted
345
- converted
346
- elsif value.is_a?(Parameters) || !value.is_a?(Hash)
331
+ private
332
+ def convert_hashes_to_parameters(key, value)
333
+ if value.is_a?(Parameters) || !value.is_a?(Hash)
347
334
  value
348
335
  else
349
- self.class.new(value)
336
+ # Convert to Parameters on first access
337
+ self[key] = self.class.new(value)
350
338
  end
351
339
  end
352
340
 
@@ -502,7 +490,7 @@ module ActionController
502
490
  # end
503
491
  # end
504
492
  #
505
- # In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you
493
+ # In order to use <tt>accepts_nested_attribute_for</tt> with Strong \Parameters, you
506
494
  # will need to specify which nested attributes should be whitelisted.
507
495
  #
508
496
  # class Person