actionpack 4.0.1 → 4.2.11.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 (241) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +402 -1173
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/abstract_controller/base.rb +39 -7
  6. data/lib/abstract_controller/callbacks.rb +32 -53
  7. data/lib/abstract_controller/collector.rb +11 -1
  8. data/lib/abstract_controller/helpers.rb +26 -16
  9. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  10. data/lib/abstract_controller/rendering.rb +57 -127
  11. data/lib/abstract_controller/url_for.rb +1 -1
  12. data/lib/abstract_controller.rb +1 -2
  13. data/lib/action_controller/base.rb +19 -10
  14. data/lib/action_controller/caching/fragments.rb +7 -1
  15. data/lib/action_controller/caching.rb +2 -12
  16. data/lib/action_controller/log_subscriber.rb +29 -20
  17. data/lib/action_controller/metal/conditional_get.rb +37 -12
  18. data/lib/action_controller/metal/data_streaming.rb +1 -1
  19. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  20. data/lib/action_controller/metal/exceptions.rb +1 -1
  21. data/lib/action_controller/metal/flash.rb +17 -0
  22. data/lib/action_controller/metal/force_ssl.rb +2 -2
  23. data/lib/action_controller/metal/head.rb +8 -6
  24. data/lib/action_controller/metal/helpers.rb +6 -2
  25. data/lib/action_controller/metal/http_authentication.rb +45 -23
  26. data/lib/action_controller/metal/instrumentation.rb +9 -6
  27. data/lib/action_controller/metal/live.rb +173 -20
  28. data/lib/action_controller/metal/mime_responds.rb +127 -232
  29. data/lib/action_controller/metal/params_wrapper.rb +16 -9
  30. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  31. data/lib/action_controller/metal/redirecting.rb +34 -26
  32. data/lib/action_controller/metal/renderers.rb +39 -12
  33. data/lib/action_controller/metal/rendering.rb +41 -14
  34. data/lib/action_controller/metal/request_forgery_protection.rb +147 -19
  35. data/lib/action_controller/metal/streaming.rb +19 -21
  36. data/lib/action_controller/metal/strong_parameters.rb +166 -22
  37. data/lib/action_controller/metal/testing.rb +0 -1
  38. data/lib/action_controller/metal/url_for.rb +11 -12
  39. data/lib/action_controller/metal.rb +14 -8
  40. data/lib/action_controller/model_naming.rb +1 -1
  41. data/lib/action_controller/railtie.rb +5 -1
  42. data/lib/action_controller/test_case.rb +160 -94
  43. data/lib/action_controller.rb +2 -18
  44. data/lib/action_dispatch/http/cache.rb +5 -4
  45. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  46. data/lib/action_dispatch/http/filter_redirect.rb +5 -4
  47. data/lib/action_dispatch/http/headers.rb +46 -10
  48. data/lib/action_dispatch/http/mime_negotiation.rb +31 -4
  49. data/lib/action_dispatch/http/mime_type.rb +25 -26
  50. data/lib/action_dispatch/http/mime_types.rb +1 -0
  51. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  52. data/lib/action_dispatch/http/parameters.rb +25 -41
  53. data/lib/action_dispatch/http/request.rb +49 -32
  54. data/lib/action_dispatch/http/response.rb +127 -25
  55. data/lib/action_dispatch/http/upload.rb +9 -21
  56. data/lib/action_dispatch/http/url.rb +97 -70
  57. data/lib/action_dispatch/journey/formatter.rb +35 -19
  58. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  59. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  60. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -33
  61. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  62. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  64. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  65. data/lib/action_dispatch/journey/parser.rb +51 -59
  66. data/lib/action_dispatch/journey/parser.y +12 -10
  67. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  68. data/lib/action_dispatch/journey/route.rb +8 -19
  69. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +54 -18
  71. data/lib/action_dispatch/journey/router.rb +53 -75
  72. data/lib/action_dispatch/journey/routes.rb +4 -0
  73. data/lib/action_dispatch/journey/scanner.rb +5 -5
  74. data/lib/action_dispatch/journey/visitors.rb +81 -60
  75. data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
  76. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  77. data/lib/action_dispatch/middleware/callbacks.rb +7 -7
  78. data/lib/action_dispatch/middleware/cookies.rb +119 -43
  79. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -13
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +60 -20
  81. data/lib/action_dispatch/middleware/flash.rb +37 -24
  82. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
  84. data/lib/action_dispatch/middleware/reloader.rb +11 -2
  85. data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
  86. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +8 -7
  89. data/lib/action_dispatch/middleware/show_exceptions.rb +6 -2
  90. data/lib/action_dispatch/middleware/ssl.rb +10 -7
  91. data/lib/action_dispatch/middleware/static.rb +79 -23
  92. data/lib/action_dispatch/middleware/templates/rescues/{_request_and_response.erb → _request_and_response.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
  95. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  96. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  97. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +1 -1
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/{routing_error.erb → routing_error.html.erb} +3 -1
  103. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/{unknown_action.erb → unknown_action.html.erb} +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  108. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
  109. data/lib/action_dispatch/railtie.rb +5 -2
  110. data/lib/action_dispatch/request/session.rb +12 -0
  111. data/lib/action_dispatch/request/utils.rb +35 -0
  112. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  113. data/lib/action_dispatch/routing/inspector.rb +11 -17
  114. data/lib/action_dispatch/routing/mapper.rb +519 -312
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +204 -79
  116. data/lib/action_dispatch/routing/redirection.rb +51 -26
  117. data/lib/action_dispatch/routing/route_set.rb +331 -206
  118. data/lib/action_dispatch/routing/routes_proxy.rb +5 -4
  119. data/lib/action_dispatch/routing/url_for.rb +19 -5
  120. data/lib/action_dispatch/routing.rb +9 -6
  121. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  122. data/lib/action_dispatch/testing/assertions/response.rb +9 -15
  123. data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
  124. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  125. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  126. data/lib/action_dispatch/testing/assertions.rb +11 -7
  127. data/lib/action_dispatch/testing/integration.rb +31 -29
  128. data/lib/action_dispatch/testing/test_request.rb +1 -1
  129. data/lib/action_dispatch/testing/test_response.rb +1 -5
  130. data/lib/action_dispatch.rb +5 -8
  131. data/lib/action_pack/gem_version.rb +15 -0
  132. data/lib/action_pack/version.rb +4 -7
  133. data/lib/action_pack.rb +1 -1
  134. metadata +77 -159
  135. data/lib/abstract_controller/layouts.rb +0 -423
  136. data/lib/abstract_controller/view_paths.rb +0 -96
  137. data/lib/action_controller/deprecated/integration_test.rb +0 -5
  138. data/lib/action_controller/deprecated.rb +0 -7
  139. data/lib/action_controller/metal/responder.rb +0 -287
  140. data/lib/action_controller/record_identifier.rb +0 -31
  141. data/lib/action_controller/vendor/html-scanner.rb +0 -5
  142. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -24
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -7
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -43
  145. data/lib/action_view/base.rb +0 -201
  146. data/lib/action_view/buffers.rb +0 -49
  147. data/lib/action_view/context.rb +0 -36
  148. data/lib/action_view/dependency_tracker.rb +0 -93
  149. data/lib/action_view/digestor.rb +0 -113
  150. data/lib/action_view/flows.rb +0 -76
  151. data/lib/action_view/helpers/active_model_helper.rb +0 -49
  152. data/lib/action_view/helpers/asset_tag_helper.rb +0 -320
  153. data/lib/action_view/helpers/asset_url_helper.rb +0 -355
  154. data/lib/action_view/helpers/atom_feed_helper.rb +0 -203
  155. data/lib/action_view/helpers/cache_helper.rb +0 -196
  156. data/lib/action_view/helpers/capture_helper.rb +0 -216
  157. data/lib/action_view/helpers/controller_helper.rb +0 -25
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -30
  159. data/lib/action_view/helpers/date_helper.rb +0 -1083
  160. data/lib/action_view/helpers/debug_helper.rb +0 -39
  161. data/lib/action_view/helpers/form_helper.rb +0 -1880
  162. data/lib/action_view/helpers/form_options_helper.rb +0 -838
  163. data/lib/action_view/helpers/form_tag_helper.rb +0 -785
  164. data/lib/action_view/helpers/javascript_helper.rb +0 -117
  165. data/lib/action_view/helpers/number_helper.rb +0 -441
  166. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  167. data/lib/action_view/helpers/record_tag_helper.rb +0 -106
  168. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  169. data/lib/action_view/helpers/sanitize_helper.rb +0 -256
  170. data/lib/action_view/helpers/tag_helper.rb +0 -173
  171. data/lib/action_view/helpers/tags/base.rb +0 -148
  172. data/lib/action_view/helpers/tags/check_box.rb +0 -64
  173. data/lib/action_view/helpers/tags/checkable.rb +0 -16
  174. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -44
  175. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -84
  176. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -36
  177. data/lib/action_view/helpers/tags/collection_select.rb +0 -28
  178. data/lib/action_view/helpers/tags/color_field.rb +0 -25
  179. data/lib/action_view/helpers/tags/date_field.rb +0 -13
  180. data/lib/action_view/helpers/tags/date_select.rb +0 -72
  181. data/lib/action_view/helpers/tags/datetime_field.rb +0 -22
  182. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -19
  183. data/lib/action_view/helpers/tags/datetime_select.rb +0 -8
  184. data/lib/action_view/helpers/tags/email_field.rb +0 -8
  185. data/lib/action_view/helpers/tags/file_field.rb +0 -8
  186. data/lib/action_view/helpers/tags/grouped_collection_select.rb +0 -29
  187. data/lib/action_view/helpers/tags/hidden_field.rb +0 -8
  188. data/lib/action_view/helpers/tags/label.rb +0 -66
  189. data/lib/action_view/helpers/tags/month_field.rb +0 -13
  190. data/lib/action_view/helpers/tags/number_field.rb +0 -18
  191. data/lib/action_view/helpers/tags/password_field.rb +0 -12
  192. data/lib/action_view/helpers/tags/radio_button.rb +0 -31
  193. data/lib/action_view/helpers/tags/range_field.rb +0 -8
  194. data/lib/action_view/helpers/tags/search_field.rb +0 -24
  195. data/lib/action_view/helpers/tags/select.rb +0 -40
  196. data/lib/action_view/helpers/tags/tel_field.rb +0 -8
  197. data/lib/action_view/helpers/tags/text_area.rb +0 -18
  198. data/lib/action_view/helpers/tags/text_field.rb +0 -29
  199. data/lib/action_view/helpers/tags/time_field.rb +0 -13
  200. data/lib/action_view/helpers/tags/time_select.rb +0 -8
  201. data/lib/action_view/helpers/tags/time_zone_select.rb +0 -20
  202. data/lib/action_view/helpers/tags/url_field.rb +0 -8
  203. data/lib/action_view/helpers/tags/week_field.rb +0 -13
  204. data/lib/action_view/helpers/tags.rb +0 -39
  205. data/lib/action_view/helpers/text_helper.rb +0 -443
  206. data/lib/action_view/helpers/translation_helper.rb +0 -107
  207. data/lib/action_view/helpers/url_helper.rb +0 -635
  208. data/lib/action_view/helpers.rb +0 -58
  209. data/lib/action_view/locale/en.yml +0 -56
  210. data/lib/action_view/log_subscriber.rb +0 -30
  211. data/lib/action_view/lookup_context.rb +0 -241
  212. data/lib/action_view/model_naming.rb +0 -12
  213. data/lib/action_view/path_set.rb +0 -77
  214. data/lib/action_view/railtie.rb +0 -43
  215. data/lib/action_view/record_identifier.rb +0 -84
  216. data/lib/action_view/renderer/abstract_renderer.rb +0 -47
  217. data/lib/action_view/renderer/partial_renderer.rb +0 -492
  218. data/lib/action_view/renderer/renderer.rb +0 -50
  219. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -103
  220. data/lib/action_view/renderer/template_renderer.rb +0 -96
  221. data/lib/action_view/routing_url_for.rb +0 -107
  222. data/lib/action_view/tasks/dependencies.rake +0 -17
  223. data/lib/action_view/template/error.rb +0 -138
  224. data/lib/action_view/template/handlers/builder.rb +0 -26
  225. data/lib/action_view/template/handlers/erb.rb +0 -146
  226. data/lib/action_view/template/handlers/raw.rb +0 -11
  227. data/lib/action_view/template/handlers.rb +0 -53
  228. data/lib/action_view/template/resolver.rb +0 -326
  229. data/lib/action_view/template/text.rb +0 -34
  230. data/lib/action_view/template/types.rb +0 -57
  231. data/lib/action_view/template.rb +0 -339
  232. data/lib/action_view/test_case.rb +0 -270
  233. data/lib/action_view/testing/resolvers.rb +0 -50
  234. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  235. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  236. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  237. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  238. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  239. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  240. data/lib/action_view/vendor/html-scanner.rb +0 -20
  241. data/lib/action_view.rb +0 -93
@@ -3,6 +3,7 @@ require 'active_support/core_ext/module/attribute_accessors'
3
3
  require 'active_support/core_ext/object/blank'
4
4
  require 'active_support/key_generator'
5
5
  require 'active_support/message_verifier'
6
+ require 'active_support/json'
6
7
 
7
8
  module ActionDispatch
8
9
  class Request < Rack::Request
@@ -23,15 +24,15 @@ module ActionDispatch
23
24
  # # This cookie will be deleted when the user's browser is closed.
24
25
  # cookies[:user_name] = "david"
25
26
  #
26
- # # Assign an array of values to a cookie.
27
- # cookies[:lat_lon] = [47.68, -122.37]
27
+ # # Cookie values are String based. Other data types need to be serialized.
28
+ # cookies[:lat_lon] = JSON.generate([47.68, -122.37])
28
29
  #
29
30
  # # Sets a cookie that expires in 1 hour.
30
31
  # cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now }
31
32
  #
32
33
  # # Sets a signed cookie, which prevents users from tampering with its value.
33
- # # The cookie is signed by your app's <tt>config.secret_key_base</tt> value.
34
- # # It can be read using the signed method <tt>cookies.signed[:name]</tt>
34
+ # # The cookie is signed by your app's `secrets.secret_key_base` value.
35
+ # # It can be read using the signed method `cookies.signed[:name]`
35
36
  # cookies.signed[:user_id] = current_user.id
36
37
  #
37
38
  # # Sets a "permanent" cookie (which expires in 20 years from now).
@@ -42,10 +43,10 @@ module ActionDispatch
42
43
  #
43
44
  # Examples of reading:
44
45
  #
45
- # cookies[:user_name] # => "david"
46
- # cookies.size # => 2
47
- # cookies[:lat_lon] # => [47.68, -122.37]
48
- # cookies.signed[:login] # => "XJ-122"
46
+ # cookies[:user_name] # => "david"
47
+ # cookies.size # => 2
48
+ # JSON.parse(cookies[:lat_lon]) # => [47.68, -122.37]
49
+ # cookies.signed[:login] # => "XJ-122"
49
50
  #
50
51
  # Example for deleting:
51
52
  #
@@ -63,18 +64,20 @@ module ActionDispatch
63
64
  #
64
65
  # The option symbols for setting cookies are:
65
66
  #
66
- # * <tt>:value</tt> - The cookie's value or list of values (as an array).
67
+ # * <tt>:value</tt> - The cookie's value.
67
68
  # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
68
69
  # of the application.
69
70
  # * <tt>:domain</tt> - The domain for which this cookie applies so you can
70
71
  # restrict to the domain level. If you use a schema like www.example.com
71
72
  # and want to share session with user.example.com set <tt>:domain</tt>
72
73
  # to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
73
- # <tt>:all</tt> again when deleting cookies.
74
+ # <tt>:all</tt> or <tt>Array</tt> again when deleting cookies.
74
75
  #
75
76
  # domain: nil # Does not sets cookie domain. (default)
76
77
  # domain: :all # Allow the cookie for the top most level
77
- # domain and subdomains.
78
+ # # domain and subdomains.
79
+ # domain: %w(.example.com .example.org) # Allow the cookie
80
+ # # for concrete domain names.
78
81
  #
79
82
  # * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
80
83
  # * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
@@ -89,6 +92,8 @@ module ActionDispatch
89
92
  ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze
90
93
  SECRET_TOKEN = "action_dispatch.secret_token".freeze
91
94
  SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
95
+ COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
96
+ COOKIES_DIGEST = "action_dispatch.cookies_digest".freeze
92
97
 
93
98
  # Cookies can typically store 4096 bytes.
94
99
  MAX_COOKIE_SIZE = 4096
@@ -117,10 +122,10 @@ module ActionDispatch
117
122
  # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
118
123
  # cookie was tampered with by the user (or a 3rd party), nil will be returned.
119
124
  #
120
- # If +config.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
125
+ # If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
121
126
  # legacy cookies signed with the old key generator will be transparently upgraded.
122
127
  #
123
- # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+.
128
+ # This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
124
129
  #
125
130
  # Example:
126
131
  #
@@ -140,10 +145,10 @@ module ActionDispatch
140
145
  # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
141
146
  # If the cookie was tampered with by the user (or a 3rd party), nil will be returned.
142
147
  #
143
- # If +config.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
148
+ # If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
144
149
  # legacy cookies signed with the old key generator will be transparently upgraded.
145
150
  #
146
- # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+.
151
+ # This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
147
152
  #
148
153
  # Example:
149
154
  #
@@ -172,15 +177,19 @@ module ActionDispatch
172
177
  end
173
178
  end
174
179
 
175
- module VerifyAndUpgradeLegacySignedMessage
180
+ # Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
181
+ # to the Message{Encryptor,Verifier} allows us to handle the
182
+ # (de)serialization step within the cookie jar, which gives us the
183
+ # opportunity to detect and migrate legacy cookies.
184
+ module VerifyAndUpgradeLegacySignedMessage # :nodoc:
176
185
  def initialize(*args)
177
186
  super
178
- @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token])
187
+ @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer)
179
188
  end
