actionpack 4.2.11.3 → 5.0.7.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +890 -384
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/base.rb +28 -38
  6. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
  7. data/lib/abstract_controller/caching.rb +62 -0
  8. data/lib/abstract_controller/callbacks.rb +54 -19
  9. data/lib/abstract_controller/collector.rb +4 -9
  10. data/lib/abstract_controller/error.rb +4 -0
  11. data/lib/abstract_controller/helpers.rb +4 -3
  12. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  13. data/lib/abstract_controller/rendering.rb +28 -18
  14. data/lib/abstract_controller/translation.rb +8 -7
  15. data/lib/abstract_controller.rb +6 -2
  16. data/lib/action_controller/api/api_rendering.rb +14 -0
  17. data/lib/action_controller/api.rb +147 -0
  18. data/lib/action_controller/base.rb +14 -11
  19. data/lib/action_controller/caching.rb +13 -58
  20. data/lib/action_controller/form_builder.rb +48 -0
  21. data/lib/action_controller/log_subscriber.rb +3 -10
  22. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  23. data/lib/action_controller/metal/conditional_get.rb +106 -34
  24. data/lib/action_controller/metal/cookies.rb +1 -3
  25. data/lib/action_controller/metal/data_streaming.rb +14 -34
  26. data/lib/action_controller/metal/etag_with_template_digest.rb +8 -2
  27. data/lib/action_controller/metal/exceptions.rb +11 -6
  28. data/lib/action_controller/metal/force_ssl.rb +11 -11
  29. data/lib/action_controller/metal/head.rb +14 -8
  30. data/lib/action_controller/metal/helpers.rb +15 -6
  31. data/lib/action_controller/metal/http_authentication.rb +44 -35
  32. data/lib/action_controller/metal/implicit_render.rb +61 -6
  33. data/lib/action_controller/metal/instrumentation.rb +5 -5
  34. data/lib/action_controller/metal/live.rb +71 -88
  35. data/lib/action_controller/metal/mime_responds.rb +27 -42
  36. data/lib/action_controller/metal/params_wrapper.rb +9 -9
  37. data/lib/action_controller/metal/redirecting.rb +32 -9
  38. data/lib/action_controller/metal/renderers.rb +83 -40
  39. data/lib/action_controller/metal/rendering.rb +38 -6
  40. data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
  41. data/lib/action_controller/metal/rescue.rb +3 -12
  42. data/lib/action_controller/metal/streaming.rb +4 -4
  43. data/lib/action_controller/metal/strong_parameters.rb +527 -134
  44. data/lib/action_controller/metal/testing.rb +1 -12
  45. data/lib/action_controller/metal/url_for.rb +12 -5
  46. data/lib/action_controller/metal.rb +88 -63
  47. data/lib/action_controller/railtie.rb +11 -7
  48. data/lib/action_controller/renderer.rb +113 -0
  49. data/lib/action_controller/template_assertions.rb +9 -0
  50. data/lib/action_controller/test_case.rb +311 -374
  51. data/lib/action_controller.rb +12 -9
  52. data/lib/action_dispatch/http/cache.rb +73 -34
  53. data/lib/action_dispatch/http/filter_parameters.rb +16 -12
  54. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  55. data/lib/action_dispatch/http/headers.rb +45 -14
  56. data/lib/action_dispatch/http/mime_negotiation.rb +42 -23
  57. data/lib/action_dispatch/http/mime_type.rb +126 -90
  58. data/lib/action_dispatch/http/mime_types.rb +3 -4
  59. data/lib/action_dispatch/http/parameter_filter.rb +19 -9
  60. data/lib/action_dispatch/http/parameters.rb +70 -40
  61. data/lib/action_dispatch/http/request.rb +144 -89
  62. data/lib/action_dispatch/http/response.rb +215 -102
  63. data/lib/action_dispatch/http/upload.rb +6 -2
  64. data/lib/action_dispatch/http/url.rb +117 -8
  65. data/lib/action_dispatch/journey/formatter.rb +47 -30
  66. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  67. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  68. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  69. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  70. data/lib/action_dispatch/journey/parser.rb +2 -0
  71. data/lib/action_dispatch/journey/parser_extras.rb +8 -2
  72. data/lib/action_dispatch/journey/path/pattern.rb +38 -42
  73. data/lib/action_dispatch/journey/route.rb +88 -26
  74. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  75. data/lib/action_dispatch/journey/router.rb +8 -10
  76. data/lib/action_dispatch/journey/routes.rb +14 -15
  77. data/lib/action_dispatch/journey/visitors.rb +89 -44
  78. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  79. data/lib/action_dispatch/middleware/cookies.rb +188 -134
  80. data/lib/action_dispatch/middleware/debug_exceptions.rb +128 -49
  81. data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
  82. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
  83. data/lib/action_dispatch/middleware/executor.rb +19 -0
  84. data/lib/action_dispatch/middleware/flash.rb +66 -45
  85. data/lib/action_dispatch/middleware/params_parser.rb +32 -46
  86. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  87. data/lib/action_dispatch/middleware/reloader.rb +14 -58
  88. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  89. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  90. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  91. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  92. data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
  93. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  94. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  95. data/lib/action_dispatch/middleware/ssl.rb +124 -36
  96. data/lib/action_dispatch/middleware/stack.rb +44 -40
  97. data/lib/action_dispatch/middleware/static.rb +51 -35
  98. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  99. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  101. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  103. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  104. data/lib/action_dispatch/railtie.rb +2 -2
  105. data/lib/action_dispatch/request/session.rb +69 -33
  106. data/lib/action_dispatch/request/utils.rb +51 -19
  107. data/lib/action_dispatch/routing/inspector.rb +32 -43
  108. data/lib/action_dispatch/routing/mapper.rb +515 -348
  109. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  110. data/lib/action_dispatch/routing/redirection.rb +5 -4
  111. data/lib/action_dispatch/routing/route_set.rb +148 -240
  112. data/lib/action_dispatch/routing/url_for.rb +27 -10
  113. data/lib/action_dispatch/routing.rb +17 -13
  114. data/lib/action_dispatch/testing/assertion_response.rb +45 -0
  115. data/lib/action_dispatch/testing/assertions/response.rb +38 -20
  116. data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
  117. data/lib/action_dispatch/testing/assertions.rb +1 -1
  118. data/lib/action_dispatch/testing/integration.rb +377 -149
  119. data/lib/action_dispatch/testing/request_encoder.rb +53 -0
  120. data/lib/action_dispatch/testing/test_process.rb +24 -20
  121. data/lib/action_dispatch/testing/test_request.rb +22 -31
  122. data/lib/action_dispatch/testing/test_response.rb +12 -4
  123. data/lib/action_dispatch.rb +4 -1
  124. data/lib/action_pack/gem_version.rb +4 -4
  125. data/lib/action_pack.rb +1 -1
  126. metadata +32 -34
  127. data/lib/action_controller/metal/hide_actions.rb +0 -40
  128. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  129. data/lib/action_controller/middleware.rb +0 -39
  130. data/lib/action_controller/model_naming.rb +0 -12
  131. data/lib/action_dispatch/journey/backwards.rb +0 -5
  132. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  133. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  134. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  135. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
  136. /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -7,7 +7,16 @@ module ActionDispatch
