actionpack 5.2.3.rc1 → 6.0.0.beta1

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +124 -337
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/abstract_controller/base.rb +4 -2
  6. data/lib/abstract_controller/caching/fragments.rb +6 -21
  7. data/lib/abstract_controller/callbacks.rb +12 -0
  8. data/lib/abstract_controller/collector.rb +1 -1
  9. data/lib/abstract_controller/helpers.rb +2 -2
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
  11. data/lib/action_controller.rb +1 -0
  12. data/lib/action_controller/api.rb +2 -1
  13. data/lib/action_controller/base.rb +2 -7
  14. data/lib/action_controller/caching.rb +1 -1
  15. data/lib/action_controller/log_subscriber.rb +8 -5
  16. data/lib/action_controller/metal/conditional_get.rb +9 -3
  17. data/lib/action_controller/metal/data_streaming.rb +5 -6
  18. data/lib/action_controller/metal/default_headers.rb +17 -0
  19. data/lib/action_controller/metal/exceptions.rb +22 -1
  20. data/lib/action_controller/metal/flash.rb +5 -5
  21. data/lib/action_controller/metal/force_ssl.rb +17 -57
  22. data/lib/action_controller/metal/head.rb +1 -1
  23. data/lib/action_controller/metal/helpers.rb +1 -2
  24. data/lib/action_controller/metal/http_authentication.rb +20 -21
  25. data/lib/action_controller/metal/implicit_render.rb +2 -12
  26. data/lib/action_controller/metal/instrumentation.rb +3 -5
  27. data/lib/action_controller/metal/live.rb +28 -26
  28. data/lib/action_controller/metal/mime_responds.rb +13 -2
  29. data/lib/action_controller/metal/params_wrapper.rb +16 -12
  30. data/lib/action_controller/metal/redirecting.rb +32 -11
  31. data/lib/action_controller/metal/rendering.rb +1 -1
  32. data/lib/action_controller/metal/request_forgery_protection.rb +22 -11
  33. data/lib/action_controller/metal/strong_parameters.rb +57 -32
  34. data/lib/action_controller/metal/url_for.rb +1 -1
  35. data/lib/action_controller/railties/helpers.rb +1 -1
  36. data/lib/action_controller/renderer.rb +15 -2
  37. data/lib/action_controller/test_case.rb +1 -4
  38. data/lib/action_dispatch.rb +3 -1
  39. data/lib/action_dispatch/http/cache.rb +14 -10
  40. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  41. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  42. data/lib/action_dispatch/http/filter_parameters.rb +8 -6
  43. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  44. data/lib/action_dispatch/http/headers.rb +1 -1
  45. data/lib/action_dispatch/http/mime_negotiation.rb +7 -10
  46. data/lib/action_dispatch/http/mime_type.rb +1 -5
  47. data/lib/action_dispatch/http/parameter_filter.rb +5 -79
  48. data/lib/action_dispatch/http/parameters.rb +13 -3
  49. data/lib/action_dispatch/http/request.rb +10 -13
  50. data/lib/action_dispatch/http/response.rb +18 -17
  51. data/lib/action_dispatch/http/upload.rb +5 -0
  52. data/lib/action_dispatch/http/url.rb +81 -81
  53. data/lib/action_dispatch/journey/formatter.rb +1 -1
  54. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
  55. data/lib/action_dispatch/journey/nodes/node.rb +9 -8
  56. data/lib/action_dispatch/journey/path/pattern.rb +3 -3
  57. data/lib/action_dispatch/journey/router.rb +0 -3
  58. data/lib/action_dispatch/journey/router/utils.rb +10 -10
  59. data/lib/action_dispatch/journey/scanner.rb +11 -4
  60. data/lib/action_dispatch/journey/visitors.rb +1 -1
  61. data/lib/action_dispatch/middleware/callbacks.rb +2 -4
  62. data/lib/action_dispatch/middleware/cookies.rb +49 -70
  63. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -58
  64. data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
  65. data/lib/action_dispatch/middleware/debug_view.rb +50 -0
  66. data/lib/action_dispatch/middleware/exception_wrapper.rb +36 -7
  67. data/lib/action_dispatch/middleware/flash.rb +1 -1
  68. data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
  69. data/lib/action_dispatch/middleware/remote_ip.rb +6 -8
  70. data/lib/action_dispatch/middleware/request_id.rb +2 -2
  71. data/lib/action_dispatch/middleware/session/cookie_store.rb +4 -10
  72. data/lib/action_dispatch/middleware/ssl.rb +8 -8
  73. data/lib/action_dispatch/middleware/static.rb +5 -6
  74. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
  75. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  76. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  77. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  78. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +20 -2
  79. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -4
  80. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
  81. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  82. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  83. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  84. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  85. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
  86. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
  87. data/lib/action_dispatch/railtie.rb +1 -0
  88. data/lib/action_dispatch/request/session.rb +8 -0
  89. data/lib/action_dispatch/routing.rb +3 -2
  90. data/lib/action_dispatch/routing/inspector.rb +99 -50
  91. data/lib/action_dispatch/routing/mapper.rb +36 -29
  92. data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -4
  93. data/lib/action_dispatch/routing/route_set.rb +11 -12
  94. data/lib/action_dispatch/routing/url_for.rb +1 -0
  95. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +3 -3
  96. data/lib/action_dispatch/testing/assertions/response.rb +2 -3
  97. data/lib/action_dispatch/testing/assertions/routing.rb +7 -2
  98. data/lib/action_dispatch/testing/integration.rb +11 -4
  99. data/lib/action_dispatch/testing/test_process.rb +2 -2
  100. data/lib/action_dispatch/testing/test_response.rb +4 -32
  101. data/lib/action_pack.rb +1 -1
  102. data/lib/action_pack/gem_version.rb +4 -4
  103. metadata +19 -11