180
189
 
181
190
  def verify_and_upgrade_legacy_signed_message(name, signed_message)
182
- @legacy_verifier.verify(signed_message).tap do |value|
183
- self[name] = value
191
+ deserialize(name, @legacy_verifier.verify(signed_message)).tap do |value|
192
+ self[name] = { value: value }
184
193
  end
185
194
  rescue ActiveSupport::MessageVerifier::InvalidSignature
186
195
  nil
@@ -210,7 +219,9 @@ module ActionDispatch
210
219
  encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
211
220
  secret_token: env[SECRET_TOKEN],
212
221
  secret_key_base: env[SECRET_KEY_BASE],
213
- upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?
222
+ upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
223
+ serializer: env[COOKIES_SERIALIZER],
224
+ digest: env[COOKIES_DIGEST]
214
225
  }
215
226
  end
216
227
 
@@ -235,6 +246,15 @@ module ActionDispatch
235
246
  @secure = secure
236
247
  @options = options
237
248
  @cookies = {}
249
+ @committed = false
250
+ end
251
+
252
+ def committed?; @committed; end
253
+
254
+ def commit!
255
+ @committed = true
256
+ @set_cookies.freeze
257
+ @delete_cookies.freeze
238
258
  end
239
259
 
240
260
  def each(&block)
