actionpack 7.0.4 → 7.1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +495 -257
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/abstract_controller/base.rb +20 -11
- data/lib/abstract_controller/caching/fragments.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +31 -6
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/helpers.rb +75 -28
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +11 -6
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +6 -4
- data/lib/action_controller/base.rb +3 -17
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +16 -4
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +121 -123
- data/lib/action_controller/metal/content_security_policy.rb +5 -5
- data/lib/action_controller/metal/data_streaming.rb +20 -18
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +8 -0
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +15 -9
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +8 -1
- data/lib/action_controller/metal/live.rb +25 -1
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +4 -2
- data/lib/action_controller/metal/permissions_policy.rb +2 -2
- data/lib/action_controller/metal/redirecting.rb +29 -8
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +114 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +144 -53
- data/lib/action_controller/metal/rescue.rb +6 -3
- data/lib/action_controller/metal/streaming.rb +71 -31
- data/lib/action_controller/metal/strong_parameters.rb +200 -103
- data/lib/action_controller/metal/url_for.rb +9 -4
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +24 -10
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +18 -8
- data/lib/action_controller.rb +13 -3
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +9 -11
- data/lib/action_dispatch/http/content_security_policy.rb +35 -13
- data/lib/action_dispatch/http/filter_parameters.rb +23 -32
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
- data/lib/action_dispatch/http/mime_type.rb +37 -11
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +1 -1
- data/lib/action_dispatch/http/permissions_policy.rb +38 -23
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +85 -32
- data/lib/action_dispatch/http/response.rb +80 -63
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +14 -14
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +9 -8
- data/lib/action_dispatch/journey/routes.rb +2 -2
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +108 -117
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
- data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
- data/lib/action_dispatch/middleware/executor.rb +7 -1
- data/lib/action_dispatch/middleware/flash.rb +7 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +21 -20
- data/lib/action_dispatch/middleware/request_id.rb +4 -2
- data/lib/action_dispatch/middleware/server_timing.rb +4 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +39 -22
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +7 -2
- data/lib/action_dispatch/middleware/static.rb +14 -10
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -41
- data/lib/action_dispatch/railtie.rb +13 -4
- data/lib/action_dispatch/request/session.rb +16 -6
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +54 -6
- data/lib/action_dispatch/routing/mapper.rb +97 -26
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +15 -6
- data/lib/action_dispatch/routing/route_set.rb +53 -23
- data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
- data/lib/action_dispatch/routing/url_for.rb +26 -22
- data/lib/action_dispatch/routing.rb +7 -7
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +25 -19
- data/lib/action_dispatch/system_testing/driver.rb +15 -23
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
- data/lib/action_dispatch/testing/assertions.rb +3 -1
- data/lib/action_dispatch/testing/integration.rb +27 -17
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +4 -3
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -9
- data/lib/action_dispatch.rb +41 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +68 -32
@@ -57,6 +57,9 @@ module ActionDispatch
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def generate(name, options, path_parameters)
|
60
|
+
original_options = options.dup
|
61
|
+
path_params = options.delete(:path_params) || {}
|
62
|
+
options = path_params.merge(options)
|
60
63
|
constraints = path_parameters.merge(options)
|
61
64
|
missing_keys = nil
|
62
65
|
|
@@ -70,8 +73,11 @@ module ActionDispatch
|
|
70
73
|
|
71
74
|
missing_keys = missing_keys(route, parameterized_parts)
|
72
75
|
next if missing_keys && !missing_keys.empty?
|
73
|
-
params = options.
|
74
|
-
|
76
|
+
params = options.delete_if do |key, _|
|
77
|
+
# top-level params' normal behavior of generating query_params
|
78
|
+
# should be preserved even if the same key is also a bind_param
|
79
|
+
parameterized_parts.key?(key) || route.defaults.key?(key) ||
|
80
|
+
(path_params.key?(key) && !original_options.key?(key))
|
75
81
|
end
|
76
82
|
|
77
83
|
defaults = route.defaults
|
@@ -183,22 +183,22 @@ module ActionDispatch
|
|
183
183
|
end
|
184
184
|
|
185
185
|
def offsets
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
186
|
+
@offsets ||= begin
|
187
|
+
offsets = [0]
|
188
|
+
|
189
|
+
spec.find_all(&:symbol?).each do |node|
|
190
|
+
node = node.to_sym
|
191
|
+
|
192
|
+
if @requirements.key?(node)
|
193
|
+
re = /#{Regexp.union(@requirements[node])}|/
|
194
|
+
offsets.push((re.match("").length - 1) + offsets.last)
|
195
|
+
else
|
196
|
+
offsets << offsets.last
|
197
|
+
end
|
198
198
|
end
|
199
|
-
end
|
200
199
|
|
201
|
-
|
200
|
+
offsets
|
201
|
+
end
|
202
202
|
end
|
203
203
|
end
|
204
204
|
end
|
@@ -5,7 +5,7 @@ module ActionDispatch
|
|
5
5
|
module Journey
|
6
6
|
class Route
|
7
7
|
attr_reader :app, :path, :defaults, :name, :precedence, :constraints,
|
8
|
-
:internal, :scope_options, :ast
|
8
|
+
:internal, :scope_options, :ast, :source_location
|
9
9
|
|
10
10
|
alias :conditions :constraints
|
11
11
|
|
@@ -53,7 +53,7 @@ module ActionDispatch
|
|
53
53
|
##
|
54
54
|
# +path+ is a path constraint.
|
55
55
|
# +constraints+ is a hash of constraints to be applied to this route.
|
56
|
-
def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false)
|
56
|
+
def initialize(name:, app: nil, path:, constraints: {}, required_defaults: [], defaults: {}, request_method_match: nil, precedence: 0, scope_options: {}, internal: false, source_location: nil)
|
57
57
|
@name = name
|
58
58
|
@app = app
|
59
59
|
@path = path
|
@@ -69,6 +69,7 @@ module ActionDispatch
|
|
69
69
|
@path_formatter = @path.build_formatter
|
70
70
|
@scope_options = scope_options
|
71
71
|
@internal = internal
|
72
|
+
@source_location = source_location
|
72
73
|
|
73
74
|
@ast = @path.ast.root
|
74
75
|
@path.ast.route = self
|
@@ -29,7 +29,7 @@ module ActionDispatch
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def serve(req)
|
32
|
-
find_routes(req)
|
32
|
+
find_routes(req) do |match, parameters, route|
|
33
33
|
set_params = req.path_parameters
|
34
34
|
path_info = req.path_info
|
35
35
|
script_name = req.script_name
|
@@ -46,24 +46,25 @@ module ActionDispatch
|
|
46
46
|
}
|
47
47
|
|
48
48
|
req.path_parameters = tmp_params
|
49
|
+
req.route_uri_pattern = route.path.spec.to_s
|
49
50
|
|
50
|
-
|
51
|
+
_, headers, _ = response = route.app.serve(req)
|
51
52
|
|
52
|
-
if "pass" == headers[
|
53
|
+
if "pass" == headers[Constants::X_CASCADE]
|
53
54
|
req.script_name = script_name
|
54
55
|
req.path_info = path_info
|
55
56
|
req.path_parameters = set_params
|
56
57
|
next
|
57
58
|
end
|
58
59
|
|
59
|
-
return
|
60
|
+
return response
|
60
61
|
end
|
61
62
|
|
62
|
-
[404, {
|
63
|
+
[404, { Constants::X_CASCADE => "pass" }, ["Not Found"]]
|
63
64
|
end
|
64
65
|
|
65
66
|
def recognize(rails_req)
|
66
|
-
find_routes(rails_req)
|
67
|
+
find_routes(rails_req) do |match, parameters, route|
|
67
68
|
unless route.path.anchored
|
68
69
|
rails_req.script_name = match.to_s
|
69
70
|
rails_req.path_info = match.post_match
|
@@ -120,14 +121,14 @@ module ActionDispatch
|
|
120
121
|
|
121
122
|
routes.sort_by!(&:precedence)
|
122
123
|
|
123
|
-
routes.
|
124
|
+
routes.each { |r|
|
124
125
|
match_data = r.path.match(path_info)
|
125
126
|
path_parameters = {}
|
126
127
|
match_data.names.each_with_index { |name, i|
|
127
128
|
val = match_data[i + 1]
|
128
129
|
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
129
130
|
}
|
130
|
-
[match_data, path_parameters, r]
|
131
|
+
yield [match_data, path_parameters, r]
|
131
132
|
}
|
132
133
|
end
|
133
134
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
|
+
def redirect(event)
|
6
|
+
payload = event.payload
|
7
|
+
|
8
|
+
info { "Redirected to #{payload[:location]}" }
|
9
|
+
|
10
|
+
info do
|
11
|
+
status = payload[:status]
|
12
|
+
|
13
|
+
message = +"Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
14
|
+
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
15
|
+
|
16
|
+
message
|
17
|
+
end
|
18
|
+
end
|
19
|
+
subscribe_log_level :redirect, :info
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActionDispatch::LogSubscriber.attach_to :action_dispatch
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "erb"
|
4
3
|
require "uri"
|
5
4
|
require "active_support/actionable_error"
|
6
5
|
|
@@ -30,15 +29,15 @@ module ActionDispatch
|
|
30
29
|
uri = URI.parse location
|
31
30
|
|
32
31
|
if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
|
33
|
-
body = "
|
32
|
+
body = ""
|
34
33
|
else
|
35
|
-
return [400, {
|
34
|
+
return [400, { Rack::CONTENT_TYPE => "text/plain; charset=utf-8" }, ["Invalid redirection URI"]]
|
36
35
|
end
|
37
36
|
|
38
37
|
[302, {
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
Rack::CONTENT_TYPE => "text/html; charset=#{Response.default_charset}",
|
39
|
+
Rack::CONTENT_LENGTH => body.bytesize.to_s,
|
40
|
+
ActionDispatch::Constants::LOCATION => location,
|
42
41
|
}, [body]]
|
43
42
|
end
|
44
43
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
# = Action Dispatch \AssumeSSL
|
5
|
+
#
|
6
|
+
# When proxying through a load balancer that terminates SSL, the forwarded request will appear
|
7
|
+
# as though it's HTTP instead of HTTPS to the application. This makes redirects and cookie
|
8
|
+
# security target HTTP instead of HTTPS. This middleware makes the server assume that the
|
9
|
+
# proxy already terminated SSL, and that the request really is HTTPS.
|
10
|
+
class AssumeSSL
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
env["HTTPS"] = "on"
|
17
|
+
env["HTTP_X_FORWARDED_PORT"] = "443"
|
18
|
+
env["HTTP_X_FORWARDED_PROTO"] = "https"
|
19
|
+
env["rack.url_scheme"] = "https"
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -70,7 +70,7 @@ module ActionDispatch
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def cookies_same_site_protection
|
73
|
-
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)
|
73
|
+
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)&.call(self)
|
74
74
|
end
|
75
75
|
|
76
76
|
def cookies_digest
|
@@ -92,10 +92,10 @@ module ActionDispatch
|
|
92
92
|
include RequestCookieMethods
|
93
93
|
end
|
94
94
|
|
95
|
-
# Read and write data to cookies through ActionController::
|
95
|
+
# Read and write data to cookies through ActionController::Cookies#cookies.
|
96
96
|
#
|
97
97
|
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
98
|
-
# When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie
|
98
|
+
# When writing cookie data, the data is sent out in the HTTP response header, +Set-Cookie+.
|
99
99
|
#
|
100
100
|
# Examples of writing:
|
101
101
|
#
|
@@ -160,13 +160,18 @@ module ActionDispatch
|
|
160
160
|
# to <tt>:all</tt>. To support multiple domains, provide an array, and
|
161
161
|
# the first domain matching <tt>request.host</tt> will be used. Make
|
162
162
|
# sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
|
163
|
-
# <tt>Array</tt> again when deleting cookies.
|
163
|
+
# <tt>Array</tt> again when deleting cookies. For more flexibility you
|
164
|
+
# can set the domain on a per-request basis by specifying <tt>:domain</tt>
|
165
|
+
# with a proc.
|
164
166
|
#
|
165
167
|
# domain: nil # Does not set cookie domain. (default)
|
166
168
|
# domain: :all # Allow the cookie for the top most level
|
167
169
|
# # domain and subdomains.
|
168
170
|
# domain: %w(.example.com .example.org) # Allow the cookie
|
169
171
|
# # for concrete domain names.
|
172
|
+
# domain: proc { Tenant.current.cookie_domain } # Set cookie domain dynamically
|
173
|
+
# domain: proc { |req| ".sub.#{req.host}" } # Set cookie domain dynamically based on request
|
174
|
+
#
|
170
175
|
#
|
171
176
|
# * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
|
172
177
|
# set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
|
@@ -178,7 +183,8 @@ module ActionDispatch
|
|
178
183
|
# only HTTP. Defaults to +false+.
|
179
184
|
# * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
|
180
185
|
# determines how this cookie should be restricted in cross-site contexts.
|
181
|
-
# Possible values are +:none+, +:lax+, and +:strict+. Defaults to
|
186
|
+
# Possible values are +nil+, +:none+, +:lax+, and +:strict+. Defaults to
|
187
|
+
# +:lax+.
|
182
188
|
class Cookies
|
183
189
|
HTTP_HEADER = "Set-Cookie"
|
184
190
|
GENERATOR_KEY = "action_dispatch.key_generator"
|
@@ -290,20 +296,6 @@ module ActionDispatch
|
|
290
296
|
class CookieJar # :nodoc:
|
291
297
|
include Enumerable, ChainedCookieJars
|
292
298
|
|
293
|
-
# This regular expression is used to split the levels of a domain.
|
294
|
-
# The top level domain can be any string without a period or
|
295
|
-
# **.**, ***.** style TLDs like co.uk or com.au
|
296
|
-
#
|
297
|
-
# www.example.co.uk gives:
|
298
|
-
# $& => example.co.uk
|
299
|
-
#
|
300
|
-
# example.com gives:
|
301
|
-
# $& => example.com
|
302
|
-
#
|
303
|
-
# lots.of.subdomains.example.local gives:
|
304
|
-
# $& => example.local
|
305
|
-
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
306
|
-
|
307
299
|
def self.build(req, cookies)
|
308
300
|
jar = new(req)
|
309
301
|
jar.update(cookies)
|
@@ -390,6 +382,8 @@ module ActionDispatch
|
|
390
382
|
# Removes the cookie on the client machine by setting the value to an empty string
|
391
383
|
# and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
|
392
384
|
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
|
385
|
+
#
|
386
|
+
# Returns the value of the cookie, or +nil+ if the cookie does not exist.
|
393
387
|
def delete(name, options = {})
|
394
388
|
return unless @cookies.has_key? name.to_s
|
395
389
|
|
@@ -415,9 +409,15 @@ module ActionDispatch
|
|
415
409
|
@cookies.each_key { |k| delete(k, options) }
|
416
410
|
end
|
417
411
|
|
418
|
-
def write(
|
419
|
-
|
420
|
-
|
412
|
+
def write(response)
|
413
|
+
@set_cookies.each do |name, value|
|
414
|
+
if write_cookie?(value)
|
415
|
+
response.set_cookie(name, value)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
@delete_cookies.each do |name, value|
|
420
|
+
response.delete_cookie(name, value)
|
421
421
|
end
|
422
422
|
end
|
423
423
|
|
@@ -428,19 +428,6 @@ module ActionDispatch
|
|
428
428
|
::Rack::Utils.escape(string)
|
429
429
|
end
|
430
430
|
|
431
|
-
def make_set_cookie_header(header)
|
432
|
-
header = @set_cookies.inject(header) { |m, (k, v)|
|
433
|
-
if write_cookie?(v)
|
434
|
-
::Rack::Utils.add_cookie_to_header(m, k, v)
|
435
|
-
else
|
436
|
-
m
|
437
|
-
end
|
438
|
-
}
|
439
|
-
@delete_cookies.inject(header) { |m, (k, v)|
|
440
|
-
::Rack::Utils.add_remove_cookie_to_header(m, k, v)
|
441
|
-
}
|
442
|
-
end
|
443
|
-
|
444
431
|
def write_cookie?(cookie)
|
445
432
|
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
446
433
|
end
|
@@ -452,17 +439,40 @@ module ActionDispatch
|
|
452
439
|
|
453
440
|
options[:path] ||= "/"
|
454
441
|
|
455
|
-
|
456
|
-
|
442
|
+
unless options.key?(:same_site)
|
443
|
+
options[:same_site] = request.cookies_same_site_protection
|
444
|
+
end
|
457
445
|
|
458
446
|
if options[:domain] == :all || options[:domain] == "all"
|
459
|
-
|
460
|
-
|
447
|
+
cookie_domain = ""
|
448
|
+
dot_splitted_host = request.host.split(".", -1)
|
449
|
+
|
450
|
+
# Case where request.host is not an IP address or it's an invalid domain
|
451
|
+
# (ip confirms to the domain structure we expect so we explicitly check for ip)
|
452
|
+
if request.host.match?(/^[\d.]+$/) || dot_splitted_host.include?("") || dot_splitted_host.length == 1
|
453
|
+
options[:domain] = nil
|
454
|
+
return
|
455
|
+
end
|
461
456
|
|
462
|
-
# If
|
463
|
-
|
464
|
-
|
465
|
-
|
457
|
+
# If there is a provided tld length then we use it otherwise default domain.
|
458
|
+
if options[:tld_length].present?
|
459
|
+
# Case where the tld_length provided is valid
|
460
|
+
if dot_splitted_host.length >= options[:tld_length]
|
461
|
+
cookie_domain = dot_splitted_host.last(options[:tld_length]).join(".")
|
462
|
+
end
|
463
|
+
# Case where tld_length is not provided
|
464
|
+
else
|
465
|
+
# Regular TLDs
|
466
|
+
if !(/\.[^.]{2,3}\.[^.]{2}\z/.match?(request.host))
|
467
|
+
cookie_domain = dot_splitted_host.last(2).join(".")
|
468
|
+
# **.**, ***.** style TLDs like co.uk and com.au
|
469
|
+
else
|
470
|
+
cookie_domain = dot_splitted_host.last(3).join(".")
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
options[:domain] = if cookie_domain.present?
|
475
|
+
cookie_domain
|
466
476
|
end
|
467
477
|
elsif options[:domain].is_a? Array
|
468
478
|
# If host matches one of the supplied domains.
|
@@ -470,6 +480,8 @@ module ActionDispatch
|
|
470
480
|
domain = domain.delete_prefix(".")
|
471
481
|
request.host == domain || request.host.end_with?(".#{domain}")
|
472
482
|
end
|
483
|
+
elsif options[:domain].respond_to?(:call)
|
484
|
+
options[:domain] = options[:domain].call(request)
|
473
485
|
end
|
474
486
|
end
|
475
487
|
end
|
@@ -533,75 +545,57 @@ module ActionDispatch
|
|
533
545
|
end
|
534
546
|
end
|
535
547
|
|
536
|
-
class MarshalWithJsonFallback # :nodoc:
|
537
|
-
def self.load(value)
|
538
|
-
Marshal.load(value)
|
539
|
-
rescue TypeError => e
|
540
|
-
ActiveSupport::JSON.decode(value) rescue raise e
|
541
|
-
end
|
542
|
-
|
543
|
-
def self.dump(value)
|
544
|
-
Marshal.dump(value)
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
class JsonSerializer # :nodoc:
|
549
|
-
def self.load(value)
|
550
|
-
ActiveSupport::JSON.decode(value)
|
551
|
-
end
|
552
|
-
|
553
|
-
def self.dump(value)
|
554
|
-
ActiveSupport::JSON.encode(value)
|
555
|
-
end
|
556
|
-
end
|
557
|
-
|
558
548
|
module SerializedCookieJars # :nodoc:
|
559
|
-
MARSHAL_SIGNATURE = "\x04\x08"
|
560
549
|
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
|
561
550
|
|
562
551
|
protected
|
563
|
-
def
|
564
|
-
request.
|
552
|
+
def digest
|
553
|
+
request.cookies_digest || "SHA1"
|
565
554
|
end
|
566
555
|
|
567
|
-
|
568
|
-
|
556
|
+
private
|
557
|
+
def serializer
|
558
|
+
@serializer ||=
|
559
|
+
case request.cookies_serializer
|
560
|
+
when nil
|
561
|
+
ActiveSupport::Messages::SerializerWithFallback[:marshal]
|
562
|
+
when :hybrid
|
563
|
+
ActiveSupport::Messages::SerializerWithFallback[:json_allow_marshal]
|
564
|
+
when Symbol
|
565
|
+
ActiveSupport::Messages::SerializerWithFallback[request.cookies_serializer]
|
566
|
+
else
|
567
|
+
request.cookies_serializer
|
568
|
+
end
|
569
569
|
end
|
570
570
|
|
571
|
-
def
|
572
|
-
|
573
|
-
|
571
|
+
def reserialize?(dumped)
|
572
|
+
serializer.is_a?(ActiveSupport::Messages::SerializerWithFallback) &&
|
573
|
+
serializer != ActiveSupport::Messages::SerializerWithFallback[:marshal] &&
|
574
|
+
!serializer.dumped?(dumped)
|
575
|
+
end
|
574
576
|
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
when rotate
|
582
|
-
serializer.load(value).tap do |v|
|
583
|
-
self[name] = { value: v }
|
584
|
-
end
|
585
|
-
else
|
586
|
-
serializer.load(value)
|
577
|
+
def parse(name, dumped, force_reserialize: false, **)
|
578
|
+
if dumped
|
579
|
+
begin
|
580
|
+
value = serializer.load(dumped)
|
581
|
+
rescue StandardError
|
582
|
+
return
|
587
583
|
end
|
584
|
+
|
585
|
+
self[name] = { value: value } if force_reserialize || reserialize?(dumped)
|
586
|
+
|
587
|
+
value
|
588
588
|
end
|
589
589
|
end
|
590
590
|
|
591
|
-
def
|
592
|
-
|
593
|
-
case serializer
|
594
|
-
when :marshal
|
595
|
-
MarshalWithJsonFallback
|
596
|
-
when :json, :hybrid
|
597
|
-
JsonSerializer
|
598
|
-
else
|
599
|
-
serializer
|
600
|
-
end
|
591
|
+
def commit(name, options)
|
592
|
+
options[:value] = serializer.dump(options[:value])
|
601
593
|
end
|
602
594
|
|
603
|
-
def
|
604
|
-
|
595
|
+
def check_for_overflow!(name, options)
|
596
|
+
if options[:value].bytesize > MAX_COOKIE_SIZE
|
597
|
+
raise CookieOverflow, "#{name} cookie overflowed with size #{options[:value].bytesize} bytes"
|
598
|
+
end
|
605
599
|
end
|
606
600
|
end
|
607
601
|
|
@@ -622,15 +616,15 @@ module ActionDispatch
|
|
622
616
|
|
623
617
|
private
|
624
618
|
def parse(name, signed_message, purpose: nil)
|
625
|
-
|
626
|
-
|
627
|
-
|
619
|
+
rotated = false
|
620
|
+
data = @verifier.verified(signed_message, purpose: purpose, on_rotation: -> { rotated = true })
|
621
|
+
super(name, data, force_reserialize: rotated)
|
628
622
|
end
|
629
623
|
|
630
624
|
def commit(name, options)
|
631
|
-
|
632
|
-
|
633
|
-
|
625
|
+
super
|
626
|
+
options[:value] = @verifier.generate(options[:value], **cookie_metadata(name, options))
|
627
|
+
check_for_overflow!(name, options)
|
634
628
|
end
|
635
629
|
end
|
636
630
|
|
@@ -672,17 +666,17 @@ module ActionDispatch
|
|
672
666
|
|
673
667
|
private
|
674
668
|
def parse(name, encrypted_message, purpose: nil)
|
675
|
-
|
676
|
-
|
677
|
-
|
669
|
+
rotated = false
|
670
|
+
data = @encryptor.decrypt_and_verify(encrypted_message, purpose: purpose, on_rotation: -> { rotated = true })
|
671
|
+
super(name, data, force_reserialize: rotated)
|
678
672
|
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
679
673
|
nil
|
680
674
|
end
|
681
675
|
|
682
676
|
def commit(name, options)
|
683
|
-
|
684
|
-
|
685
|
-
|
677
|
+
super
|
678
|
+
options[:value] = @encryptor.encrypt_and_sign(options[:value], **cookie_metadata(name, options))
|
679
|
+
check_for_overflow!(name, options)
|
686
680
|
end
|
687
681
|
end
|
688
682
|
|
@@ -691,21 +685,18 @@ module ActionDispatch
|
|
691
685
|
end
|
692
686
|
|
693
687
|
def call(env)
|
694
|
-
request = ActionDispatch::Request.new
|
695
|
-
|
696
|
-
status, headers, body = @app.call(env)
|
688
|
+
request = ActionDispatch::Request.new(env)
|
689
|
+
response = @app.call(env)
|
697
690
|
|
698
691
|
if request.have_cookie_jar?
|
699
692
|
cookie_jar = request.cookie_jar
|
700
693
|
unless cookie_jar.committed?
|
701
|
-
|
702
|
-
|
703
|
-
headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
|
704
|
-
end
|
694
|
+
response = Rack::Response[*response]
|
695
|
+
cookie_jar.write(response)
|
705
696
|
end
|
706
697
|
end
|
707
698
|
|
708
|
-
|
699
|
+
response.to_a
|
709
700
|
end
|
710
701
|
end
|
711
702
|
end
|