7
7
  define_callbacks :call
8
8
 
9
9
  class << self
10
- delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
10
+ def to_prepare(*args, &block)
11
+ ActiveSupport::Reloader.to_prepare(*args, &block)
12
+ end
13
+
14
+ def to_cleanup(*args, &block)
15
+ ActiveSupport::Reloader.to_complete(*args, &block)
16
+ end
17
+
18
+ deprecate to_prepare: 'use ActiveSupport::Reloader.to_prepare instead',
19
+ to_cleanup: 'use ActiveSupport::Reloader.to_complete instead'
11
20
 
12
21
  def before(*args, &block)
13
22
  set_callback(:call, :before, *args, &block)
@@ -1,15 +1,64 @@
1
1
  require 'active_support/core_ext/hash/keys'
2
- require 'active_support/core_ext/module/attribute_accessors'
3
- require 'active_support/core_ext/object/blank'
4
2
  require 'active_support/key_generator'
5
3
  require 'active_support/message_verifier'
6
4
  require 'active_support/json'
5
+ require 'rack/utils'
7
6
 
8
7
  module ActionDispatch
9
- class Request < Rack::Request
8
+ class Request
10
9
  def cookie_jar
11
- env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
10
+ fetch_header('action_dispatch.cookies'.freeze) do
11
+ self.cookie_jar = Cookies::CookieJar.build(self, cookies)
12
+ end
13
+ end
14
+
15
+ # :stopdoc:
16
+ prepend Module.new {
17
+ def commit_cookie_jar!
18
+ cookie_jar.commit!
19
+ end
20
+ }
21
+
22
+ def have_cookie_jar?
23
+ has_header? 'action_dispatch.cookies'.freeze
24
+ end
25
+
26
+ def cookie_jar=(jar)
27
+ set_header 'action_dispatch.cookies'.freeze, jar
28
+ end
29
+
30
+ def key_generator
31
+ get_header Cookies::GENERATOR_KEY
32
+ end
33
+
34
+ def signed_cookie_salt
35
+ get_header Cookies::SIGNED_COOKIE_SALT
36
+ end
37
+
38
+ def encrypted_cookie_salt
39
+ get_header Cookies::ENCRYPTED_COOKIE_SALT
40
+ end
41
+
42
+ def encrypted_signed_cookie_salt
43
+ get_header Cookies::ENCRYPTED_SIGNED_COOKIE_SALT
12
44
  end