@@ -278,8 +298,8 @@ module ActionDispatch
278
298
  end
279
299
  end
280
300
 
281
- # Sets the cookie named +name+. The second argument may be the very cookie
282
- # value, or a hash of options as documented above.
301
+ # Sets the cookie named +name+. The second argument may be the cookie's
302
+ # value or a hash of options as documented above.
283
303
  def []=(name, options)
284
304
  if options.is_a?(Hash)
285
305
  options.symbolize_keys!
@@ -291,7 +311,7 @@ module ActionDispatch
291
311
 
292
312
  handle_options(options)
293
313
 
294
- if @cookies[name.to_s] != value or options[:expires]
314
+ if @cookies[name.to_s] != value || options[:expires]
295
315
  @cookies[name.to_s] = value
296
316
  @set_cookies[name.to_s] = options
297
317
  @delete_cookies.delete(name.to_s)
@@ -334,8 +354,8 @@ module ActionDispatch
334
354
  end
335
355
 
336
356
  def recycle! #:nodoc:
337
- @set_cookies.clear
338
- @delete_cookies.clear
357
+ @set_cookies = {}
358
+ @delete_cookies = {}
339
359
  end
340
360
 
341
361
  mattr_accessor :always_write_cookie
@@ -372,31 +392,83 @@ module ActionDispatch
372
392
  end
