actionpack 5.1.7 → 5.2.4.3
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 +282 -362
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/lib/abstract_controller.rb +3 -0
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +10 -2
- data/lib/abstract_controller/caching.rb +3 -2
- data/lib/abstract_controller/caching/fragments.rb +30 -7
- data/lib/abstract_controller/callbacks.rb +25 -3
- data/lib/abstract_controller/collector.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +4 -5
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +9 -16
- data/lib/abstract_controller/translation.rb +2 -0
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/action_controller.rb +3 -0
- data/lib/action_controller/api.rb +2 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/base.rb +3 -0
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +5 -3
- data/lib/action_controller/metal.rb +13 -14
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +4 -3
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +2 -0
- data/lib/action_controller/metal/data_streaming.rb +7 -5
- data/lib/action_controller/metal/etag_with_flash.rb +2 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +3 -2
- data/lib/action_controller/metal/exceptions.rb +2 -3
- data/lib/action_controller/metal/flash.rb +3 -2
- data/lib/action_controller/metal/force_ssl.rb +4 -2
- data/lib/action_controller/metal/head.rb +2 -0
- data/lib/action_controller/metal/helpers.rb +4 -3
- data/lib/action_controller/metal/http_authentication.rb +8 -9
- data/lib/action_controller/metal/implicit_render.rb +2 -0
- data/lib/action_controller/metal/instrumentation.rb +4 -6
- data/lib/action_controller/metal/live.rb +3 -1
- data/lib/action_controller/metal/mime_responds.rb +3 -1
- data/lib/action_controller/metal/parameter_encoding.rb +2 -0
- data/lib/action_controller/metal/params_wrapper.rb +14 -10
- data/lib/action_controller/metal/redirecting.rb +22 -11
- data/lib/action_controller/metal/renderers.rb +4 -3
- data/lib/action_controller/metal/rendering.rb +2 -2
- data/lib/action_controller/metal/request_forgery_protection.rb +62 -10
- data/lib/action_controller/metal/rescue.rb +5 -3
- data/lib/action_controller/metal/streaming.rb +3 -1
- data/lib/action_controller/metal/strong_parameters.rb +36 -25
- data/lib/action_controller/metal/testing.rb +2 -6
- data/lib/action_controller/metal/url_for.rb +2 -0
- data/lib/action_controller/railtie.rb +16 -4
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +2 -0
- data/lib/action_controller/template_assertions.rb +2 -0
- data/lib/action_controller/test_case.rb +16 -10
- data/lib/action_dispatch.rb +9 -5
- data/lib/action_dispatch/http/cache.rb +22 -14
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +4 -2
- data/lib/action_dispatch/http/filter_redirect.rb +2 -0
- data/lib/action_dispatch/http/headers.rb +2 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +4 -8
- data/lib/action_dispatch/http/mime_type.rb +15 -13
- data/lib/action_dispatch/http/mime_types.rb +17 -2
- data/lib/action_dispatch/http/parameter_filter.rb +2 -0
- data/lib/action_dispatch/http/parameters.rb +6 -9
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +36 -16
- data/lib/action_dispatch/http/response.rb +11 -9
- data/lib/action_dispatch/http/upload.rb +2 -0
- data/lib/action_dispatch/http/url.rb +5 -6
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/journey/formatter.rb +4 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +2 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +3 -2
- data/lib/action_dispatch/journey/nfa/builder.rb +2 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +12 -10
- data/lib/action_dispatch/journey/nfa/simulator.rb +2 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +2 -0
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +4 -1
- data/lib/action_dispatch/journey/route.rb +15 -6
- data/lib/action_dispatch/journey/router.rb +3 -1
- data/lib/action_dispatch/journey/router/utils.rb +14 -7
- data/lib/action_dispatch/journey/routes.rb +3 -1
- data/lib/action_dispatch/journey/scanner.rb +1 -0
- data/lib/action_dispatch/journey/visitors.rb +5 -3
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +148 -91
- data/lib/action_dispatch/middleware/debug_exceptions.rb +4 -2
- data/lib/action_dispatch/middleware/debug_locks.rb +9 -7
- data/lib/action_dispatch/middleware/exception_wrapper.rb +5 -6
- data/lib/action_dispatch/middleware/executor.rb +2 -0
- data/lib/action_dispatch/middleware/flash.rb +4 -2
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -4
- data/lib/action_dispatch/middleware/reloader.rb +2 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +7 -5
- data/lib/action_dispatch/middleware/request_id.rb +3 -1
- data/lib/action_dispatch/middleware/session/abstract_store.rb +17 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +13 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +31 -32
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +3 -1
- data/lib/action_dispatch/middleware/ssl.rb +44 -38
- data/lib/action_dispatch/middleware/stack.rb +4 -2
- data/lib/action_dispatch/middleware/static.rb +14 -12
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +6 -2
- data/lib/action_dispatch/railtie.rb +11 -1
- data/lib/action_dispatch/request/session.rb +16 -5
- data/lib/action_dispatch/request/utils.rb +6 -4
- data/lib/action_dispatch/routing.rb +3 -1
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +6 -4
- data/lib/action_dispatch/routing/mapper.rb +64 -52
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +7 -5
- data/lib/action_dispatch/routing/route_set.rb +29 -24
- data/lib/action_dispatch/routing/routes_proxy.rb +5 -2
- data/lib/action_dispatch/routing/url_for.rb +25 -5
- data/lib/action_dispatch/system_test_case.rb +22 -6
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +9 -3
- data/lib/action_dispatch/system_testing/server.rb +2 -16
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +12 -14
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +8 -2
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +2 -0
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/assertions/response.rb +4 -2
- data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
- data/lib/action_dispatch/testing/integration.rb +24 -21
- data/lib/action_dispatch/testing/request_encoder.rb +3 -1
- data/lib/action_dispatch/testing/test_process.rb +2 -0
- data/lib/action_dispatch/testing/test_request.rb +3 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -3
- data/lib/action_pack.rb +3 -1
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +2 -0
- metadata +23 -11
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "action_dispatch/http/request"
|
2
4
|
require "action_dispatch/middleware/exception_wrapper"
|
3
5
|
require "action_dispatch/routing/inspector"
|
@@ -10,7 +12,7 @@ module ActionDispatch
|
|
10
12
|
# This middleware is responsible for logging exceptions and
|
11
13
|
# showing a debugging page in case the request is local.
|
12
14
|
class DebugExceptions
|
13
|
-
RESCUES_TEMPLATE_PATH = File.expand_path("
|
15
|
+
RESCUES_TEMPLATE_PATH = File.expand_path("templates", __dir__)
|
14
16
|
|
15
17
|
class DebugView < ActionView::Base
|
16
18
|
def debug_params(params)
|
@@ -21,7 +23,7 @@ module ActionDispatch
|
|
21
23
|
if clean_params.empty?
|
22
24
|
"None"
|
23
25
|
else
|
24
|
-
PP.pp(clean_params, "", 200)
|
26
|
+
PP.pp(clean_params, "".dup, 200)
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
3
5
|
#
|
@@ -41,7 +43,7 @@ module ActionDispatch
|
|
41
43
|
|
42
44
|
private
|
43
45
|
def render_details(req)
|
44
|
-
threads = ActiveSupport::Dependencies.interlock.raw_state do |
|
46
|
+
threads = ActiveSupport::Dependencies.interlock.raw_state do |raw_threads|
|
45
47
|
# The Interlock itself comes to a complete halt as long as this block
|
46
48
|
# is executing. That gives us a more consistent picture of everything,
|
47
49
|
# but creates a pretty strong Observer Effect.
|
@@ -51,29 +53,29 @@ module ActionDispatch
|
|
51
53
|
# strictly diagnostic tool (to be used when something has gone wrong),
|
52
54
|
# and not for any sort of general monitoring.
|
53
55
|
|
54
|
-
|
56
|
+
raw_threads.each.with_index do |(thread, info), idx|
|
55
57
|
info[:index] = idx
|
56
58
|
info[:backtrace] = thread.backtrace
|
57
59
|
end
|
58
60
|
|
59
|
-
|
61
|
+
raw_threads
|
60
62
|
end
|
61
63
|
|
62
64
|
str = threads.map do |thread, info|
|
63
65
|
if info[:exclusive]
|
64
|
-
lock_state = "Exclusive"
|
66
|
+
lock_state = "Exclusive".dup
|
65
67
|
elsif info[:sharing] > 0
|
66
|
-
lock_state = "Sharing"
|
68
|
+
lock_state = "Sharing".dup
|
67
69
|
lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
|
68
70
|
else
|
69
|
-
lock_state = "No lock"
|
71
|
+
lock_state = "No lock".dup
|
70
72
|
end
|
71
73
|
|
72
74
|
if info[:waiting]
|
73
75
|
lock_state << " (yielded share)"
|
74
76
|
end
|
75
77
|
|
76
|
-
msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
|
78
|
+
msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n".dup
|
77
79
|
|
78
80
|
if info[:sleeper]
|
79
81
|
msg << " Waiting in #{info[:sleeper]}"
|
@@ -1,11 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/module/attribute_accessors"
|
2
4
|
require "rack/utils"
|
3
5
|
|
4
6
|
module ActionDispatch
|
5
7
|
class ExceptionWrapper
|
6
|
-
cattr_accessor :rescue_responses
|
7
|
-
@@rescue_responses = Hash.new(:internal_server_error)
|
8
|
-
@@rescue_responses.merge!(
|
8
|
+
cattr_accessor :rescue_responses, default: Hash.new(:internal_server_error).merge!(
|
9
9
|
"ActionController::RoutingError" => :not_found,
|
10
10
|
"AbstractController::ActionNotFound" => :not_found,
|
11
11
|
"ActionController::MethodNotAllowed" => :method_not_allowed,
|
@@ -21,12 +21,11 @@ module ActionDispatch
|
|
21
21
|
"Rack::QueryParser::InvalidParameterError" => :bad_request
|
22
22
|
)
|
23
23
|
|
24
|
-
cattr_accessor :rescue_templates
|
25
|
-
@@rescue_templates = Hash.new("diagnostics")
|
26
|
-
@@rescue_templates.merge!(
|
24
|
+
cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
|
27
25
|
"ActionView::MissingTemplate" => "missing_template",
|
28
26
|
"ActionController::RoutingError" => "routing_error",
|
29
27
|
"AbstractController::ActionNotFound" => "unknown_action",
|
28
|
+
"ActiveRecord::StatementInvalid" => "invalid_statement",
|
30
29
|
"ActionView::Template::Error" => "template_error"
|
31
30
|
)
|
32
31
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/hash/keys"
|
2
4
|
|
3
5
|
module ActionDispatch
|
@@ -65,13 +67,13 @@ module ActionDispatch
|
|
65
67
|
self.flash = flash_hash.dup
|
66
68
|
end
|
67
69
|
|
68
|
-
if (!session.respond_to?(:loaded?) || session.loaded?) && #
|
70
|
+
if (!session.respond_to?(:loaded?) || session.loaded?) && # reset_session uses {}, which doesn't implement #loaded?
|
69
71
|
session.key?("flash") && session["flash"].nil?
|
70
72
|
session.delete("flash")
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
74
|
-
def reset_session # :nodoc
|
76
|
+
def reset_session # :nodoc:
|
75
77
|
super
|
76
78
|
self.flash = nil
|
77
79
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
# When called, this middleware renders an error page. By default if an HTML
|
3
|
-
# response is expected it will render static error pages from the
|
5
|
+
# response is expected it will render static error pages from the <tt>/public</tt>
|
4
6
|
# directory. For example when this middleware receives a 500 response it will
|
5
|
-
# render the template found in
|
7
|
+
# render the template found in <tt>/public/500.html</tt>.
|
6
8
|
# If an internationalized locale is set, this middleware will attempt to render
|
7
|
-
# the template in
|
8
|
-
# is not found it will fall back on
|
9
|
+
# the template in <tt>/public/500.<locale>.html</tt>. If an internationalized template
|
10
|
+
# is not found it will fall back on <tt>/public/500.html</tt>.
|
9
11
|
#
|
10
12
|
# When a request with a content type other than HTML is made, this middleware
|
11
13
|
# will attempt to convert error information into the appropriate response type.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "ipaddr"
|
2
4
|
|
3
5
|
module ActionDispatch
|
@@ -10,7 +12,7 @@ module ActionDispatch
|
|
10
12
|
# by @gingerlime. A more detailed explanation of the algorithm is given
|
11
13
|
# at GetIp#calculate_ip.
|
12
14
|
#
|
13
|
-
# Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[
|
15
|
+
# Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
|
14
16
|
# requires. Some Rack servers simply drop preceding headers, and only report
|
15
17
|
# the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
|
16
18
|
# If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
|
@@ -29,7 +31,7 @@ module ActionDispatch
|
|
29
31
|
# The default trusted IPs list simply includes IP addresses that are
|
30
32
|
# guaranteed by the IP specification to be private addresses. Those will
|
31
33
|
# not be the ultimate client IP in production, and so are discarded. See
|
32
|
-
#
|
34
|
+
# https://en.wikipedia.org/wiki/Private_network for details.
|
33
35
|
TRUSTED_PROXIES = [
|
34
36
|
"127.0.0.1", # localhost IPv4
|
35
37
|
"::1", # localhost IPv6
|
@@ -157,13 +159,13 @@ module ActionDispatch
|
|
157
159
|
|
158
160
|
def ips_from(header) # :doc:
|
159
161
|
return [] unless header
|
160
|
-
# Split the comma-separated list into an array of strings
|
162
|
+
# Split the comma-separated list into an array of strings.
|
161
163
|
ips = header.strip.split(/[,\s]+/)
|
162
164
|
ips.select do |ip|
|
163
165
|
begin
|
164
|
-
# Only return IPs that are valid according to the IPAddr#new method
|
166
|
+
# Only return IPs that are valid according to the IPAddr#new method.
|
165
167
|
range = IPAddr.new(ip).to_range
|
166
|
-
#
|
168
|
+
# We want to make sure nobody is sneaking a netmask in.
|
167
169
|
range.begin == range.end
|
168
170
|
rescue ArgumentError
|
169
171
|
nil
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "securerandom"
|
2
4
|
require "active_support/core_ext/string/access"
|
3
5
|
|
@@ -28,7 +30,7 @@ module ActionDispatch
|
|
28
30
|
private
|
29
31
|
def make_request_id(request_id)
|
30
32
|
if request_id.presence
|
31
|
-
request_id.gsub(/[^\w
|
33
|
+
request_id.gsub(/[^\w\-@]/, "".freeze).first(255)
|
32
34
|
else
|
33
35
|
internal_request_id
|
34
36
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "rack/utils"
|
2
4
|
require "rack/request"
|
3
5
|
require "rack/session/abstract/id"
|
@@ -53,7 +55,7 @@ module ActionDispatch
|
|
53
55
|
rescue ArgumentError => argument_error
|
54
56
|
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
|
55
57
|
begin
|
56
|
-
# Note that the regexp does not allow $1 to end with a ':'
|
58
|
+
# Note that the regexp does not allow $1 to end with a ':'.
|
57
59
|
$1.constantize
|
58
60
|
rescue LoadError, NameError
|
59
61
|
raise ActionDispatch::Session::SessionRestoreError
|
@@ -81,7 +83,21 @@ module ActionDispatch
|
|
81
83
|
include SessionObject
|
82
84
|
|
83
85
|
private
|
86
|
+
def set_cookie(request, session_id, cookie)
|
87
|
+
request.cookie_jar[key] = cookie
|
88
|
+
end
|
89
|
+
end
|
84
90
|
|
91
|
+
class AbstractSecureStore < Rack::Session::Abstract::PersistedSecure
|
92
|
+
include Compatibility
|
93
|
+
include StaleSessionCheck
|
94
|
+
include SessionObject
|
95
|
+
|
96
|
+
def generate_sid
|
97
|
+
Rack::Session::SessionId.new(super)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
85
101
|
def set_cookie(request, session_id, cookie)
|
86
102
|
request.cookie_jar[key] = cookie
|
87
103
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "action_dispatch/middleware/session/abstract_store"
|
2
4
|
|
3
5
|
module ActionDispatch
|
@@ -10,7 +12,7 @@ module ActionDispatch
|
|
10
12
|
# * <tt>cache</tt> - The cache to use. If it is not specified, <tt>Rails.cache</tt> will be used.
|
11
13
|
# * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring.
|
12
14
|
# By default, the <tt>:expires_in</tt> option of the cache is used.
|
13
|
-
class CacheStore <
|
15
|
+
class CacheStore < AbstractSecureStore
|
14
16
|
def initialize(app, options = {})
|
15
17
|
@cache = options[:cache] || Rails.cache
|
16
18
|
options[:expire_after] ||= @cache.options[:expires_in]
|
@@ -19,7 +21,7 @@ module ActionDispatch
|
|
19
21
|
|
20
22
|
# Get a session from the cache.
|
21
23
|
def find_session(env, sid)
|
22
|
-
unless sid && (session =
|
24
|
+
unless sid && (session = get_session_with_fallback(sid))
|
23
25
|
sid, session = generate_sid, {}
|
24
26
|
end
|
25
27
|
[sid, session]
|
@@ -27,7 +29,7 @@ module ActionDispatch
|
|
27
29
|
|
28
30
|
# Set a session in the cache.
|
29
31
|
def write_session(env, sid, session, options)
|
30
|
-
key = cache_key(sid)
|
32
|
+
key = cache_key(sid.private_id)
|
31
33
|
if session
|
32
34
|
@cache.write(key, session, expires_in: options[:expire_after])
|
33
35
|
else
|
@@ -38,14 +40,19 @@ module ActionDispatch
|
|
38
40
|
|
39
41
|
# Remove a session from the cache.
|
40
42
|
def delete_session(env, sid, options)
|
41
|
-
@cache.delete(cache_key(sid))
|
43
|
+
@cache.delete(cache_key(sid.private_id))
|
44
|
+
@cache.delete(cache_key(sid.public_id))
|
42
45
|
generate_sid
|
43
46
|
end
|
44
47
|
|
45
48
|
private
|
46
49
|
# Turn the session id into a cache key.
|
47
|
-
def cache_key(
|
48
|
-
"_session_id:#{
|
50
|
+
def cache_key(id)
|
51
|
+
"_session_id:#{id}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_session_with_fallback(sid)
|
55
|
+
@cache.read(cache_key(sid.private_id)) || @cache.read(cache_key(sid.public_id))
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/hash/keys"
|
2
4
|
require "action_dispatch/middleware/session/abstract_store"
|
3
5
|
require "rack/session/cookie"
|
@@ -19,39 +21,26 @@ module ActionDispatch
|
|
19
21
|
# knowing your app's secret key, but can easily read their +user_id+. This
|
20
22
|
# was the default for Rails 3 apps.
|
21
23
|
#
|
22
|
-
#
|
24
|
+
# Your cookies will be encrypted using your apps secret_key_base. This
|
23
25
|
# goes a step further than signed cookies in that encrypted cookies cannot
|
24
26
|
# be altered or read by users. This is the default starting in Rails 4.
|
25
27
|
#
|
26
|
-
#
|
27
|
-
# be encrypted, and signed cookies generated by Rails 3 will be
|
28
|
-
# transparently read and encrypted to provide a smooth upgrade path.
|
29
|
-
#
|
30
|
-
# Configure your session store in config/initializers/session_store.rb:
|
28
|
+
# Configure your session store in <tt>config/initializers/session_store.rb</tt>:
|
31
29
|
#
|
32
30
|
# Rails.application.config.session_store :cookie_store, key: '_your_app_session'
|
33
31
|
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# To generate a secret key for an existing application, run `rails secret`.
|
32
|
+
# In the development and test environments your application's secret key base is
|
33
|
+
# generated by Rails and stored in a temporary file in <tt>tmp/development_secret.txt</tt>.
|
34
|
+
# In all other environments, it is stored encrypted in the
|
35
|
+
# <tt>config/credentials.yml.enc</tt> file.
|
40
36
|
#
|
41
|
-
# If
|
42
|
-
#
|
43
|
-
# Note that you should wait to set secret_key_base until you have 100% of
|
44
|
-
# your userbase on Rails 4 and are reasonably sure you will not need to
|
45
|
-
# rollback to Rails 3. This is because cookies signed based on the new
|
46
|
-
# secret_key_base in Rails 4 are not backwards compatible with Rails 3.
|
47
|
-
# You are free to leave your existing secret_token in place, not set the
|
48
|
-
# new secret_key_base, and ignore the deprecation warnings until you are
|
49
|
-
# reasonably sure that your upgrade is otherwise complete. Additionally,
|
50
|
-
# you should take care to make sure you are not relying on the ability to
|
51
|
-
# decode signed cookies generated by your app in external applications or
|
52
|
-
# JavaScript before upgrading.
|
37
|
+
# If your application was not updated to Rails 5.2 defaults, the secret_key_base
|
38
|
+
# will be found in the old <tt>config/secrets.yml</tt> file.
|
53
39
|
#
|
54
|
-
# Note that changing
|
40
|
+
# Note that changing your secret_key_base will invalidate all existing session.
|
41
|
+
# Additionally, you should take care to make sure you are not relying on the
|
42
|
+
# ability to decode signed cookies generated by your app in external
|
43
|
+
# applications or JavaScript before changing it.
|
55
44
|
#
|
56
45
|
# Because CookieStore extends Rack::Session::Abstract::Persisted, many of the
|
57
46
|
# options described there can be used to customize the session cookie that
|
@@ -62,7 +51,16 @@ module ActionDispatch
|
|
62
51
|
# would set the session cookie to expire automatically 14 days after creation.
|
63
52
|
# Other useful options include <tt>:key</tt>, <tt>:secure</tt> and
|
64
53
|
# <tt>:httponly</tt>.
|
65
|
-
class CookieStore <
|
54
|
+
class CookieStore < AbstractSecureStore
|
55
|
+
class SessionId < DelegateClass(Rack::Session::SessionId)
|
56
|
+
attr_reader :cookie_value
|
57
|
+
|
58
|
+
def initialize(session_id, cookie_value = {})
|
59
|
+
super(session_id)
|
60
|
+
@cookie_value = cookie_value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
66
64
|
def initialize(app, options = {})
|
67
65
|
super(app, options.merge!(cookie_only: true))
|
68
66
|
end
|
@@ -70,7 +68,7 @@ module ActionDispatch
|
|
70
68
|
def delete_session(req, session_id, options)
|
71
69
|
new_sid = generate_sid unless options[:drop]
|
72
70
|
# Reset hash and Assign the new session id
|
73
|
-
req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid } : {})
|
71
|
+
req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid.public_id } : {})
|
74
72
|
new_sid
|
75
73
|
end
|
76
74
|
|
@@ -78,7 +76,7 @@ module ActionDispatch
|
|
78
76
|
stale_session_check! do
|
79
77
|
data = unpacked_cookie_data(req)
|
80
78
|
data = persistent_session_id!(data)
|
81
|
-
[data["session_id"], data]
|
79
|
+
[Rack::Session::SessionId.new(data["session_id"]), data]
|
82
80
|
end
|
83
81
|
end
|
84
82
|
|
@@ -86,7 +84,8 @@ module ActionDispatch
|
|
86
84
|
|
87
85
|
def extract_session_id(req)
|
88
86
|
stale_session_check! do
|
89
|
-
unpacked_cookie_data(req)["session_id"]
|
87
|
+
sid = unpacked_cookie_data(req)["session_id"]
|
88
|
+
sid && Rack::Session::SessionId.new(sid)
|
90
89
|
end
|
91
90
|
end
|
92
91
|
|
@@ -104,13 +103,13 @@ module ActionDispatch
|
|
104
103
|
|
105
104
|
def persistent_session_id!(data, sid = nil)
|
106
105
|
data ||= {}
|
107
|
-
data["session_id"] ||= sid || generate_sid
|
106
|
+
data["session_id"] ||= sid || generate_sid.public_id
|
108
107
|
data
|
109
108
|
end
|
110
109
|
|
111
110
|
def write_session(req, sid, session_data, options)
|
112
|
-
session_data["session_id"] = sid
|
113
|
-
session_data
|
111
|
+
session_data["session_id"] = sid.public_id
|
112
|
+
SessionId.new(sid, session_data)
|
114
113
|
end
|
115
114
|
|
116
115
|
def set_cookie(request, session_id, cookie)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "action_dispatch/http/request"
|
2
4
|
require "action_dispatch/middleware/exception_wrapper"
|
3
5
|
|
@@ -8,7 +10,7 @@ module ActionDispatch
|
|
8
10
|
# The exceptions app should be passed as parameter on initialization
|
9
11
|
# of ShowExceptions. Every time there is an exception, ShowExceptions will
|
10
12
|
# store the exception in env["action_dispatch.exception"], rewrite the
|
11
|
-
# PATH_INFO to the exception status code and call the
|
13
|
+
# PATH_INFO to the exception status code and call the Rack app.
|
12
14
|
#
|
13
15
|
# If the application returns a "X-Cascade" pass response, this middleware
|
14
16
|
# will send an empty response as result with the correct status code.
|
@@ -1,50 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
|
-
# This middleware is added to the stack when
|
3
|
-
# the options set in
|
4
|
+
# This middleware is added to the stack when <tt>config.force_ssl = true</tt>, and is passed
|
5
|
+
# the options set in +config.ssl_options+. It does three jobs to enforce secure HTTP
|
4
6
|
# requests:
|
5
7
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
8
|
+
# 1. <b>TLS redirect</b>: Permanently redirects +http://+ requests to +https://+
|
9
|
+
# with the same URL host, path, etc. Enabled by default. Set +config.ssl_options+
|
10
|
+
# to modify the destination URL
|
11
|
+
# (e.g. <tt>redirect: { host: "secure.widgets.com", port: 8080 }</tt>), or set
|
12
|
+
# <tt>redirect: false</tt> to disable this feature.
|
13
|
+
#
|
14
|
+
# Requests can opt-out of redirection with +exclude+:
|
15
|
+
#
|
16
|
+
# config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }
|
11
17
|
#
|
12
18
|
# Cookies will not be flagged as secure for excluded requests.
|
13
19
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
20
|
+
# 2. <b>Secure cookies</b>: Sets the +secure+ flag on cookies to tell browsers they
|
21
|
+
# must not be sent along with +http://+ requests. Enabled by default. Set
|
22
|
+
# +config.ssl_options+ with <tt>secure_cookies: false</tt> to disable this feature.
|
17
23
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
24
|
+
# 3. <b>HTTP Strict Transport Security (HSTS)</b>: Tells the browser to remember
|
25
|
+
# this site as TLS-only and automatically redirect non-TLS requests.
|
26
|
+
# Enabled by default. Configure +config.ssl_options+ with <tt>hsts: false</tt> to disable.
|
21
27
|
#
|
22
|
-
#
|
23
|
-
# * `expires`: How long, in seconds, these settings will stick. The minimum
|
24
|
-
# required to qualify for browser preload lists is `18.weeks`. Defaults to
|
25
|
-
# `180.days` (recommended).
|
26
|
-
# * `subdomains`: Set to `true` to tell the browser to apply these settings
|
27
|
-
# to all subdomains. This protects your cookies from interception by a
|
28
|
-
# vulnerable site on a subdomain. Defaults to `true`.
|
29
|
-
# * `preload`: Advertise that this site may be included in browsers'
|
30
|
-
# preloaded HSTS lists. HSTS protects your site on every visit *except the
|
31
|
-
# first visit* since it hasn't seen your HSTS header yet. To close this
|
32
|
-
# gap, browser vendors include a baked-in list of HSTS-enabled sites.
|
33
|
-
# Go to https://hstspreload.appspot.com to submit your site for inclusion.
|
34
|
-
# Defaults to `false`.
|
28
|
+
# Set +config.ssl_options+ with <tt>hsts: { ... }</tt> to configure HSTS:
|
35
29
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# `hsts: { expires: 0 }`.
|
30
|
+
# * +expires+: How long, in seconds, these settings will stick. The minimum
|
31
|
+
# required to qualify for browser preload lists is 1 year. Defaults to
|
32
|
+
# 1 year (recommended).
|
40
33
|
#
|
41
|
-
#
|
34
|
+
# * +subdomains+: Set to +true+ to tell the browser to apply these settings
|
35
|
+
# to all subdomains. This protects your cookies from interception by a
|
36
|
+
# vulnerable site on a subdomain. Defaults to +true+.
|
42
37
|
#
|
43
|
-
#
|
38
|
+
# * +preload+: Advertise that this site may be included in browsers'
|
39
|
+
# preloaded HSTS lists. HSTS protects your site on every visit <i>except the
|
40
|
+
# first visit</i> since it hasn't seen your HSTS header yet. To close this
|
41
|
+
# gap, browser vendors include a baked-in list of HSTS-enabled sites.
|
42
|
+
# Go to https://hstspreload.org to submit your site for inclusion.
|
43
|
+
# Defaults to +false+.
|
44
|
+
#
|
45
|
+
# To turn off HSTS, omitting the header is not enough. Browsers will remember the
|
46
|
+
# original HSTS directive until it expires. Instead, use the header to tell browsers to
|
47
|
+
# expire HSTS immediately. Setting <tt>hsts: false</tt> is a shortcut for
|
48
|
+
# <tt>hsts: { expires: 0 }</tt>.
|
44
49
|
class SSL
|
45
|
-
#
|
46
|
-
|
47
|
-
|
50
|
+
# :stopdoc:
|
51
|
+
|
52
|
+
# Default to 1 year, the minimum for browser preload lists.
|
53
|
+
HSTS_EXPIRES_IN = 31536000
|
48
54
|
|
49
55
|
def self.default_hsts_options
|
50
56
|
{ expires: HSTS_EXPIRES_IN, subdomains: true, preload: false }
|
@@ -94,9 +100,9 @@ module ActionDispatch
|
|
94
100
|
end
|
95
101
|
end
|
96
102
|
|
97
|
-
#
|
103
|
+
# https://tools.ietf.org/html/rfc6797#section-6.1
|
98
104
|
def build_hsts_header(hsts)
|
99
|
-
value = "max-age=#{hsts[:expires].to_i}"
|
105
|
+
value = "max-age=#{hsts[:expires].to_i}".dup
|
100
106
|
value << "; includeSubDomains" if hsts[:subdomains]
|
101
107
|
value << "; preload" if hsts[:preload]
|
102
108
|
value
|
@@ -135,7 +141,7 @@ module ActionDispatch
|
|
135
141
|
host = @redirect[:host] || request.host
|
136
142
|
port = @redirect[:port] || request.port
|
137
143
|
|
138
|
-
location = "https://#{host}"
|
144
|
+
location = "https://#{host}".dup
|
139
145
|
location << ":#{port}" if port != 80 && port != 443
|
140
146
|
location << request.fullpath
|
141
147
|
location
|