45
+
46
+ def secret_token
47
+ get_header Cookies::SECRET_TOKEN
48
+ end
49
+
50
+ def secret_key_base
51
+ get_header Cookies::SECRET_KEY_BASE
52
+ end
53
+
54
+ def cookies_serializer
55
+ get_header Cookies::COOKIES_SERIALIZER
56
+ end
57
+
58
+ def cookies_digest
59
+ get_header Cookies::COOKIES_DIGEST
60
+ end
61
+ # :startdoc:
13
62
  end
14
63
 
15
64
  # \Cookies are read and written through ActionController#cookies.
@@ -35,6 +84,12 @@ module ActionDispatch
35
84
  # # It can be read using the signed method `cookies.signed[:name]`
36
85
  # cookies.signed[:user_id] = current_user.id
37
86
  #
87
+ # # Sets an encrypted cookie value before sending it to the client which
88
+ # # prevent users from reading and tampering with its value.
89
+ # # The cookie is signed by your app's `secrets.secret_key_base` value.
90
+ # # It can be read using the encrypted method `cookies.encrypted[:name]`
91
+ # cookies.encrypted[:discount] = 45
92
+ #
38
93
  # # Sets a "permanent" cookie (which expires in 20 years from now).
39
94
  # cookies.permanent[:login] = "XJ-122"
40
95
  #
@@ -47,6 +102,7 @@ module ActionDispatch
47
102
  # cookies.size # => 2
48
103
  # JSON.parse(cookies[:lat_lon]) # => [47.68, -122.37]
49
104
  # cookies.signed[:login] # => "XJ-122"
105
+ # cookies.encrypted[:discount] # => 45
50
106
  #
51
107
  # Example for deleting:
52
108
  #
@@ -73,12 +129,15 @@ module ActionDispatch
73
129
  # to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
74
130
  # <tt>:all</tt> or <tt>Array</tt> again when deleting cookies.
75
131
  #
76
- # domain: nil # Does not sets cookie domain. (default)
132
+ # domain: nil # Does not set cookie domain. (default)
77
133
  # domain: :all # Allow the cookie for the top most level