373
393
  end
374
394
 
395
+ class JsonSerializer # :nodoc:
396
+ def self.load(value)
397
+ ActiveSupport::JSON.decode(value)
398
+ end
399
+
400
+ def self.dump(value)
401
+ ActiveSupport::JSON.encode(value)
402
+ end
403
+ end
404
+
405
+ module SerializedCookieJars # :nodoc:
406
+ MARSHAL_SIGNATURE = "\x04\x08".freeze
407
+
408
+ protected
409
+ def needs_migration?(value)
410
+ @options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
411
+ end
412
+
413
+ def serialize(name, value)
414
+ serializer.dump(value)
415
+ end
416
+
417
+ def deserialize(name, value)
418
+ if value
419
+ if needs_migration?(value)
420
+ Marshal.load(value).tap do |v|
421
+ self[name] = { value: v }
422
+ end
423
+ else
424
+ serializer.load(value)
425
+ end
426
+ end
427
+ end
428
+
429
+ def serializer
430
+ serializer = @options[:serializer] || :marshal
431
+ case serializer
432
+ when :marshal
433
+ Marshal
434
+ when :json, :hybrid
435
+ JsonSerializer
436
+ else
437
+ serializer
438
+ end
439
+ end
440
+
441
+ def digest
442
+ @options[:digest] || 'SHA1'
443
+ end
444
+ end
445
+
375
446
  class SignedCookieJar #:nodoc:
376
447
  include ChainedCookieJars
448
+ include SerializedCookieJars
377
449
 
378
450
  def initialize(parent_jar, key_generator, options = {})
379
451
  @parent_jar = parent_jar
380
452
  @options = options
381
453
  secret = key_generator.generate_key(@options[:signed_cookie_salt])
382
- @verifier = ActiveSupport::MessageVerifier.new(secret)
454
+ @verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
383
455
  end
384
456
 
385
457
  def [](name)
386
458
  if signed_message = @parent_jar[name]
387
- verify(signed_message)
459
+ deserialize name, verify(signed_message)
388
460
  end
389
461
  end
390
462
 
391
463
  def []=(name, options)
392
464
  if options.is_a?(Hash)
393
465
  options.symbolize_keys!
394
- options[:value] = @verifier.generate(options[:value])
466
+ options[:value] = @verifier.generate(serialize(name, options[:value]))
395
467
  else
396
- options = { :value => @verifier.generate(options) }
468
+ options = { :value => @verifier.generate(serialize(name, options)) }
397
469
  end
398
470
 
399
- raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
471
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
400
472
  @parent_jar[name] = options
401
473
  end
402
474
 
@@ -409,7 +481,7 @@ module ActionDispatch
409
481
  end
410
482
 
411
483
  # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if
