actionpack 5.2.3 → 6.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +191 -292
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -2
- data/lib/abstract_controller/base.rb +4 -2
- data/lib/abstract_controller/caching/fragments.rb +6 -22
- data/lib/abstract_controller/callbacks.rb +12 -0
- data/lib/abstract_controller/collector.rb +1 -1
- data/lib/abstract_controller/helpers.rb +2 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -1
- data/lib/abstract_controller/translation.rb +1 -0
- data/lib/action_controller.rb +5 -1
- data/lib/action_controller/api.rb +2 -1
- data/lib/action_controller/base.rb +2 -7
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +8 -5
- data/lib/action_controller/metal.rb +3 -3
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +9 -3
- data/lib/action_controller/metal/data_streaming.rb +5 -6
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +23 -2
- data/lib/action_controller/metal/flash.rb +5 -5
- data/lib/action_controller/metal/force_ssl.rb +15 -56
- data/lib/action_controller/metal/head.rb +1 -1
- data/lib/action_controller/metal/helpers.rb +3 -4
- data/lib/action_controller/metal/http_authentication.rb +20 -21
- data/lib/action_controller/metal/implicit_render.rb +4 -14
- data/lib/action_controller/metal/instrumentation.rb +3 -5
- data/lib/action_controller/metal/live.rb +29 -27
- data/lib/action_controller/metal/mime_responds.rb +13 -2
- data/lib/action_controller/metal/params_wrapper.rb +18 -14
- data/lib/action_controller/metal/redirecting.rb +5 -5
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +2 -2
- data/lib/action_controller/metal/request_forgery_protection.rb +23 -12
- data/lib/action_controller/metal/strong_parameters.rb +63 -44
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +16 -3
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +4 -6
- data/lib/action_dispatch.rb +4 -2
- data/lib/action_dispatch/http/cache.rb +14 -10
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +28 -16
- data/lib/action_dispatch/http/filter_parameters.rb +8 -6
- data/lib/action_dispatch/http/filter_redirect.rb +1 -1
- data/lib/action_dispatch/http/headers.rb +1 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +7 -5
- data/lib/action_dispatch/http/mime_type.rb +14 -6
- data/lib/action_dispatch/http/parameter_filter.rb +5 -79
- data/lib/action_dispatch/http/parameters.rb +13 -3
- data/lib/action_dispatch/http/request.rb +10 -13
- data/lib/action_dispatch/http/response.rb +39 -18
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +81 -81
- data/lib/action_dispatch/journey/formatter.rb +2 -2
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -2
- data/lib/action_dispatch/journey/nodes/node.rb +9 -8
- data/lib/action_dispatch/journey/path/pattern.rb +8 -3
- data/lib/action_dispatch/journey/route.rb +5 -4
- data/lib/action_dispatch/journey/router.rb +0 -3
- data/lib/action_dispatch/journey/router/utils.rb +10 -10
- data/lib/action_dispatch/journey/routes.rb +0 -1
- data/lib/action_dispatch/journey/scanner.rb +11 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -1
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +39 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +52 -74
- data/lib/action_dispatch/middleware/debug_exceptions.rb +39 -59
- data/lib/action_dispatch/middleware/debug_locks.rb +5 -5
- data/lib/action_dispatch/middleware/debug_view.rb +68 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +49 -15
- data/lib/action_dispatch/middleware/flash.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +103 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -2
- data/lib/action_dispatch/middleware/remote_ip.rb +9 -11
- data/lib/action_dispatch/middleware/request_id.rb +2 -2
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -6
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -1
- data/lib/action_dispatch/middleware/ssl.rb +8 -8
- data/lib/action_dispatch/middleware/stack.rb +34 -2
- data/lib/action_dispatch/middleware/static.rb +5 -6
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +3 -1
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +3 -0
- data/lib/action_dispatch/railtie.rb +7 -2
- data/lib/action_dispatch/request/session.rb +8 -0
- data/lib/action_dispatch/routing.rb +21 -20
- data/lib/action_dispatch/routing/inspector.rb +99 -50
- data/lib/action_dispatch/routing/mapper.rb +61 -39
- data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -4
- data/lib/action_dispatch/routing/route_set.rb +24 -27
- data/lib/action_dispatch/routing/url_for.rb +1 -0
- data/lib/action_dispatch/system_test_case.rb +43 -5
- data/lib/action_dispatch/system_testing/browser.rb +38 -7
- data/lib/action_dispatch/system_testing/driver.rb +10 -1
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +6 -5
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +7 -6
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +2 -3
- data/lib/action_dispatch/testing/assertions/routing.rb +15 -3
- data/lib/action_dispatch/testing/integration.rb +12 -5
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +2 -2
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +29 -16
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -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}"
|
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
|
|
@@ -67,7 +67,7 @@ module ActionDispatch
|
|
67
67
|
parameterized_parts = recall.merge(options)
|
68
68
|
|
69
69
|
keys_to_keep = route.parts.reverse_each.drop_while { |part|
|
70
|
-
!options.key?(part) || (options[part] || recall[part]).nil?
|
70
|
+
!(options.key?(part) || route.scope_options.key?(part)) || (options[part] || recall[part]).nil?
|
71
71
|
} | route.required_parts
|
72
72
|
|
73
73
|
parameterized_parts.delete_if do |bad_key, _|
|
@@ -32,7 +32,7 @@ module ActionDispatch
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def name
|
35
|
-
left.tr
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
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,8 @@ module ActionDispatch
|
|
119
119
|
|
120
120
|
class UnanchoredRegexp < AnchoredRegexp # :nodoc:
|
121
121
|
def accept(node)
|
122
|
-
|
122
|
+
path = visit node
|
123
|
+
path == "/" ? %r{\A/} : %r{\A#{path}(?:\b|\Z|/)}
|
123
124
|
end
|
124
125
|
end
|
125
126
|
|
@@ -136,6 +137,10 @@ module ActionDispatch
|
|
136
137
|
Array.new(length - 1) { |i| self[i + 1] }
|
137
138
|
end
|
138
139
|
|
140
|
+
def named_captures
|
141
|
+
@names.zip(captures).to_h
|
142
|
+
end
|
143
|
+
|
139
144
|
def [](x)
|
140
145
|
idx = @offsets[x - 1] + x
|
141
146
|
@match[idx]
|
@@ -183,7 +188,7 @@ module ActionDispatch
|
|
183
188
|
node = node.to_sym
|
184
189
|
|
185
190
|
if @requirements.key?(node)
|
186
|
-
re = /#{@requirements[node]}|/
|
191
|
+
re = /#{Regexp.union(@requirements[node])}|/
|
187
192
|
@offsets.push((re.match("").length - 1) + @offsets.last)
|
188
193
|
else
|
189
194
|
@offsets << @offsets.last
|
@@ -4,9 +4,9 @@ module ActionDispatch
|
|
4
4
|
# :stopdoc:
|
5
5
|
module Journey
|
6
6
|
class Route
|
7
|
-
attr_reader :app, :path, :defaults, :name, :precedence
|
7
|
+
attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
|
8
|
+
:internal, :scope_options
|
8
9
|
|
9
|
-
attr_reader :constraints, :internal
|
10
10
|
alias :conditions :constraints
|
11
11
|
|
12
12
|
module VerbMatchers
|
@@ -51,13 +51,13 @@ module ActionDispatch
|
|
51
51
|
|
52
52
|
def self.build(name, app, path, constraints, required_defaults, defaults)
|
53
53
|
request_method_match = verb_matcher(constraints.delete(:request_method))
|
54
|
-
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0
|
54
|
+
new name, app, path, constraints, required_defaults, defaults, request_method_match, 0, {}
|
55
55
|
end
|
56
56
|
|
57
57
|
##
|
58
58
|
# +path+ is a path constraint.
|
59
59
|
# +constraints+ is a hash of constraints to be applied to this route.
|
60
|
-
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, internal = false)
|
60
|
+
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, scope_options, internal = false)
|
61
61
|
@name = name
|
62
62
|
@app = app
|
63
63
|
@path = path
|
@@ -72,6 +72,7 @@ module ActionDispatch
|
|
72
72
|
@decorated_ast = nil
|
73
73
|
@precedence = precedence
|
74
74
|
@path_formatter = @path.build_formatter
|
75
|
+
@scope_options = scope_options
|
75
76
|
@internal = internal
|
76
77
|
end
|
77
78
|
|
@@ -17,11 +17,11 @@ module ActionDispatch
|
|
17
17
|
def self.normalize_path(path)
|
18
18
|
path ||= ""
|
19
19
|
encoding = path.encoding
|
20
|
-
path = "/#{path}"
|
21
|
-
path.squeeze!("/"
|
22
|
-
path.sub!(%r{/+\Z}, ""
|
20
|
+
path = +"/#{path}"
|
21
|
+
path.squeeze!("/")
|
22
|
+
path.sub!(%r{/+\Z}, "")
|
23
23
|
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
|
24
|
-
path = "/"
|
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"
|
32
|
+
ENCODE = "%%%02X"
|
33
33
|
US_ASCII = Encoding::US_ASCII
|
34
34
|
UTF_8 = Encoding::UTF_8
|
35
|
-
EMPTY = "".
|
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"
|
39
|
-
DIGIT = "0-9"
|
40
|
-
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~"
|
41
|
-
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;="
|
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 =
|
57
|
+
when text = dedup_scan(/:\w+/)
|
51
58
|
[:SYMBOL, text]
|
52
|
-
when text =
|
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 =
|
65
|
+
when text = dedup_scan(/./)
|
59
66
|
[:LITERAL, text]
|
60
67
|
end
|
61
68
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
require "action_dispatch/http/request"
|
5
|
+
require "active_support/actionable_error"
|
6
|
+
|
7
|
+
module ActionDispatch
|
8
|
+
class ActionableExceptions # :nodoc:
|
9
|
+
cattr_accessor :endpoint, default: "/rails/actions"
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
request = ActionDispatch::Request.new(env)
|
17
|
+
return @app.call(env) unless actionable_request?(request)
|
18
|
+
|
19
|
+
ActiveSupport::ActionableError.dispatch(request.params[:error].to_s.safe_constantize, request.params[:action])
|
20
|
+
|
21
|
+
redirect_to request.params[:location]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def actionable_request?(request)
|
26
|
+
request.show_exceptions? && request.post? && request.path == endpoint
|
27
|
+
end
|
28
|
+
|
29
|
+
def redirect_to(location)
|
30
|
+
body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
|
31
|
+
|
32
|
+
[302, {
|
33
|
+
"Content-Type" => "text/html; charset=#{Response.default_charset}",
|
34
|
+
"Content-Length" => body.bytesize.to_s,
|
35
|
+
"Location" => location,
|
36
|
+
}, [body]]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -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"
|
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"
|
25
|
+
has_header? "action_dispatch.cookies"
|
26
26
|
end
|
27
27
|
|
28
28
|
def cookie_jar=(jar)
|
29
|
-
set_header "action_dispatch.cookies"
|
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"
|
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
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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? &&
|
@@ -348,7 +338,7 @@ module ActionDispatch
|
|
348
338
|
|
349
339
|
def update_cookies_from_jar
|
350
340
|
request_jar = @request.cookie_jar.instance_variable_get(:@cookies)
|
351
|
-
set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) }
|
341
|
+
set_cookies = request_jar.reject { |k, _| @delete_cookies.key?(k) || @set_cookies.key?(k) }
|
352
342
|
|
353
343
|
@cookies.update set_cookies if set_cookies
|
354
344
|
end
|
@@ -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,26 @@ module ActionDispatch
|
|
490
480
|
|
491
481
|
private
|
492
482
|
def expiry_options(options)
|
493
|
-
if
|
494
|
-
|
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] }
|
499
485
|
else
|
500
|
-
{}
|
486
|
+
{ expires_at: options[:expires] }
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def cookie_metadata(name, options)
|
491
|
+
expiry_options(options).tap do |metadata|
|
492
|
+
metadata[:purpose] = "cookie.#{name}" if request.use_cookies_with_metadata
|
501
493
|
end
|
502
494
|
end
|
503
495
|
|
504
|
-
def parse(name, data); data; end
|
505
|
-
def commit(options); end
|
496
|
+
def parse(name, data, purpose: nil); data; end
|
497
|
+
def commit(name, options); end
|
506
498
|
end
|
507
499
|
|
508
500
|
class PermanentCookieJar < AbstractCookieJar # :nodoc:
|
509
501
|
private
|
510
|
-
def commit(options)
|
502
|
+
def commit(name, options)
|
511
503
|
options[:expires] = 20.years.from_now
|
512
504
|
end
|
513
505
|
end
|
@@ -523,7 +515,7 @@ module ActionDispatch
|
|
523
515
|
end
|
524
516
|
|
525
517
|
module SerializedCookieJars # :nodoc:
|
526
|
-
MARSHAL_SIGNATURE = "\x04\x08"
|
518
|
+
MARSHAL_SIGNATURE = "\x04\x08"
|
527
519
|
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
|
528
520
|
|
529
521
|
protected
|
@@ -542,9 +534,13 @@ module ActionDispatch
|
|
542
534
|
if value
|
543
535
|
case
|
544
536
|
when needs_migration?(value)
|
545
|
-
|
537
|
+
Marshal.load(value).tap do |v|
|
538
|
+
self[name] = { value: v }
|
539
|
+
end
|
546
540
|
when rotate
|
547
|
-
|
541
|
+
serializer.load(value).tap do |v|
|
542
|
+
self[name] = { value: v }
|
543
|
+
end
|
548
544
|
else
|
549
545
|
serializer.load(value)
|
550
546
|
end
|
@@ -580,21 +576,17 @@ module ActionDispatch
|
|
580
576
|
request.cookies_rotations.signed.each do |*secrets, **options|
|
581
577
|
@verifier.rotate(*secrets, serializer: SERIALIZER, **options)
|
582
578
|
end
|
583
|
-
|
584
|
-
if upgrade_legacy_signed_cookies?
|
585
|
-
@verifier.rotate request.secret_token, serializer: SERIALIZER
|
586
|
-
end
|
587
579
|
end
|
588
580
|
|
589
581
|
private
|
590
|
-
def parse(name, signed_message)
|
582
|
+
def parse(name, signed_message, purpose: nil)
|
591
583
|
deserialize(name) do |rotate|
|
592
|
-
@verifier.verified(signed_message, on_rotation: rotate)
|
584
|
+
@verifier.verified(signed_message, on_rotation: rotate, purpose: purpose)
|
593
585
|
end
|
594
586
|
end
|
595
587
|
|
596
|
-
def commit(options)
|
597
|
-
options[:value] = @verifier.generate(serialize(options[:value]),
|
588
|
+
def commit(name, options)
|
589
|
+
options[:value] = @verifier.generate(serialize(options[:value]), cookie_metadata(name, options))
|
598
590
|
|
599
591
|
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
|
600
592
|
end
|
@@ -628,36 +620,22 @@ module ActionDispatch
|
|
628
620
|
|
629
621
|
@encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER)
|
630
622
|
end
|
631
|
-
|
632
|
-
if upgrade_legacy_signed_cookies?
|
633
|
-
@legacy_verifier = ActiveSupport::MessageVerifier.new(request.secret_token, digest: digest, serializer: SERIALIZER)
|
634
|
-
end
|
635
623
|
end
|
636
624
|
|
637
625
|
private
|
638
|
-
def parse(name, encrypted_message)
|
626
|
+
def parse(name, encrypted_message, purpose: nil)
|
639
627
|
deserialize(name) do |rotate|
|
640
|
-
@encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate)
|
628
|
+
@encryptor.decrypt_and_verify(encrypted_message, on_rotation: rotate, purpose: purpose)
|
641
629
|
end
|
642
630
|
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
643
|
-
|
631
|
+
nil
|
644
632
|
end
|
645
633
|
|
646
|
-
def commit(options)
|
647
|
-
options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]),
|
634
|
+
def commit(name, options)
|
635
|
+
options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value]), cookie_metadata(name, options))
|
648
636
|
|
649
637
|
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
|
650
638
|
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
639
|
end
|
662
640
|
|
663
641
|
def initialize(app)
|