78
134
  # # domain and subdomains.
79
135
  # domain: %w(.example.com .example.org) # Allow the cookie
80
136
  # # for concrete domain names.
81
137
  #
138
+ # * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
139
+ # set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
140
+ # For example, to share cookies between user1.lvh.me and user2.lvh.me, set <tt>:tld_length</tt> to 1.
82
141
  # * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
83
142
  # * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
84
143
  # Default is +false+.
@@ -115,7 +174,7 @@ module ActionDispatch
115
174
  # cookies.permanent.signed[:remember_me] = current_user.id
116
175
  # # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
117
176
  def permanent
118
- @permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
177
+ @permanent ||= PermanentCookieJar.new(self)
119
178
  end
120
179
 
121
180
  # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
@@ -135,10 +194,10 @@ module ActionDispatch
135
194
  # cookies.signed[:discount] # => 45
136
195
  def signed
137
196
  @signed ||=
138
- if @options[:upgrade_legacy_signed_cookies]
139
- UpgradeLegacySignedCookieJar.new(self, @key_generator, @options)
197
+ if upgrade_legacy_signed_cookies?
198
+ UpgradeLegacySignedCookieJar.new(self)
140
199
  else
141
- SignedCookieJar.new(self, @key_generator, @options)
200
+ SignedCookieJar.new(self)
142
201
  end
143
202
  end
144
203
 
@@ -158,10 +217,10 @@ module ActionDispatch
158
217
  # cookies.encrypted[:discount] # => 45
159
218
  def encrypted
160
219
  @encrypted ||=
161
- if @options[:upgrade_legacy_signed_cookies]
162
- UpgradeLegacyEncryptedCookieJar.new(self, @key_generator, @options)
220
+ if upgrade_legacy_signed_cookies?
221
+ UpgradeLegacyEncryptedCookieJar.new(self)
163
222
  else
164
- EncryptedCookieJar.new(self, @key_generator, @options)
223
+ EncryptedCookieJar.new(self)
165
224
  end
166
225
  end
167
226
 
@@ -169,12 +228,18 @@ module ActionDispatch
169
228
  # Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores.
170
229
  def signed_or_encrypted
171
230
  @signed_or_encrypted ||=
172
- if @options[:secret_key_base].present?
231
+ if request.secret_key_base.present?
173
232
  encrypted
174
233
  else
175
234
  signed
176
235
  end
177
236
  end
237
+
238
+ private
239
+
240
+ def upgrade_legacy_signed_cookies?
241
+ request.secret_token.present? && request.secret_key_base.present?
242
+ end
178
243
  end
179
244
 
180
245
  # Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
@@ -184,7 +249,7 @@ module ActionDispatch
184
249
  module VerifyAndUpgradeLegacySignedMessage # :nodoc:
185
250
  def initialize(*args)
186
251
  super
187
- @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer)
252
+ @legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
188
253
  end
189
254
 
190
255
  def verify_and_upgrade_legacy_signed_message(name, signed_message)
@@ -194,6 +259,11 @@ module ActionDispatch
194
259
  rescue ActiveSupport::MessageVerifier::InvalidSignature
195
260
  nil
196
261
  end
262
+
263
+ private
264
+ def parse(name, signed_message)
265
+ super || verify_and_upgrade_legacy_signed_message(name, signed_message)
266
+ end
197
267
  end
198
268
 
199
269
  class CookieJar #:nodoc:
@@ -213,38 +283,18 @@ module ActionDispatch
213
283
  # $& => example.local
214
284
  DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
215
285
 