412
- # config.secret_token and config.secret_key_base are both set. It reads
484
+ # secrets.secret_token and secrets.secret_key_base are both set. It reads
413
485
  # legacy cookies signed with the old dummy key generator and re-saves
414
486
  # them using the new key generator to provide a smooth upgrade path.
415
487
  class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
@@ -417,30 +489,31 @@ module ActionDispatch
417
489
 
418
490
  def [](name)
419
491
  if signed_message = @parent_jar[name]
420
- verify(signed_message) || verify_and_upgrade_legacy_signed_message(name, signed_message)
492
+ deserialize(name, verify(signed_message)) || verify_and_upgrade_legacy_signed_message(name, signed_message)
421
493
  end
422
494
  end
423
495
  end
424
496
 
425
497
  class EncryptedCookieJar #:nodoc:
426
498
  include ChainedCookieJars
499
+ include SerializedCookieJars
427
500
 
428
501
  def initialize(parent_jar, key_generator, options = {})
429
502
  if ActiveSupport::LegacyKeyGenerator === key_generator
430
- raise "You didn't set config.secret_key_base, which is required for this cookie jar. " +
503
+ raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " +
431
504
  "Read the upgrade documentation to learn more about this new config option."
432
505
  end
433
506
 
434
507
  @parent_jar = parent_jar
435
508
  @options = options
436
- secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
509
+ secret = key_generator.generate_key(@options[:encrypted_cookie_salt])[0, ActiveSupport::MessageEncryptor.key_len]
437
510
  sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
438
- @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)
511
+ @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
439
512
  end
440
513
 
441
514
  def [](name)
442
515
  if encrypted_message = @parent_jar[name]
443
- decrypt_and_verify(encrypted_message)
516
+ deserialize name, decrypt_and_verify(encrypted_message)
444
517
  end
445
518
  end
446
519
 
@@ -450,9 +523,10 @@ module ActionDispatch
450
523
  else
451
524
  options = { :value => options }
452
525
  end
453
- options[:value] = @encryptor.encrypt_and_sign(options[:value])
454
526
 
455
- raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
527
+ options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value]))
528
+
529
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
456
530
  @parent_jar[name] = options
457
531
  end
458
532
 
@@ -465,7 +539,7 @@ module ActionDispatch
465
539
  end
466
540
 
467
541
  # UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore
468
- # instead of EncryptedCookieJar if config.secret_token and config.secret_key_base
542
+ # instead of EncryptedCookieJar if secrets.secret_token and secrets.secret_key_base
469
543
  # are both set. It reads legacy cookies signed with the old dummy key generator and