@@ -50,7 +50,7 @@ module ActionDispatch
50
50
  unmatched_keys = (missing_keys || []) & constraints.keys
51
51
  missing_keys = (missing_keys || []) - unmatched_keys
52
52
 
53
- message = "No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}".dup
53
+ message = +"No route matches #{Hash[constraints.sort_by { |k, v| k.to_s }].inspect}"
54
54
  message << ", missing required keys: #{missing_keys.sort.inspect}" if missing_keys && !missing_keys.empty?
55
55
  message << ", possible unmatched constraints: #{unmatched_keys.sort.inspect}" if unmatched_keys && !unmatched_keys.empty?
56
56
 
@@ -25,8 +25,6 @@ module ActionDispatch
25
25
  state = tt.eclosure(0)
26
26
  until input.eos?
27
27
  sym = input.scan(%r([/.?]|[^/.?]+))
28
-
29
- # FIXME: tt.eclosure is not needed for the GTG
30
28
  state = tt.eclosure(tt.move(state, sym))
31
29
  end
32
30
 
@@ -32,7 +32,7 @@ module ActionDispatch
32
32
  end
33
33
 
34
34
  def name
35
- left.tr "*:".freeze, "".freeze
35
+ -left.tr("*:", "")
36
36
  end
37
37
 
38
38
  def type
@@ -65,12 +65,12 @@ module ActionDispatch
65
65
  def literal?; false; end
66
66
  end
67
67
 
68
- %w{ Symbol Slash Dot }.each do |t|
69
- class_eval <<-eoruby, __FILE__, __LINE__ + 1
70
- class #{t} < Terminal;
71
- def type; :#{t.upcase}; end
72
- end
73
- eoruby
68
+ class Slash < Terminal # :nodoc:
69
+ def type; :SLASH; end
70
+ end
71
+
72
+ class Dot < Terminal # :nodoc:
73
+ def type; :DOT; end
74
74
  end
75
75
 
76
76
  class Symbol < Terminal # :nodoc:
@@ -82,13 +82,14 @@ module ActionDispatch
82
82
  def initialize(left)
