actionpack 4.2.11.1 → 6.1.3.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +291 -489
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +81 -51
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +61 -33
- data/lib/abstract_controller/collector.rb +9 -13
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +115 -99
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
- data/lib/abstract_controller/rendering.rb +48 -47
- data/lib/abstract_controller/translation.rb +17 -8
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +13 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +150 -0
- data/lib/action_controller/base.rb +29 -24
- data/lib/action_controller/caching.rb +12 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +17 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +134 -46
- data/lib/action_controller/metal/content_security_policy.rb +51 -0
- data/lib/action_controller/metal/cookies.rb +6 -4
- data/lib/action_controller/metal/data_streaming.rb +30 -50
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
- data/lib/action_controller/metal/exceptions.rb +63 -15
- data/lib/action_controller/metal/flash.rb +9 -8
- data/lib/action_controller/metal/head.rb +26 -21
- data/lib/action_controller/metal/helpers.rb +37 -18
- data/lib/action_controller/metal/http_authentication.rb +81 -73
- data/lib/action_controller/metal/implicit_render.rb +53 -9
- data/lib/action_controller/metal/instrumentation.rb +32 -35
- data/lib/action_controller/metal/live.rb +102 -120
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +49 -47
- data/lib/action_controller/metal/parameter_encoding.rb +82 -0
- data/lib/action_controller/metal/params_wrapper.rb +83 -66
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +53 -32
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +77 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
- data/lib/action_controller/metal/rescue.rb +10 -17
- data/lib/action_controller/metal/streaming.rb +12 -11
- data/lib/action_controller/metal/strong_parameters.rb +714 -186
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +104 -87
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +3 -1
- data/lib/action_controller/renderer.rb +141 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +296 -422
- data/lib/action_controller.rb +34 -23
- data/lib/action_dispatch/http/cache.rb +107 -56
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +286 -0
- data/lib/action_dispatch/http/filter_parameters.rb +32 -25
- data/lib/action_dispatch/http/filter_redirect.rb +10 -12
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
- data/lib/action_dispatch/http/mime_type.rb +153 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameters.rb +90 -40
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +226 -121
- data/lib/action_dispatch/http/response.rb +248 -113
- data/lib/action_dispatch/http/upload.rb +21 -7
- data/lib/action_dispatch/http/url.rb +182 -100
- data/lib/action_dispatch/journey/formatter.rb +90 -43
- data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
- data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
- data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
- data/lib/action_dispatch/journey/nodes/node.rb +29 -15
- data/lib/action_dispatch/journey/parser.rb +17 -16
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +58 -54
- data/lib/action_dispatch/journey/route.rb +100 -32
- data/lib/action_dispatch/journey/router/utils.rb +29 -18
- data/lib/action_dispatch/journey/router.rb +55 -51
- data/lib/action_dispatch/journey/routes.rb +17 -17
- data/lib/action_dispatch/journey/scanner.rb +26 -17
- data/lib/action_dispatch/journey/visitors.rb +98 -54
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +3 -6
- data/lib/action_dispatch/middleware/cookies.rb +347 -217
- data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
- data/lib/action_dispatch/middleware/request_id.rb +17 -10
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
- data/lib/action_dispatch/middleware/ssl.rb +118 -35
- data/lib/action_dispatch/middleware/stack.rb +82 -41
- data/lib/action_dispatch/middleware/static.rb +156 -89
- 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/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- 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 +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
- 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 +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
- data/lib/action_dispatch/railtie.rb +27 -13
- data/lib/action_dispatch/request/session.rb +109 -61
- data/lib/action_dispatch/request/utils.rb +90 -23
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +141 -102
- data/lib/action_dispatch/routing/mapper.rb +811 -473
- data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
- data/lib/action_dispatch/routing/redirection.rb +37 -27
- data/lib/action_dispatch/routing/route_set.rb +363 -331
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +66 -26
- data/lib/action_dispatch/routing.rb +36 -36
- data/lib/action_dispatch/system_test_case.rb +190 -0
- data/lib/action_dispatch/system_testing/browser.rb +86 -0
- data/lib/action_dispatch/system_testing/driver.rb +67 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
- data/lib/action_dispatch/testing/assertion_response.rb +46 -0
- data/lib/action_dispatch/testing/assertions/response.rb +44 -22
- data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +391 -220
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +53 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +11 -11
- data/lib/action_dispatch.rb +35 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +78 -48
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,48 +1,77 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/attribute_accessors"
|
4
|
+
require "rack/utils"
|
3
5
|
|
4
6
|
module ActionDispatch
|
5
7
|
class ExceptionWrapper
|
6
|
-
cattr_accessor :rescue_responses
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
8
|
+
cattr_accessor :rescue_responses, default: Hash.new(:internal_server_error).merge!(
|
9
|
+
"ActionController::RoutingError" => :not_found,
|
10
|
+
"AbstractController::ActionNotFound" => :not_found,
|
11
|
+
"ActionController::MethodNotAllowed" => :method_not_allowed,
|
12
|
+
"ActionController::UnknownHttpMethod" => :method_not_allowed,
|
13
|
+
"ActionController::NotImplemented" => :not_implemented,
|
14
|
+
"ActionController::UnknownFormat" => :not_acceptable,
|
15
|
+
"ActionDispatch::Http::MimeNegotiation::InvalidType" => :not_acceptable,
|
16
|
+
"ActionController::MissingExactTemplate" => :not_acceptable,
|
17
|
+
"ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
|
18
|
+
"ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
|
19
|
+
"ActionDispatch::Http::Parameters::ParseError" => :bad_request,
|
20
|
+
"ActionController::BadRequest" => :bad_request,
|
21
|
+
"ActionController::ParameterMissing" => :bad_request,
|
22
|
+
"Rack::QueryParser::ParameterTypeError" => :bad_request,
|
23
|
+
"Rack::QueryParser::InvalidParameterError" => :bad_request
|
20
24
|
)
|
21
25
|
|
22
|
-
cattr_accessor :rescue_templates
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
|
27
|
+
"ActionView::MissingTemplate" => "missing_template",
|
28
|
+
"ActionController::RoutingError" => "routing_error",
|
29
|
+
"AbstractController::ActionNotFound" => "unknown_action",
|
30
|
+
"ActiveRecord::StatementInvalid" => "invalid_statement",
|
31
|
+
"ActionView::Template::Error" => "template_error",
|
32
|
+
"ActionController::MissingExactTemplate" => "missing_exact_template",
|
29
33
|
)
|
30
34
|
|
31
|
-
|
35
|
+
cattr_accessor :wrapper_exceptions, default: [
|
36
|
+
"ActionView::Template::Error"
|
37
|
+
]
|
38
|
+
|
39
|
+
cattr_accessor :silent_exceptions, default: [
|
40
|
+
"ActionController::RoutingError",
|
41
|
+
"ActionDispatch::Http::MimeNegotiation::InvalidType"
|
42
|
+
]
|
43
|
+
|
44
|
+
attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file
|
32
45
|
|
33
|
-
def initialize(
|
34
|
-
@
|
35
|
-
@exception =
|
46
|
+
def initialize(backtrace_cleaner, exception)
|
47
|
+
@backtrace_cleaner = backtrace_cleaner
|
48
|
+
@exception = exception
|
49
|
+
@exception_class_name = @exception.class.name
|
50
|
+
@wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
|
36
51
|
|
37
|
-
expand_backtrace if exception.is_a?(SyntaxError) || exception.
|
52
|
+
expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
|
53
|
+
end
|
54
|
+
|
55
|
+
def unwrapped_exception
|
56
|
+
if wrapper_exceptions.include?(@exception_class_name)
|
57
|
+
exception.cause
|
58
|
+
else
|
59
|
+
exception
|
60
|
+
end
|
38
61
|
end
|
39
62
|
|
40
63
|
def rescue_template
|
41
|
-
@@rescue_templates[@
|
64
|
+
@@rescue_templates[@exception_class_name]
|
42
65
|
end
|
43
66
|
|
44
67
|
def status_code
|
45
|
-
self.class.status_code_for_exception(
|
68
|
+
self.class.status_code_for_exception(unwrapped_exception.class.name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def exception_trace
|
72
|
+
trace = application_trace
|
73
|
+
trace = framework_trace if trace.empty? && !silent_exceptions.include?(@exception_class_name)
|
74
|
+
trace
|
46
75
|
end
|
47
76
|
|
48
77
|
def application_trace
|
@@ -58,15 +87,19 @@ module ActionDispatch
|
|
58
87
|
end
|
59
88
|
|
60
89
|
def traces
|
61
|
-
|
90
|
+
application_trace_with_ids = []
|
62
91
|
framework_trace_with_ids = []
|
63
92
|
full_trace_with_ids = []
|
64
93
|
|
65
94
|
full_trace.each_with_index do |trace, idx|
|
66
|
-
trace_with_id = {
|
95
|
+
trace_with_id = {
|
96
|
+
exception_object_id: @exception.object_id,
|
97
|
+
id: idx,
|
98
|
+
trace: trace
|
99
|
+
}
|
67
100
|
|
68
101
|
if application_trace.include?(trace)
|
69
|
-
|
102
|
+
application_trace_with_ids << trace_with_id
|
70
103
|
else
|
71
104
|
framework_trace_with_ids << trace_with_id
|
72
105
|
end
|
@@ -75,7 +108,7 @@ module ActionDispatch
|
|
75
108
|
end
|
76
109
|
|
77
110
|
{
|
78
|
-
"Application Trace" =>
|
111
|
+
"Application Trace" => application_trace_with_ids,
|
79
112
|
"Framework Trace" => framework_trace_with_ids,
|
80
113
|
"Full Trace" => full_trace_with_ids
|
81
114
|
}
|
@@ -87,8 +120,7 @@ module ActionDispatch
|
|
87
120
|
|
88
121
|
def source_extracts
|
89
122
|
backtrace.map do |trace|
|
90
|
-
file,
|
91
|
-
line_number = line.to_i
|
123
|
+
file, line_number = extract_file_and_line_number(trace)
|
92
124
|
|
93
125
|
{
|
94
126
|
code: source_fragment(file, line_number),
|
@@ -97,52 +129,64 @@ module ActionDispatch
|
|
97
129
|
end
|
98
130
|
end
|
99
131
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
Array(@exception.backtrace)
|
104
|
-
end
|
105
|
-
|
106
|
-
def original_exception(exception)
|
107
|
-
if registered_original_exception?(exception)
|
108
|
-
exception.original_exception
|
132
|
+
def trace_to_show
|
133
|
+
if traces["Application Trace"].empty? && rescue_template != "routing_error"
|
134
|
+
"Full Trace"
|
109
135
|
else
|
110
|
-
|
136
|
+
"Application Trace"
|
111
137
|
end
|
112
138
|
end
|
113
139
|
|
114
|
-
def
|
115
|
-
|
140
|
+
def source_to_show_id
|
141
|
+
(traces[trace_to_show].first || {})[:id]
|
116
142
|
end
|
117
143
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
else
|
122
|
-
backtrace
|
144
|
+
private
|
145
|
+
def backtrace
|
146
|
+
Array(@exception.backtrace)
|
123
147
|
end
|
124
|
-
end
|
125
148
|
|
126
|
-
|
127
|
-
|
128
|
-
|
149
|
+
def causes_for(exception)
|
150
|
+
return enum_for(__method__, exception) unless block_given?
|
151
|
+
|
152
|
+
yield exception while exception = exception.cause
|
153
|
+
end
|
154
|
+
|
155
|
+
def wrapped_causes_for(exception, backtrace_cleaner)
|
156
|
+
causes_for(exception).map { |cause| self.class.new(backtrace_cleaner, cause) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def clean_backtrace(*args)
|
160
|
+
if backtrace_cleaner
|
161
|
+
backtrace_cleaner.clean(backtrace, *args)
|
162
|
+
else
|
163
|
+
backtrace
|
164
|
+
end
|
165
|
+
end
|
129
166
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
167
|
+
def source_fragment(path, line)
|
168
|
+
return unless Rails.respond_to?(:root) && Rails.root
|
169
|
+
full_path = Rails.root.join(path)
|
170
|
+
if File.exist?(full_path)
|
171
|
+
File.open(full_path, "r") do |file|
|
172
|
+
start = [line - 3, 0].max
|
173
|
+
lines = file.each_line.drop(start).take(6)
|
174
|
+
Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
|
175
|
+
end
|
138
176
|
end
|
139
177
|
end
|
140
|
-
end
|
141
178
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
179
|
+
def extract_file_and_line_number(trace)
|
180
|
+
# Split by the first colon followed by some digits, which works for both
|
181
|
+
# Windows and Unix path styles.
|
182
|
+
file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
|
183
|
+
[file, line.to_i]
|
184
|
+
end
|
185
|
+
|
186
|
+
def expand_backtrace
|
187
|
+
@exception.backtrace.unshift(
|
188
|
+
@exception.to_s.split("\n")
|
189
|
+
).flatten!
|
190
|
+
end
|
147
191
|
end
|
148
192
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/body_proxy"
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
class Executor
|
7
|
+
def initialize(app, executor)
|
8
|
+
@app, @executor = app, executor
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
state = @executor.run!
|
13
|
+
begin
|
14
|
+
response = @app.call(env)
|
15
|
+
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
16
|
+
ensure
|
17
|
+
state.complete! unless returned
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,15 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
class Request < Rack::Request
|
5
|
-
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
|
6
|
-
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
|
7
|
-
# to put a new one.
|
8
|
-
def flash
|
9
|
-
@env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
|
10
|
-
end
|
11
|
-
end
|
3
|
+
require "active_support/core_ext/hash/keys"
|
12
4
|
|
5
|
+
module ActionDispatch
|
13
6
|
# The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
|
14
7
|
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
|
15
8
|
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
|
@@ -45,7 +38,46 @@ module ActionDispatch
|
|
45
38
|
#
|
46
39
|
# See docs on the FlashHash class for more details about the flash.
|
47
40
|
class Flash
|
48
|
-
KEY =
|
41
|
+
KEY = "action_dispatch.request.flash_hash"
|
42
|
+
|
43
|
+
module RequestMethods
|
44
|
+
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
|
45
|
+
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
|
46
|
+
# to put a new one.
|
47
|
+
def flash
|
48
|
+
flash = flash_hash
|
49
|
+
return flash if flash
|
50
|
+
self.flash = Flash::FlashHash.from_session_value(session["flash"])
|
51
|
+
end
|
52
|
+
|
53
|
+
def flash=(flash)
|
54
|
+
set_header Flash::KEY, flash
|
55
|
+
end
|
56
|
+
|
57
|
+
def flash_hash # :nodoc:
|
58
|
+
get_header Flash::KEY
|
59
|
+
end
|
60
|
+
|
61
|
+
def commit_flash # :nodoc:
|
62
|
+
session = self.session || {}
|
63
|
+
flash_hash = self.flash_hash
|
64
|
+
|
65
|
+
if flash_hash && (flash_hash.present? || session.key?("flash"))
|
66
|
+
session["flash"] = flash_hash.to_session_value
|
67
|
+
self.flash = flash_hash.dup
|
68
|
+
end
|
69
|
+
|
70
|
+
if (!session.respond_to?(:loaded?) || session.loaded?) && # reset_session uses {}, which doesn't implement #loaded?
|
71
|
+
session.key?("flash") && session["flash"].nil?
|
72
|
+
session.delete("flash")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def reset_session # :nodoc:
|
77
|
+
super
|
78
|
+
self.flash = nil
|
79
|
+
end
|
80
|
+
end
|
49
81
|
|
50
82
|
class FlashNow #:nodoc:
|
51
83
|
attr_accessor :flash
|
@@ -80,24 +112,30 @@ module ActionDispatch
|
|
80
112
|
include Enumerable
|
81
113
|
|
82
114
|
def self.from_session_value(value) #:nodoc:
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
115
|
+
case value
|
116
|
+
when FlashHash # Rails 3.1, 3.2
|
117
|
+
flashes = value.instance_variable_get(:@flashes)
|
118
|
+
if discard = value.instance_variable_get(:@used)
|
119
|
+
flashes.except!(*discard)
|
120
|
+
end
|
121
|
+
new(flashes, flashes.keys)
|
122
|
+
when Hash # Rails 4.0
|
123
|
+
flashes = value["flashes"]
|
124
|
+
if discard = value["discard"]
|
125
|
+
flashes.except!(*discard)
|
126
|
+
end
|
127
|
+
new(flashes, flashes.keys)
|
128
|
+
else
|
129
|
+
new
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Builds a hash containing the flashes to keep for the next request.
|
134
|
+
# If there are none to keep, returns +nil+.
|
98
135
|
def to_session_value #:nodoc:
|
99
|
-
|
100
|
-
|
136
|
+
flashes_to_keep = @flashes.except(*@discard)
|
137
|
+
return nil if flashes_to_keep.empty?
|
138
|
+
{ "discard" => [], "flashes" => flashes_to_keep }
|
101
139
|
end
|
102
140
|
|
103
141
|
def initialize(flashes = {}, discard = []) #:nodoc:
|
@@ -241,36 +279,22 @@ module ActionDispatch
|
|
241
279
|
end
|
242
280
|
|
243
281
|
protected
|
244
|
-
|
245
|
-
|
246
|
-
end
|
247
|
-
|
248
|
-
def stringify_array(array)
|
249
|
-
array.map do |item|
|
250
|
-
item.kind_of?(Symbol) ? item.to_s : item
|
282
|
+
def now_is_loaded?
|
283
|
+
@now
|
251
284
|
end
|
252
|
-
end
|
253
|
-
end
|
254
285
|
|
255
|
-
|
256
|
-
|
286
|
+
private
|
287
|
+
def stringify_array(array) # :doc:
|
288
|
+
array.map do |item|
|
289
|
+
item.kind_of?(Symbol) ? item.to_s : item
|
290
|
+
end
|
291
|
+
end
|
257
292
|
end
|
258
293
|
|
259
|
-
def
|
260
|
-
|
261
|
-
ensure
|
262
|
-
session = Request::Session.find(env) || {}
|
263
|
-
flash_hash = env[KEY]
|
264
|
-
|
265
|
-
if flash_hash && (flash_hash.present? || session.key?('flash'))
|
266
|
-
session["flash"] = flash_hash.to_session_value
|
267
|
-
env[KEY] = flash_hash.dup
|
268
|
-
end
|
294
|
+
def self.new(app) app; end
|
295
|
+
end
|
269
296
|
|
270
|
-
|
271
|
-
|
272
|
-
session.delete('flash')
|
273
|
-
end
|
274
|
-
end
|
297
|
+
class Request
|
298
|
+
prepend Flash::RequestMethods
|
275
299
|
end
|
276
300
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_dispatch/http/request"
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
# This middleware guards from DNS rebinding attacks by explicitly permitting
|
7
|
+
# the hosts a request can be sent to, and is passed the options set in
|
8
|
+
# +config.host_authorization+.
|
9
|
+
#
|
10
|
+
# Requests can opt-out of Host Authorization with +exclude+:
|
11
|
+
#
|
12
|
+
# config.host_authorization = { exclude: ->(request) { request.path =~ /healthcheck/ } }
|
13
|
+
#
|
14
|
+
# When a request comes to an unauthorized host, the +response_app+
|
15
|
+
# application will be executed and rendered. If no +response_app+ is given, a
|
16
|
+
# default one will run, which responds with <tt>403 Forbidden</tt>.
|
17
|
+
class HostAuthorization
|
18
|
+
class Permissions # :nodoc:
|
19
|
+
def initialize(hosts)
|
20
|
+
@hosts = sanitize_hosts(hosts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty?
|
24
|
+
@hosts.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def allows?(host)
|
28
|
+
@hosts.any? do |allowed|
|
29
|
+
allowed === host
|
30
|
+
rescue
|
31
|
+
# IPAddr#=== raises an error if you give it a hostname instead of
|
32
|
+
# IP. Treat similar errors as blocked access.
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def sanitize_hosts(hosts)
|
39
|
+
Array(hosts).map do |host|
|
40
|
+
case host
|
41
|
+
when Regexp then sanitize_regexp(host)
|
42
|
+
when String then sanitize_string(host)
|
43
|
+
else host
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def sanitize_regexp(host)
|
49
|
+
/\A#{host}\z/
|
50
|
+
end
|
51
|
+
|
52
|
+
def sanitize_string(host)
|
53
|
+
if host.start_with?(".")
|
54
|
+
/\A(.+\.)?#{Regexp.escape(host[1..-1])}\z/i
|
55
|
+
else
|
56
|
+
/\A#{Regexp.escape host}\z/i
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
DEFAULT_RESPONSE_APP = -> env do
|
62
|
+
request = Request.new(env)
|
63
|
+
|
64
|
+
format = request.xhr? ? "text/plain" : "text/html"
|
65
|
+
template = DebugView.new(host: request.host)
|
66
|
+
body = template.render(template: "rescues/blocked_host", layout: "rescues/layout")
|
67
|
+
|
68
|
+
[403, {
|
69
|
+
"Content-Type" => "#{format}; charset=#{Response.default_charset}",
|
70
|
+
"Content-Length" => body.bytesize.to_s,
|
71
|
+
}, [body]]
|
72
|
+
end
|
73
|
+
|
74
|
+
def initialize(app, hosts, deprecated_response_app = nil, exclude: nil, response_app: nil)
|
75
|
+
@app = app
|
76
|
+
@permissions = Permissions.new(hosts)
|
77
|
+
@exclude = exclude
|
78
|
+
|
79
|
+
unless deprecated_response_app.nil?
|
80
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
81
|
+
`action_dispatch.hosts_response_app` is deprecated and will be ignored in Rails 6.2.
|
82
|
+
Use the Host Authorization `response_app` setting instead.
|
83
|
+
MSG
|
84
|
+
|
85
|
+
response_app ||= deprecated_response_app
|
86
|
+
end
|
87
|
+
|
88
|
+
@response_app = response_app || DEFAULT_RESPONSE_APP
|
89
|
+
end
|
90
|
+
|
91
|
+
def call(env)
|
92
|
+
return @app.call(env) if @permissions.empty?
|
93
|
+
|
94
|
+
request = Request.new(env)
|
95
|
+
|
96
|
+
if authorized?(request) || excluded?(request)
|
97
|
+
mark_as_authorized(request)
|
98
|
+
@app.call(env)
|
99
|
+
else
|
100
|
+
@response_app.call(env)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
def authorized?(request)
|
106
|
+
valid_host = /
|
107
|
+
\A
|
108
|
+
(?<host>[a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9.:]+\])
|
109
|
+
(:\d+)?
|
110
|
+
\z
|
111
|
+
/x
|
112
|
+
|
113
|
+
origin_host = valid_host.match(
|
114
|
+
request.get_header("HTTP_HOST").to_s.downcase)
|
115
|
+
forwarded_host = valid_host.match(
|
116
|
+
request.x_forwarded_host.to_s.split(/,\s?/).last)
|
117
|
+
|
118
|
+
origin_host && @permissions.allows?(origin_host[:host]) && (
|
119
|
+
forwarded_host.nil? || @permissions.allows?(forwarded_host[:host]))
|
120
|
+
end
|
121
|
+
|
122
|
+
def excluded?(request)
|
123
|
+
@exclude && @exclude.call(request)
|
124
|
+
end
|
125
|
+
|
126
|
+
def mark_as_authorized(request)
|
127
|
+
request.set_header("action_dispatch.authorized_host", request.host)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
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.
|
@@ -17,39 +19,42 @@ module ActionDispatch
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def call(env)
|
20
|
-
status = env["PATH_INFO"][1..-1]
|
21
22
|
request = ActionDispatch::Request.new(env)
|
22
|
-
|
23
|
-
|
23
|
+
status = request.path_info[1..-1].to_i
|
24
|
+
begin
|
25
|
+
content_type = request.formats.first
|
26
|
+
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
27
|
+
content_type = Mime[:text]
|
28
|
+
end
|
29
|
+
body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
|
24
30
|
|
25
31
|
render(status, content_type, body)
|
26
32
|
end
|
27
33
|
|
28
34
|
private
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
def render(status, content_type, body)
|
36
|
+
format = "to_#{content_type.to_sym}" if content_type
|
37
|
+
if format && body.respond_to?(format)
|
38
|
+
render_format(status, content_type, body.public_send(format))
|
39
|
+
else
|
40
|
+
render_html(status)
|
41
|
+
end
|
36
42
|
end
|
37
|
-
end
|
38
43
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
def render_format(status, content_type, body)
|
45
|
+
[status, { "Content-Type" => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
|
46
|
+
"Content-Length" => body.bytesize.to_s }, [body]]
|
47
|
+
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
def render_html(status)
|
50
|
+
path = "#{public_path}/#{status}.#{I18n.locale}.html"
|
51
|
+
path = "#{public_path}/#{status}.html" unless (found = File.exist?(path))
|
47
52
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
53
|
+
if found || File.exist?(path)
|
54
|
+
render_format(status, "text/html", File.read(path))
|
55
|
+
else
|
56
|
+
[404, { "X-Cascade" => "pass" }, []]
|
57
|
+
end
|
52
58
|
end
|
53
|
-
end
|
54
59
|
end
|
55
60
|
end
|