470
544
  # encrypts and re-saves them using the new key generator to provide a smooth upgrade path.
471
545
  class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc:
@@ -473,7 +547,7 @@ module ActionDispatch
473
547
 
474
548
  def [](name)
475
549
  if encrypted_or_signed_message = @parent_jar[name]
476
- decrypt_and_verify(encrypted_or_signed_message) || verify_and_upgrade_legacy_signed_message(name, encrypted_or_signed_message)
550
+ deserialize(name, decrypt_and_verify(encrypted_or_signed_message)) || verify_and_upgrade_legacy_signed_message(name, encrypted_or_signed_message)
477
551
  end
478
552
  end
479
553
  end
@@ -486,9 +560,11 @@ module ActionDispatch
486
560
  status, headers, body = @app.call(env)
487
561
 
488
562
  if cookie_jar = env['action_dispatch.cookies']
489
- cookie_jar.write(headers)
490
- if headers[HTTP_HEADER].respond_to?(:join)
491
- headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
563
+ unless cookie_jar.committed?
564
+ cookie_jar.write(headers)
565
+ if headers[HTTP_HEADER].respond_to?(:join)
566
+ headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
567
+ end
492
568
  end
493
569
  end
494
570
 
@@ -34,27 +34,46 @@ module ActionDispatch
34
34
  log_error(env, wrapper)
35
35
 
36
36
  if env['action_dispatch.show_detailed_exceptions']
37
+ request = Request.new(env)
38
+ traces = wrapper.traces
39
+
40
+ trace_to_show = 'Application Trace'
41
+ if traces[trace_to_show].empty? && wrapper.rescue_template != 'routing_error'
42
+ trace_to_show = 'Full Trace'
43
+ end
44
+
45
+ if source_to_show = traces[trace_to_show].first
46
+ source_to_show_id = source_to_show[:id]
47
+ end
48
+
37
49
  template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
38
- :request => Request.new(env),
39
- :exception => wrapper.exception,
40
- :application_trace => wrapper.application_trace,
41
- :framework_trace => wrapper.framework_trace,
42
- :full_trace => wrapper.full_trace,
43
- :routes_inspector => routes_inspector(exception),
44
- :source_extract => wrapper.source_extract,
45
- :line_number => wrapper.line_number,
46
- :file => wrapper.file
50
+ request: request,
51
+ exception: wrapper.exception,
52
+ traces: traces,
53
+ show_source_idx: source_to_show_id,
54
+ trace_to_show: trace_to_show,
55
+ routes_inspector: routes_inspector(exception),
56
+ source_extracts: wrapper.source_extracts,
57
+ line_number: wrapper.line_number,
58
+ file: wrapper.file
47
59
  )
48
60
  file = "rescues/#{wrapper.rescue_template}"
49
- body = template.render(:template => file, :layout => 'rescues/layout')
50
- render(wrapper.status_code, body)
61
+
62
+ if request.xhr?
63
+ body = template.render(template: file, layout: false, formats: [:text])
64
+ format = "text/plain"
65
+ else
66
+ body = template.render(template: file, layout: 'rescues/layout')
67
+ format = "text/html"
68
+ end
69
+ render(wrapper.status_code, body, format)
51
70
  else
52
71
  raise exception
53
72
  end
54
73
  end
55
74
 