83
83
  super
84
84
  @regexp = DEFAULT_EXP
85
- @name = left.tr "*:".freeze, "".freeze
85
+ @name = -left.tr("*:", "")
86
86
  end
87
87
 
88
88
  def default_regexp?
89
89
  regexp == DEFAULT_EXP
90
90
  end
91
91
 
92
+ def type; :SYMBOL; end
92
93
  def symbol?; true; end
93
94
  end
94
95
 
@@ -90,7 +90,7 @@ module ActionDispatch
90
90
  return @separator_re unless @matchers.key?(node)
91
91
 
92
92
  re = @matchers[node]
93
- "(#{re})"
93
+ "(#{Regexp.union(re)})"
94
94
  end
95
95
 
96
96
  def visit_GROUP(node)
@@ -119,7 +119,7 @@ module ActionDispatch
119
119
 
120
120
  class UnanchoredRegexp < AnchoredRegexp # :nodoc:
121
121
  def accept(node)
122
- %r{\A#{visit node}(?:\b|\Z)}
122
+ %r{\A#{visit node}}
123
123
  end
124
124
  end
125
125
 
@@ -183,7 +183,7 @@ module ActionDispatch
183
183
  node = node.to_sym
184
184
 
185
185
  if @requirements.key?(node)
186
- re = /#{@requirements[node]}|/
186
+ re = /#{Regexp.union(@requirements[node])}|/
187
187
  @offsets.push((re.match("").length - 1) + @offsets.last)
188
188
  else
189
189
  @offsets << @offsets.last
@@ -15,9 +15,6 @@ require "action_dispatch/journey/path/pattern"
15
15
  module ActionDispatch
16
16
  module Journey # :nodoc:
17
17
  class Router # :nodoc:
18
- class RoutingError < ::StandardError # :nodoc:
19
- end
20
-
21
18
  attr_accessor :routes
22
19
 
23
20
  def initialize(routes)
@@ -17,11 +17,11 @@ module ActionDispatch
17
17
  def self.normalize_path(path)
18
18
  path ||= ""
19
19
  encoding = path.encoding
20
- path = "/#{path}".dup
21
- path.squeeze!("/".freeze)
22
- path.sub!(%r{/+\Z}, "".freeze)
20
+ path = +"/#{path}"
21
+ path.squeeze!("/")
22
+ path.sub!(%r{/+\Z}, "")
23
23
  path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
24
- path = "/".dup if path == "".freeze
24
+ path = +"/" if path == ""
25
25
  path.force_encoding(encoding)
26
26
  path
27
27
  end
@@ -29,16 +29,16 @@ module ActionDispatch
29
29
  # URI path and fragment escaping
30
30
  # https://tools.ietf.org/html/rfc3986
31
31
  class UriEncoder # :nodoc:
32
- ENCODE = "%%%02X".freeze
32
+ ENCODE = "%%%02X"
33
33
  US_ASCII = Encoding::US_ASCII
34
34
  UTF_8 = Encoding::UTF_8
35
- EMPTY = "".dup.force_encoding(US_ASCII).freeze
35
+ EMPTY = (+"").force_encoding(US_ASCII).freeze
36
36
  DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
37
37
 
38
- ALPHA = "a-zA-Z".freeze
39
- DIGIT = "0-9".freeze
40
- UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~".freeze
41
- SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;=".freeze
38
+ ALPHA = "a-zA-Z"
39
+ DIGIT = "0-9"
40
+ UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
41
+ SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
42
42
 
43
43
  ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
44
44
 
@@ -34,6 +34,13 @@ module ActionDispatch
34
34
 
35
35
  private
36
36
 
37
+ # takes advantage of String @- deduping capabilities in Ruby 2.5 upwards
38
+ # see: https://bugs.ruby-lang.org/issues/13077
39
+ def dedup_scan(regex)
40
+ r = @ss.scan(regex)
41
+ r ? -r : nil
42
+ end
43
+
37
44
  def scan
38
45
  case
39
46
  # /
@@ -47,15 +54,15 @@ module ActionDispatch
47
54
  [:OR, "|"]
48
55
  when @ss.skip(/\./)
49
56
  [:DOT, "."]
50
- when text = @ss.scan(/:\w+/)
57
+ when text = dedup_scan(/:\w+/)
51
58
  [:SYMBOL, text]
52
- when text = @ss.scan(/\*\w+/)
59
+ when text = dedup_scan(/\*\w+/)
53
60
  [:STAR, text]
54
61
  when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
55
62
  text.tr! "\\", ""
56
- [:LITERAL, text]
63
+ [:LITERAL, -text]
57
64
  # any char
58
- when text = @ss.scan(/./)
65
+ when text = dedup_scan(/./)
59
66
  [:LITERAL, text]
60
67
  end
61
68
  end
@@ -40,7 +40,7 @@ module ActionDispatch
40
40
  @parameters.each do |index|
41
41
  param = parts[index]
42
42
  value = hash[param.name]
43
- return "".freeze unless value
43
+ return "" unless value
44
44
  parts[index] = param.escape value
45
45
  end
46
46
 
@@ -24,10 +24,8 @@ module ActionDispatch
24
24
  def call(env)
25
25
  error = nil
26
26
  result = run_callbacks :call do
27
- begin
28
- @app.call(env)
29
- rescue => error
30
- end
27
+ @app.call(env)
28
+ rescue => error
31
29
  end
32
30
  raise error if error
33
31
  result
@@ -9,7 +9,7 @@ require "rack/utils"
9
9
  module ActionDispatch
10
10
  class Request
11
11
  def cookie_jar
12
- fetch_header("action_dispatch.cookies".freeze) do
12
+ fetch_header("action_dispatch.cookies") do
13
13
  self.cookie_jar = Cookies::CookieJar.build(self, cookies)
14
14
  end
15
15
  end
@@ -22,11 +22,11 @@ module ActionDispatch
22
22
  }
23
23
 
24
24
  def have_cookie_jar?
25
- has_header? "action_dispatch.cookies".freeze
25
+ has_header? "action_dispatch.cookies"
26
26
  end
27
27
 
28
28
  def cookie_jar=(jar)
29
- set_header "action_dispatch.cookies".freeze, jar
29
+ set_header "action_dispatch.cookies", jar
30
30
  end
31
31
 
32
32
  def key_generator
@@ -61,10 +61,6 @@ module ActionDispatch
61
61
  get_header Cookies::SIGNED_COOKIE_DIGEST
62
62
  end
63
63
 
64
- def secret_token
65
- get_header Cookies::SECRET_TOKEN
66
- end
67
-
68
64
  def secret_key_base
69
65
  get_header Cookies::SECRET_KEY_BASE
70
66
  end
@@ -81,6 +77,10 @@ module ActionDispatch
81
77
  get_header Cookies::COOKIES_ROTATIONS
82
78
  end
83
79
 
80
+ def use_cookies_with_metadata
81
+ get_header Cookies::USE_COOKIES_WITH_METADATA
82
+ end
83
+
84
84
  # :startdoc:
85
85
  end
86
86
 
@@ -168,20 +168,20 @@ module ActionDispatch
168
168
  # * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
169
169
  # only HTTP. Defaults to +false+.
170
170
  class Cookies
171
- HTTP_HEADER = "Set-Cookie".freeze
172
- GENERATOR_KEY = "action_dispatch.key_generator".freeze
173
- SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt".freeze
174
- ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze
175
- ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze
176
- AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt".freeze
177
- USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption".freeze
178
- ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher".freeze
179
- SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest".freeze
180
- SECRET_TOKEN = "action_dispatch.secret_token".freeze
181
- SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
182
- COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
183
- COOKIES_DIGEST = "action_dispatch.cookies_digest".freeze
184
- COOKIES_ROTATIONS = "action_dispatch.cookies_rotations".freeze
171
+ HTTP_HEADER = "Set-Cookie"
172
+ GENERATOR_KEY = "action_dispatch.key_generator"
173
+ SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt"
174
+ ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt"
175
+ ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt"
176
+ AUTHENTICATED_ENCRYPTED_COOKIE_SALT = "action_dispatch.authenticated_encrypted_cookie_salt"
177
+ USE_AUTHENTICATED_COOKIE_ENCRYPTION = "action_dispatch.use_authenticated_cookie_encryption"
178
+ ENCRYPTED_COOKIE_CIPHER = "action_dispatch.encrypted_cookie_cipher"
179
+ SIGNED_COOKIE_DIGEST = "action_dispatch.signed_cookie_digest"
180
+ SECRET_KEY_BASE = "action_dispatch.secret_key_base"
181
+ COOKIES_SERIALIZER = "action_dispatch.cookies_serializer"
182
+ COOKIES_DIGEST = "action_dispatch.cookies_digest"
183
+ COOKIES_ROTATIONS = "action_dispatch.cookies_rotations"
184
+ USE_COOKIES_WITH_METADATA = "action_dispatch.use_cookies_with_metadata"
185
185
 
186
186
  # Cookies can typically store 4096 bytes.
187
187
  MAX_COOKIE_SIZE = 4096
@@ -210,9 +210,6 @@ module ActionDispatch
210
210
  # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
211
211
  # cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
212
212
  #
213
- # If +secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
214
- # legacy cookies signed with the old key generator will be transparently upgraded.
215
- #
216
213
  # This jar requires that you set a suitable secret for the verification on your app's +secret_key_base+.
217
214
  #
218
215
  # Example:
@@ -228,9 +225,6 @@ module ActionDispatch
228
225
  # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
229
226
  # If the cookie was tampered with by the user (or a 3rd party), +nil+ will be returned.
230
227
  #
231
- # If +secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
232
- # legacy cookies signed with the old key generator will be transparently upgraded.
233
- #
234
228
  # If +config.action_dispatch.encrypted_cookie_salt+ and +config.action_dispatch.encrypted_signed_cookie_salt+
235
229
  # are both set, legacy cookies encrypted with HMAC AES-256-CBC will be transparently upgraded.
236
230
  #
@@ -259,10 +253,6 @@ module ActionDispatch
259
253
 
260
254
  private
261
255
 
262
- def upgrade_legacy_signed_cookies?
263
- request.secret_token.present? && request.secret_key_base.present?
264
- end
265
-
266
256
  def upgrade_legacy_hmac_aes_cbc_cookies?
267
257
  request.secret_key_base.present? &&
268
258
  request.encrypted_signed_cookie_salt.present? &&
@@ -470,7 +460,7 @@ module ActionDispatch
470
460
 
471
461
  def [](name)
472
462
  if data = @parent_jar[name.to_s]
473
- parse name, data
463
+ parse(name, data, purpose: "cookie.#{name}") || parse(name, data)
474
464
  end
475
465
  end
476
466
 
@@ -481,7 +471,7 @@ module ActionDispatch
481
471
  options = { value: options }
482
472
  end
483
473
 
484
- commit(options)
474
+ commit(name, options)
485
475
  @parent_jar[name] = options
486
476
  end
487
477
 
@@ -490,24 +480,31 @@ module ActionDispatch
490
480
 
491
481
  private
492
482
  def expiry_options(options)
493
- if request.use_authenticated_cookie_encryption
494
- if options[:expires].respond_to?(:from_now)
495
- { expires_in: options[:expires] }
496
- else
497
- { expires_at: options[:expires] }
498
- end
483
+ if options[:expires].respond_to?(:from_now)
484
+ { expires_in: options[:expires] }
485
+ else
486
+ { expires_at: options[:expires] }
487
+ end
488
+ end
489
+
490
+ def cookie_metadata(name, options)
491
+ if request.use_cookies_with_metadata
492
+ metadata = expiry_options(options)
493
+ metadata[:purpose] = "cookie.#{name}"
494
+
495
+ metadata
499
496
  else
500
497
  {}
501
498
  end
502
499
  end
503
500
 
504
- def parse(name, data); data; end
505
- def commit(options); end
501
+ def parse(name, data, purpose: nil); data; end
502
+ def commit(name, options); end
506
503
  end
507
504
 
508
505
  class PermanentCookieJar < AbstractCookieJar # :nodoc:
509
506
  private
510
- def commit(options)
507
+ def commit(name, options)
511
508
  options[:expires] = 20.years.from_now
512
509
  end
513
510
  end
@@ -523,7 +520,7 @@ module ActionDispatch
523
520
  end
524
521
 
525
522
  module SerializedCookieJars # :nodoc:
526
- MARSHAL_SIGNATURE = "\x04\x08".freeze
523
+ MARSHAL_SIGNATURE = "\x04\x08"
527
524
  SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
528
525
 
529
526
  protected
@@ -580,21 +577,17 @@ module ActionDispatch
580
577
  request.cookies_rotations.signed.each do |*secrets, **options|
581
578
  @verifier.rotate(*secrets, serializer: SERIALIZER, **options)
582
579
  end
583
-
584
- if upgrade_legacy_signed_cookies?
585
- @verifier.rotate request.secret_token, serializer: SERIALIZER
586
- end
587
580
  end
588
581
 
589
582
  private
590
- def parse(name, signed_message)
583
+ def parse(name, signed_message, purpose: nil)
591
584
  deserialize(name) do |rotate|
592
- @verifier.verified(signed_message, on_rotation: rotate)
585
+ @verifier.verified(signed_message, on_rotation: rotate, purpose: purpose)
593
586
  end
594
587
  end
595
588
 
596
- def commit(options)
597
- options[:value] = @verifier.generate(serialize(options[:value]), expiry_options(options))
589
+ def commit(name, options)
590
+ options[:value] = @verifier.generate(serialize(options[:value]), cookie_metadata(name, options))
598
591
 
599
592
  raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
600
593
  end
@@ -628,36 +621,22 @@ module ActionDispatch
628
621
 
629
622
  @encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER)