216
- def self.options_for_env(env) #:nodoc:
217
- { signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '',
218
- encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '',
219
- encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
220
- secret_token: env[SECRET_TOKEN],
221
- secret_key_base: env[SECRET_KEY_BASE],
222
- upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
223
- serializer: env[COOKIES_SERIALIZER],
224
- digest: env[COOKIES_DIGEST]
225
- }
226
- end
227
-
228
- def self.build(request)
229
- env = request.env
230
- key_generator = env[GENERATOR_KEY]
231
- options = options_for_env env
232
-
233
- host = request.host
234
- secure = request.ssl?
235
-
236
- new(key_generator, host, secure, options).tap do |hash|
237
- hash.update(request.cookies)
286
+ def self.build(req, cookies)
287
+ new(req).tap do |hash|
288
+ hash.update(cookies)
238
289
  end
239
290
  end
240
291
 
241
- def initialize(key_generator, host = nil, secure = false, options = {})
242
- @key_generator = key_generator
292
+ attr_reader :request
293
+
294
+ def initialize(request)
243
295
  @set_cookies = {}
244
296
  @delete_cookies = {}
245
- @host = host
246
- @secure = secure
247
- @options = options
297
+ @request = request
248
298
  @cookies = {}
249
299
  @committed = false
250
300
  end
@@ -280,21 +330,32 @@ module ActionDispatch
280
330
  self
281
331
  end
282
332
 
333
+ def update_cookies_from_jar
334
+ request_jar = @request.cookie_jar.instance_variable_get(:@cookies)
335
+ set_cookies = request_jar.reject { |k,_| @delete_cookies.key?(k) }
336
+
337
+ @cookies.update set_cookies if set_cookies
338
+ end
339
+
340
+ def to_header
341
+ @cookies.map { |k,v| "#{escape(k)}=#{escape(v)}" }.join '; '
342
+ end
343
+
283
344
  def handle_options(options) #:nodoc:
284
345
  options[:path] ||= "/"
285
346
 
286
- if options[:domain] == :all
347
+ if options[:domain] == :all || options[:domain] == 'all'
287
348
  # if there is a provided tld length then we use it otherwise default domain regexp
288
349
  domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
289
350
 
290
351
  # if host is not ip and matches domain regexp
291
352
  # (ip confirms to domain regexp so we explicitly check for ip)
292
- options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
353
+ options[:domain] = if (request.host !~ /^[\d.]+$/) && (request.host =~ domain_regexp)
293
354
  ".#{$&}"
294
355
  end
295
356
  elsif options[:domain].is_a? Array
296
357
  # if host matches one of the supplied domains without a dot in front of it
297
- options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') }
358
+ options[:domain] = options[:domain].find {|domain| request.host.include? domain.sub(/^\./, '') }
298
359
  end
299
360
  end
300
361
 
@@ -349,47 +410,75 @@ module ActionDispatch
349
410
  end
350
411
 
351
412
  def write(headers)
352
- @set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) }
353
- @delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
354
- end
355
-
356
- def recycle! #:nodoc:
357
- @set_cookies = {}
358
- @delete_cookies = {}
413
+ if header = make_set_cookie_header(headers[HTTP_HEADER])
414
+ headers[HTTP_HEADER] = header
415
+ end
359
416
  end
360
417
 
361
418
  mattr_accessor :always_write_cookie
362
419
  self.always_write_cookie = false
363
420
 
364
421
  private
365
- def write_cookie?(cookie)
366
- @secure || !cookie[:secure] || always_write_cookie
367
- end
422
+
423
+ def escape(string)
424
+ ::Rack::Utils.escape(string)
425
+ end
426
+
427
+ def make_set_cookie_header(header)
428
+ header = @set_cookies.inject(header) { |m, (k, v)|
429
+ if write_cookie?(v)
430
+ ::Rack::Utils.add_cookie_to_header(m, k, v)
431
+ else
432
+ m
433
+ end
434
+ }
435
+ @delete_cookies.inject(header) { |m, (k, v)|
436
+ ::Rack::Utils.add_remove_cookie_to_header(m, k, v)
437
+ }
438
+ end
439
+
440
+ def write_cookie?(cookie)
441
+ request.ssl? || !cookie[:secure] || always_write_cookie
442
+ end
368
443
  end
369
444
 