56
- def render(status, body)
57
- [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
75
+ def render(status, body, format)
76
+ [status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
58
77
  end
59
78
 
60
79
  def log_error(env, wrapper)
@@ -1,21 +1,22 @@
1
1
  require 'action_controller/metal/exceptions'
2
- require 'active_support/core_ext/class/attribute_accessors'
2
+ require 'active_support/core_ext/module/attribute_accessors'
3
3
 
4
4
  module ActionDispatch
5
5
  class ExceptionWrapper
6
6
  cattr_accessor :rescue_responses
7
7
  @@rescue_responses = Hash.new(:internal_server_error)
8
8
  @@rescue_responses.merge!(
9
- 'ActionController::RoutingError' => :not_found,
10
- 'AbstractController::ActionNotFound' => :not_found,
11
- 'ActionController::MethodNotAllowed' => :method_not_allowed,
12
- 'ActionController::UnknownHttpMethod' => :method_not_allowed,
13
- 'ActionController::NotImplemented' => :not_implemented,
14
- 'ActionController::UnknownFormat' => :not_acceptable,
15
- 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
16
- 'ActionDispatch::ParamsParser::ParseError' => :bad_request,
17
- 'ActionController::BadRequest' => :bad_request,
18
- 'ActionController::ParameterMissing' => :bad_request
9
+ 'ActionController::RoutingError' => :not_found,
10
+ 'AbstractController::ActionNotFound' => :not_found,
11
+ 'ActionController::MethodNotAllowed' => :method_not_allowed,
12
+ 'ActionController::UnknownHttpMethod' => :method_not_allowed,
13
+ 'ActionController::NotImplemented' => :not_implemented,
14
+ 'ActionController::UnknownFormat' => :not_acceptable,
15
+ 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
16
+ 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity,
17
+ 'ActionDispatch::ParamsParser::ParseError' => :bad_request,
18
+ 'ActionController::BadRequest' => :bad_request,
19
+ 'ActionController::ParameterMissing' => :bad_request
19
20
  )
20
21
 
21
22
  cattr_accessor :rescue_templates
@@ -32,6 +33,8 @@ module ActionDispatch
32
33
  def initialize(env, exception)
33
34
  @env = env
34
35
  @exception = original_exception(exception)
36
+
37
+ expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
35
38
  end
36
39
 
37
40
  def rescue_template
@@ -54,21 +57,52 @@ module ActionDispatch
54
57
  clean_backtrace(:all)
55
58
  end
56
59
 
60
+ def traces
61
+ appplication_trace_with_ids = []
62
+ framework_trace_with_ids = []
63
+ full_trace_with_ids = []
64
+
65
+ full_trace.each_with_index do |trace, idx|
66
+ trace_with_id = { id: idx, trace: trace }
67
+
68
+ if application_trace.include?(trace)
69
+ appplication_trace_with_ids << trace_with_id
70
+ else
71
+ framework_trace_with_ids << trace_with_id
72
+ end
73
+
74
+ full_trace_with_ids << trace_with_id
75
+ end
76
+
77
+ {
78
+ "Application Trace" => appplication_trace_with_ids,
79
+ "Framework Trace" => framework_trace_with_ids,
80
+ "Full Trace" => full_trace_with_ids
81
+ }
82
+ end
83
+
57
84
  def self.status_code_for_exception(class_name)
58
85
  Rack::Utils.status_code(@@rescue_responses[class_name])
59
86
  end
60
87
 
61
- def source_extract
62
- if application_trace && trace = application_trace.first
63
- file, line, _ = trace.split(":")
64
- @file = file
65
- @line_number = line.to_i
66
- source_fragment(@file, @line_number)
88
+ def source_extracts
89
+ backtrace.map do |trace|
90
+ file, line = trace.split(":")
91
+ line_number = line.to_i
92
+
93
+ {
94
+ code: source_fragment(file, line_number),
95
+ line_number: line_number
96
+ }
67
97
  end
68
98
  end
69
99
 
70
100
  private
71
101
 
102
+ def backtrace
103
+ Array(@exception.backtrace)
104
+ end
105
+
72
106
  def original_exception(exception)
73
107
  if registered_original_exception?(exception)
74
108
  exception.original_exception
@@ -83,9 +117,9 @@ module ActionDispatch
83
117
 
84
118
  def clean_backtrace(*args)
85
119
  if backtrace_cleaner
86
- backtrace_cleaner.clean(@exception.backtrace, *args)
120
+ backtrace_cleaner.clean(backtrace, *args)
87
121
  else
88
- @exception.backtrace
122
+ backtrace
89
123
  end
90
124
  end
91
125
 
@@ -96,7 +130,7 @@ module ActionDispatch
96
130
  def source_fragment(path, line)
97
131
  return unless Rails.respond_to?(:root) && Rails.root
98
132
  full_path = Rails.root.join(path)
99
- if File.exists?(full_path)
133
+ if File.exist?(full_path)
100
134
  File.open(full_path, "r") do |file|
101
135
  start = [line - 3, 0].max
102
136
  lines = file.each_line.drop(start).take(6)
@@ -104,5 +138,11 @@ module ActionDispatch
104
138
  end
105
139
  end
106
140
  end
141
+
142
+ def expand_backtrace
143
+ @exception.backtrace.unshift(
144
+ @exception.to_s.split("\n")
145
+ ).flatten!
146
+ end
107
147
  end
108
148
  end