630
623
  end
631
-
632
- if upgrade_legacy_signed_cookies?
633
- @legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, digest: digest, serializer: SERIALIZER)
634
- end
635
624
  end
636
625
 
637
626
  private
638
- def parse(name, encrypted_message)
627
+ def parse(name, encrypted_message, purpose: nil)
639
628
  deserialize(name) do |rotate|
640
- @encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate)
629
+ @encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate, purpose: purpose)
641
630
  end
642
631
  rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
643
- parse_legacy_signed_message(name, encrypted_message)
632
+ nil
644
633
  end
645
634
 
646
- def commit(options)
647
- options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]), expiry_options(options))
635
+ def commit(name, options)
636
+ options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]), cookie_metadata(name, options))
648
637
 
649
638
  raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
650
639
  end
651
-
652
- def parse_legacy_signed_message(name, legacy_signed_message)
653
- if defined?(@legacy_verifier)
654
- deserialize(name) do |rotate|
655
- rotate.call
656
-
657
- @legacy_verifier.verified(legacy_signed_message)
658
- end
659
- end
660
- end
661
640
  end
662
641
 
663
642
  def initialize(app)
@@ -3,57 +3,26 @@
3
3
  require "action_dispatch/http/request"
4
4
  require "action_dispatch/middleware/exception_wrapper"