370
- class PermanentCookieJar #:nodoc:
445
+ class AbstractCookieJar # :nodoc:
371
446
  include ChainedCookieJars
372
447
 
373
- def initialize(parent_jar, key_generator, options = {})
448
+ def initialize(parent_jar)
374
449
  @parent_jar = parent_jar
375
- @key_generator = key_generator
376
- @options = options
377
450
  end
378
451
 
379
452
  def [](name)
380
- @parent_jar[name.to_s]
453
+ if data = @parent_jar[name.to_s]
454
+ parse name, data
455
+ end
381
456
  end
382
457
 
383
458
  def []=(name, options)
384
459
  if options.is_a?(Hash)
385
460
  options.symbolize_keys!
386
461
  else
387
- options = { :value => options }
462
+ options = { value: options }
388
463
  end
389
464
 
390
- options[:expires] = 20.years.from_now
465
+ commit(options)
391
466
  @parent_jar[name] = options
392
467
  end
468
+
469
+ protected
470
+ def request; @parent_jar.request; end
471
+
472
+ private
473
+ def parse(name, data); data; end
474
+ def commit(options); end
475
+ end
476
+
477
+ class PermanentCookieJar < AbstractCookieJar # :nodoc:
478
+ private
479
+ def commit(options)
480
+ options[:expires] = 20.years.from_now
481
+ end
393
482
  end
394
483
 
395
484
  class JsonSerializer # :nodoc:
@@ -407,10 +496,10 @@ module ActionDispatch
407
496
 
408
497
  protected
409
498
  def needs_migration?(value)
410
- @options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
499
+ request.cookies_serializer == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
411
500
  end
412
501
 
413
- def serialize(name, value)
502
+ def serialize(value)
414
503
  serializer.dump(value)
415
504
  end
416
505
 
@@ -427,7 +516,7 @@ module ActionDispatch
427
516
  end
428
517
 
429
518
  def serializer
430
- serializer = @options[:serializer] || :marshal
519
+ serializer = request.cookies_serializer || :marshal
431
520
  case serializer
432
521
  when :marshal
433
522
  Marshal
@@ -439,103 +528,71 @@ module ActionDispatch
439
528
  end
440
529
 
441
530
  def digest
442
- @options[:digest] || 'SHA1'
531
+ request.cookies_digest || 'SHA1'
532
+ end
533
+
534
+ def key_generator
535
+ request.key_generator
443
536
  end
444
537
  end
445
538
 
446
- class SignedCookieJar #:nodoc:
447
- include ChainedCookieJars
539
+ class SignedCookieJar < AbstractCookieJar # :nodoc:
448
540
  include SerializedCookieJars
449
541
 
450
- def initialize(parent_jar, key_generator, options = {})
451
- @parent_jar = parent_jar
452
- @options = options
453
- secret = key_generator.generate_key(@options[:signed_cookie_salt])
542
+ def initialize(parent_jar)
543
+ super
544
+ secret = key_generator.generate_key(request.signed_cookie_salt)
454
545
  @verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
455
546
  end
456
547
 
457
- def [](name)
458
- if signed_message = @parent_jar[name]
459
- deserialize name, verify(signed_message)
460
- end
461
- end
462
-
463
- def []=(name, options)
464
- if options.is_a?(Hash)
465
- options.symbolize_keys!
466
- options[:value] = @verifier.generate(serialize(name, options[:value]))
467
- else
468
- options = { :value => @verifier.generate(serialize(name, options)) }
548
+ private
549
+ def parse(name, signed_message)
550
+ deserialize name, @verifier.verified(signed_message)
469
551
  end
470
552
 
471
- raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
472
- @parent_jar[name] = options
473
- end
553
+ def commit(options)
554
+ options[:value] = @verifier.generate(serialize(options[:value]))
474
555
 
475
- private
476
- def verify(signed_message)
477
- @verifier.verify(signed_message)
478
- rescue ActiveSupport::MessageVerifier::InvalidSignature
479
- nil
556
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
480
557
  end