5
5
  require "action_dispatch/routing/inspector"
6
+
6
7
  require "action_view"
7
8
  require "action_view/base"
8
9
 
9
- require "pp"
10
-
11
10
  module ActionDispatch
12
11
  # This middleware is responsible for logging exceptions and
13
12
  # showing a debugging page in case the request is local.
14
13
  class DebugExceptions
15
- RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
16
-
17
- class DebugView < ActionView::Base
18
- def debug_params(params)
19
- clean_params = params.clone
20
- clean_params.delete("action")
21
- clean_params.delete("controller")
22
-
23
- if clean_params.empty?
24
- "None"
25
- else
26
- PP.pp(clean_params, "".dup, 200)
27
- end
28
- end
29
-
30
- def debug_headers(headers)
31
- if headers.present?
32
- headers.inspect.gsub(",", ",\n")
33
- else
34
- "None"
35
- end
36
- end
37
-
38
- def debug_hash(object)
39
- object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
40
- end
41
-
42
- def render(*)
43
- logger = ActionView::Base.logger
14
+ cattr_reader :interceptors, instance_accessor: false, default: []
44
15
 
45
- if logger && logger.respond_to?(:silence)
46
- logger.silence { super }
47
- else
48
- super
49
- end
50
- end
16
+ def self.register_interceptor(object = nil, &block)
17
+ interceptor = object || block
18
+ interceptors << interceptor
51
19
  end