481
558
  end
482
559
 
483
560
  # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if
484
561
  # secrets.secret_token and secrets.secret_key_base are both set. It reads
485
- # legacy cookies signed with the old dummy key generator and re-saves
486
- # them using the new key generator to provide a smooth upgrade path.
562
+ # legacy cookies signed with the old dummy key generator and signs and
563
+ # re-saves them using the new key generator to provide a smooth upgrade path.
487
564
  class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
488
565
  include VerifyAndUpgradeLegacySignedMessage
489
-
490
- def [](name)
491
- if signed_message = @parent_jar[name]
492
- deserialize(name, verify(signed_message)) || verify_and_upgrade_legacy_signed_message(name, signed_message)
493
- end
494
- end
495
566
  end
496
567
 
497
- class EncryptedCookieJar #:nodoc:
498
- include ChainedCookieJars
568
+ class EncryptedCookieJar < AbstractCookieJar # :nodoc:
499
569
  include SerializedCookieJars
500
570
 
501
- def initialize(parent_jar, key_generator, options = {})
571
+ def initialize(parent_jar)
572
+ super
573
+
502
574
  if ActiveSupport::LegacyKeyGenerator === key_generator
503
575
  raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " +
504
576
  "Read the upgrade documentation to learn more about this new config option."
505
577
  end
506
578
 
507
- @parent_jar = parent_jar
508
- @options = options
509
- secret = key_generator.generate_key(@options[:encrypted_cookie_salt])[0, ActiveSupport::MessageEncryptor.key_len]
510
- sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
579
+ secret = key_generator.generate_key(request.encrypted_cookie_salt || "")[0, ActiveSupport::MessageEncryptor.key_len]
580
+ sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || "")
511
581
  @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
512
582
  end
513
583
 
514
- def [](name)
515
- if encrypted_message = @parent_jar[name]
516
- deserialize name, decrypt_and_verify(encrypted_message)
517
- end
518
- end
519
-
520
- def []=(name, options)
521
- if options.is_a?(Hash)
522
- options.symbolize_keys!
523
- else
524
- options = { :value => options }
525
- end
526
-
527
- options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value]))
528
-
529
- raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
530
- @parent_jar[name] = options
531
- end
532
-
533
584
  private
534
- def decrypt_and_verify(encrypted_message)
535
- @encryptor.decrypt_and_verify(encrypted_message)
585
+ def parse(name, encrypted_message)
586
+ deserialize name, @encryptor.decrypt_and_verify(encrypted_message)
536
587
  rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
537
588
  nil
538
589
  end
590
+
591
+ def commit(options)
592
+ options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]))
593
+
594
+ raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
595
+ end
539
596
  end
540
597
 
541
598
  # UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore
@@ -544,12 +601,6 @@ module ActionDispatch
544
601
  # encrypts and re-saves them using the new key generator to provide a smooth upgrade path.
545
602
  class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc:
546
603
  include VerifyAndUpgradeLegacySignedMessage
547
-
548
- def [](name)
549
- if encrypted_or_signed_message = @parent_jar[name]
550
- deserialize(name, decrypt_and_verify(encrypted_or_signed_message)) || verify_and_upgrade_legacy_signed_message(name, encrypted_or_signed_message)
551
- end
552
- end
553
604
  end
554
605
 
555
606
  def initialize(app)
@@ -557,9 +608,12 @@ module ActionDispatch
557
608
  end
558
609
 
559
610
  def call(env)
611
+ request = ActionDispatch::Request.new env
612
+
560
613
  status, headers, body = @app.call(env)
561
614
 
562
- if cookie_jar = env['action_dispatch.cookies']
615
+ if request.have_cookie_jar?
616
+ cookie_jar = request.cookie_jar
563
617
  unless cookie_jar.committed?
564
618
  cookie_jar.write(headers)
565
619
  if headers[HTTP_HEADER].respond_to?(:join)