52
20
 
53
- def initialize(app, routes_app = nil, response_format = :default)
21
+ def initialize(app, routes_app = nil, response_format = :default, interceptors = self.class.interceptors)
54
22
  @app = app
55
23
  @routes_app = routes_app
56
24
  @response_format = response_format
25
+ @interceptors = interceptors
57
26
  end
58
27
 
59
28
  def call(env)
@@ -67,12 +36,24 @@ module ActionDispatch
67
36
 
68
37
  response
69
38
  rescue Exception => exception
39
+ invoke_interceptors(request, exception)
70
40
  raise exception unless request.show_exceptions?
71
41
  render_exception(request, exception)
72
42
  end
73
43
 
74
44
  private
75
45
 
46
+ def invoke_interceptors(request, exception)
47
+ backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
48
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
49
+
50
+ @interceptors.each do |interceptor|
51
+ interceptor.call(request, exception)
52
+ rescue Exception
53
+ log_error(request, wrapper)
54
+ end
55
+ end
56
+
76
57
  def render_exception(request, exception)
77
58
  backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
78
59
  wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
@@ -130,23 +111,13 @@ module ActionDispatch
130
111
  end
131
112
 
132
113
  def create_template(request, wrapper)
133
- traces = wrapper.traces
134
-
135
- trace_to_show = "Application Trace"
136
- if traces[trace_to_show].empty? && wrapper.rescue_template != "routing_error"
137
- trace_to_show = "Full Trace"
138
- end
139
-
140
- if source_to_show = traces[trace_to_show].first
141
- source_to_show_id = source_to_show[:id]
142
- end
143
-
144
- DebugView.new([RESCUES_TEMPLATE_PATH],
114
+ DebugView.new(
145
115
  request: request,
116
+ exception_wrapper: wrapper,
146
117
  exception: wrapper.exception,
147
- traces: traces,
148
- show_source_idx: source_to_show_id,
149
- trace_to_show: trace_to_show,
118
+ traces: wrapper.traces,
119
+ show_source_idx: wrapper.source_to_show_id,
120
+ trace_to_show: wrapper.trace_to_show,
150
121
  routes_inspector: routes_inspector(wrapper.exception),
151
122
  source_extracts: wrapper.source_extracts,
152
123
  line_number: wrapper.line_number,
@@ -168,11 +139,14 @@ module ActionDispatch
168
139
  trace = wrapper.framework_trace if trace.empty?
169
140
 
170
141
  ActiveSupport::Deprecation.silence do
171
- logger.fatal " "
172
- logger.fatal "#{exception.class} (#{exception.message}):"
173
- log_array logger, exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
174
- logger.fatal " "
175
- log_array logger, trace
142
+ message = []
143
+ message << " "
144
+ message << "#{exception.class} (#{exception.message}):"
145
+ message.concat(exception.annoted_source_code) if exception.respond_to?(:annoted_source_code)
146
+ message << " "
147
+ message.concat(trace)
148
+
149
+ log_array(logger, message)
176
150
  end
177
151
  end
178
152