actionpack 7.1.3 → 7.2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -501
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +102 -98
- data/lib/abstract_controller/caching/fragments.rb +50 -53
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +66 -64
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +70 -85
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +13 -12
- data/lib/abstract_controller/translation.rb +15 -7
- data/lib/abstract_controller/url_for.rb +8 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +74 -72
- data/lib/action_controller/base.rb +198 -126
- data/lib/action_controller/caching.rb +15 -12
- data/lib/action_controller/deprecator.rb +2 -0
- data/lib/action_controller/form_builder.rb +20 -17
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +188 -174
- data/lib/action_controller/metal/content_security_policy.rb +25 -24
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +64 -55
- data/lib/action_controller/metal/default_headers.rb +5 -3
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
- data/lib/action_controller/metal/exceptions.rb +11 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +12 -10
- data/lib/action_controller/metal/helpers.rb +63 -55
- data/lib/action_controller/metal/http_authentication.rb +210 -205
- data/lib/action_controller/metal/implicit_render.rb +17 -15
- data/lib/action_controller/metal/instrumentation.rb +15 -12
- data/lib/action_controller/metal/live.rb +113 -107
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +57 -59
- data/lib/action_controller/metal/permissions_policy.rb +13 -12
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +108 -82
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -75
- data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
- data/lib/action_controller/metal/rescue.rb +11 -9
- data/lib/action_controller/metal/streaming.rb +138 -136
- data/lib/action_controller/metal/strong_parameters.rb +525 -480
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +17 -15
- data/lib/action_controller/metal.rb +86 -60
- data/lib/action_controller/railtie.rb +3 -0
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +42 -36
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +146 -126
- data/lib/action_controller.rb +10 -3
- data/lib/action_dispatch/constants.rb +2 -0
- data/lib/action_dispatch/deprecator.rb +2 -0
- data/lib/action_dispatch/http/cache.rb +27 -26
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +44 -38
- data/lib/action_dispatch/http/filter_parameters.rb +18 -9
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +22 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +30 -41
- data/lib/action_dispatch/http/mime_type.rb +31 -24
- data/lib/action_dispatch/http/mime_types.rb +2 -0
- data/lib/action_dispatch/http/parameters.rb +11 -9
- data/lib/action_dispatch/http/permissions_policy.rb +20 -44
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +94 -75
- data/lib/action_dispatch/http/response.rb +73 -61
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +13 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- 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 +9 -7
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +4 -2
- data/lib/action_dispatch/journey/routes.rb +4 -2
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +2 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
- data/lib/action_dispatch/middleware/callbacks.rb +3 -1
- data/lib/action_dispatch/middleware/cookies.rb +119 -104
- data/lib/action_dispatch/middleware/debug_exceptions.rb +13 -5
- data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
- data/lib/action_dispatch/middleware/debug_view.rb +2 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +6 -11
- data/lib/action_dispatch/middleware/executor.rb +8 -0
- data/lib/action_dispatch/middleware/flash.rb +63 -51
- data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
- data/lib/action_dispatch/middleware/public_exceptions.rb +8 -6
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +77 -72
- data/lib/action_dispatch/middleware/request_id.rb +14 -9
- data/lib/action_dispatch/middleware/server_timing.rb +4 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
- data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +31 -21
- data/lib/action_dispatch/middleware/ssl.rb +43 -40
- data/lib/action_dispatch/middleware/stack.rb +11 -10
- data/lib/action_dispatch/middleware/static.rb +33 -31
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +2 -4
- data/lib/action_dispatch/request/session.rb +23 -21
- data/lib/action_dispatch/request/utils.rb +2 -0
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +5 -3
- data/lib/action_dispatch/routing/mapper.rb +671 -636
- data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
- data/lib/action_dispatch/routing/redirection.rb +37 -32
- data/lib/action_dispatch/routing/route_set.rb +59 -45
- data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
- data/lib/action_dispatch/routing/url_for.rb +130 -125
- data/lib/action_dispatch/routing.rb +150 -148
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +10 -3
- data/lib/action_dispatch/system_testing/driver.rb +3 -1
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +8 -6
- data/lib/action_dispatch/testing/assertions/response.rb +26 -23
- data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/integration.rb +223 -222
- data/lib/action_dispatch/testing/request_encoder.rb +2 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +12 -8
- data/lib/action_dispatch/testing/test_request.rb +3 -1
- data/lib/action_dispatch/testing/test_response.rb +27 -26
- data/lib/action_dispatch.rb +22 -28
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +17 -16
- metadata +39 -16
@@ -1,41 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/hash/keys"
|
4
6
|
|
5
7
|
module ActionDispatch
|
6
|
-
#
|
8
|
+
# # Action Dispatch Flash
|
7
9
|
#
|
8
|
-
# The flash provides a way to pass temporary primitive-types (String, Array,
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# The flash provides a way to pass temporary primitive-types (String, Array,
|
11
|
+
# Hash) between actions. Anything you place in the flash will be exposed to the
|
12
|
+
# very next action and then cleared out. This is a great way of doing notices
|
13
|
+
# and alerts, such as a create action that sets `flash[:notice] = "Post
|
14
|
+
# successfully created"` before redirecting to a display action that can then
|
15
|
+
# expose the flash to its template. Actually, that exposure is automatically
|
16
|
+
# done.
|
12
17
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
18
|
+
# class PostsController < ActionController::Base
|
19
|
+
# def create
|
20
|
+
# # save post
|
21
|
+
# flash[:notice] = "Post successfully created"
|
22
|
+
# redirect_to @post
|
23
|
+
# end
|
19
24
|
#
|
20
|
-
#
|
21
|
-
#
|
25
|
+
# def show
|
26
|
+
# # doesn't need to assign the flash notice to the template, that's done automatically
|
27
|
+
# end
|
22
28
|
# end
|
23
|
-
# end
|
24
29
|
#
|
25
|
-
# Then in
|
30
|
+
# Then in `show.html.erb`:
|
26
31
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
32
|
+
# <% if flash[:notice] %>
|
33
|
+
# <div class="notice"><%= flash[:notice] %></div>
|
34
|
+
# <% end %>
|
30
35
|
#
|
31
|
-
# Since the
|
36
|
+
# Since the `notice` and `alert` keys are a common idiom, convenience accessors
|
37
|
+
# are available:
|
32
38
|
#
|
33
|
-
#
|
34
|
-
#
|
39
|
+
# flash.alert = "You must be logged in"
|
40
|
+
# flash.notice = "Post successfully created"
|
35
41
|
#
|
36
|
-
# This example places a string in the flash. And of course, you can put as many
|
37
|
-
#
|
38
|
-
#
|
42
|
+
# This example places a string in the flash. And of course, you can put as many
|
43
|
+
# as you like at a time too. If you want to pass non-primitive types, you will
|
44
|
+
# have to handle that in your application. Example: To show messages with links,
|
45
|
+
# you will have to use sanitize helper.
|
39
46
|
#
|
40
47
|
# Just remember: They'll be gone by the time the next action has been performed.
|
41
48
|
#
|
@@ -98,12 +105,12 @@ module ActionDispatch
|
|
98
105
|
@flash[k.to_s]
|
99
106
|
end
|
100
107
|
|
101
|
-
# Convenience accessor for
|
108
|
+
# Convenience accessor for `flash.now[:alert]=`.
|
102
109
|
def alert=(message)
|
103
110
|
self[:alert] = message
|
104
111
|
end
|
105
112
|
|
106
|
-
# Convenience accessor for
|
113
|
+
# Convenience accessor for `flash.now[:notice]=`.
|
107
114
|
def notice=(message)
|
108
115
|
self[:notice] = message
|
109
116
|
end
|
@@ -131,8 +138,8 @@ module ActionDispatch
|
|
131
138
|
end
|
132
139
|
end
|
133
140
|
|
134
|
-
# Builds a hash containing the flashes to keep for the next request.
|
135
|
-
#
|
141
|
+
# Builds a hash containing the flashes to keep for the next request. If there
|
142
|
+
# are none to keep, returns `nil`.
|
136
143
|
def to_session_value # :nodoc:
|
137
144
|
flashes_to_keep = @flashes.except(*@discard)
|
138
145
|
return nil if flashes_to_keep.empty?
|
@@ -177,8 +184,8 @@ module ActionDispatch
|
|
177
184
|
@flashes.key? name.to_s
|
178
185
|
end
|
179
186
|
|
180
|
-
# Immediately deletes the single flash entry. Use this method when you
|
181
|
-
#
|
187
|
+
# Immediately deletes the single flash entry. Use this method when you want
|
188
|
+
# remove the message within the current action. See also #discard.
|
182
189
|
def delete(key)
|
183
190
|
key = key.to_s
|
184
191
|
@discard.delete key
|
@@ -211,45 +218,49 @@ module ActionDispatch
|
|
211
218
|
self
|
212
219
|
end
|
213
220
|
|
214
|
-
# Sets a flash that will not be available to the next action, only to the
|
221
|
+
# Sets a flash that will not be available to the next action, only to the
|
222
|
+
# current.
|
215
223
|
#
|
216
224
|
# flash.now[:message] = "Hello current action"
|
217
225
|
#
|
218
|
-
# This method enables you to use the flash as a central messaging system in your
|
219
|
-
# When you need to pass an object to the next action, you use the standard
|
220
|
-
# When you need to pass an object to the current action,
|
221
|
-
# vanish when the current action is done.
|
226
|
+
# This method enables you to use the flash as a central messaging system in your
|
227
|
+
# app. When you need to pass an object to the next action, you use the standard
|
228
|
+
# flash assign (`[]=`). When you need to pass an object to the current action,
|
229
|
+
# you use `now`, and your object will vanish when the current action is done.
|
222
230
|
#
|
223
|
-
# Entries set via
|
231
|
+
# Entries set via `now` are accessed the same way as standard entries:
|
232
|
+
# `flash['my-key']`.
|
224
233
|
#
|
225
234
|
# Also, brings two convenience accessors:
|
226
235
|
#
|
227
|
-
#
|
228
|
-
#
|
236
|
+
# flash.now.alert = "Beware now!"
|
237
|
+
# # Equivalent to flash.now[:alert] = "Beware now!"
|
229
238
|
#
|
230
|
-
#
|
231
|
-
#
|
239
|
+
# flash.now.notice = "Good luck now!"
|
240
|
+
# # Equivalent to flash.now[:notice] = "Good luck now!"
|
232
241
|
def now
|
233
242
|
@now ||= FlashNow.new(self)
|
234
243
|
end
|
235
244
|
|
236
|
-
# Keeps either the entire current flash or a specific flash entry available for
|
245
|
+
# Keeps either the entire current flash or a specific flash entry available for
|
246
|
+
# the next action:
|
237
247
|
#
|
238
|
-
#
|
239
|
-
#
|
248
|
+
# flash.keep # keeps the entire flash
|
249
|
+
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
|
240
250
|
def keep(k = nil)
|
241
251
|
k = k.to_s if k
|
242
252
|
@discard.subtract Array(k || keys)
|
243
253
|
k ? self[k] : self
|
244
254
|
end
|
245
255
|
|
246
|
-
# Marks the entire flash or a single flash entry to be discarded by the end of
|
256
|
+
# Marks the entire flash or a single flash entry to be discarded by the end of
|
257
|
+
# the current action:
|
247
258
|
#
|
248
259
|
# flash.discard # discard the entire flash at the end of the current action
|
249
260
|
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
|
250
261
|
#
|
251
|
-
# Use this method when you want to display the message in the current
|
252
|
-
#
|
262
|
+
# Use this method when you want to display the message in the current action but
|
263
|
+
# not in the next one. See also #delete.
|
253
264
|
def discard(k = nil)
|
254
265
|
k = k.to_s if k
|
255
266
|
@discard.merge Array(k || keys)
|
@@ -258,28 +269,29 @@ module ActionDispatch
|
|
258
269
|
|
259
270
|
# Mark for removal entries that were kept, and delete unkept ones.
|
260
271
|
#
|
261
|
-
# This method is called automatically by filters, so you generally don't need to
|
272
|
+
# This method is called automatically by filters, so you generally don't need to
|
273
|
+
# care about it.
|
262
274
|
def sweep # :nodoc:
|
263
275
|
@discard.each { |k| @flashes.delete k }
|
264
276
|
@discard.replace @flashes.keys
|
265
277
|
end
|
266
278
|
|
267
|
-
# Convenience accessor for
|
279
|
+
# Convenience accessor for `flash[:alert]`.
|
268
280
|
def alert
|
269
281
|
self[:alert]
|
270
282
|
end
|
271
283
|
|
272
|
-
# Convenience accessor for
|
284
|
+
# Convenience accessor for `flash[:alert]=`.
|
273
285
|
def alert=(message)
|
274
286
|
self[:alert] = message
|
275
287
|
end
|
276
288
|
|
277
|
-
# Convenience accessor for
|
289
|
+
# Convenience accessor for `flash[:notice]`.
|
278
290
|
def notice
|
279
291
|
self[:notice]
|
280
292
|
end
|
281
293
|
|
282
|
-
# Convenience accessor for
|
294
|
+
# Convenience accessor for `flash[:notice]=`.
|
283
295
|
def notice=(message)
|
284
296
|
self[:notice] = message
|
285
297
|
end
|
@@ -1,24 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
|
-
#
|
6
|
+
# # Action Dispatch HostAuthorization
|
5
7
|
#
|
6
|
-
# This middleware guards from DNS rebinding attacks by explicitly permitting
|
7
|
-
#
|
8
|
-
#
|
8
|
+
# This middleware guards from DNS rebinding attacks by explicitly permitting the
|
9
|
+
# hosts a request can be sent to, and is passed the options set in
|
10
|
+
# `config.host_authorization`.
|
9
11
|
#
|
10
|
-
# Requests can opt-out of Host Authorization with
|
12
|
+
# Requests can opt-out of Host Authorization with `exclude`:
|
11
13
|
#
|
12
|
-
#
|
14
|
+
# config.host_authorization = { exclude: ->(request) { request.path =~ /healthcheck/ } }
|
13
15
|
#
|
14
|
-
# When a request comes to an unauthorized host, the
|
15
|
-
#
|
16
|
-
# default
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
16
|
+
# When a request comes to an unauthorized host, the `response_app` application
|
17
|
+
# will be executed and rendered. If no `response_app` is given, a default one
|
18
|
+
# will run. The default response app logs blocked host info with level 'error'
|
19
|
+
# and responds with `403 Forbidden`. The body of the response contains debug
|
20
|
+
# info if `config.consider_all_requests_local` is set to true, otherwise the
|
21
|
+
# body is empty.
|
20
22
|
class HostAuthorization
|
21
|
-
ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
|
23
|
+
ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", ".test", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
|
22
24
|
PORT_REGEX = /(?::\d+)/ # :nodoc:
|
23
25
|
SUBDOMAIN_REGEX = /(?:[a-z0-9-]+\.)/i # :nodoc:
|
24
26
|
IPV4_HOSTNAME = /(?<host>\d+\.\d+\.\d+\.\d+)#{PORT_REGEX}?/ # :nodoc:
|
@@ -45,8 +47,8 @@ module ActionDispatch
|
|
45
47
|
begin
|
46
48
|
allowed === extract_hostname(host)
|
47
49
|
rescue
|
48
|
-
# IPAddr#=== raises an error if you give it a hostname instead of
|
49
|
-
#
|
50
|
+
# IPAddr#=== raises an error if you give it a hostname instead of IP. Treat
|
51
|
+
# similar errors as blocked access.
|
50
52
|
false
|
51
53
|
end
|
52
54
|
else
|
@@ -1,15 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
|
-
#
|
6
|
+
# # Action Dispatch PublicExceptions
|
5
7
|
#
|
6
8
|
# When called, this middleware renders an error page. By default if an HTML
|
7
|
-
# response is expected it will render static error pages from the
|
9
|
+
# response is expected it will render static error pages from the `/public`
|
8
10
|
# directory. For example when this middleware receives a 500 response it will
|
9
|
-
# render the template found in
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# render the template found in `/public/500.html`. If an internationalized
|
12
|
+
# locale is set, this middleware will attempt to render the template in
|
13
|
+
# `/public/500.<locale>.html`. If an internationalized template is not found it
|
14
|
+
# will fall back on `/public/500.html`.
|
13
15
|
#
|
14
16
|
# When a request with a content type other than HTML is made, this middleware
|
15
17
|
# will attempt to convert error information into the appropriate response type.
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionDispatch
|
4
|
-
#
|
6
|
+
# # Action Dispatch Reloader
|
5
7
|
#
|
6
8
|
# ActionDispatch::Reloader wraps the request with callbacks provided by
|
7
9
|
# ActiveSupport::Reloader, intended to assist with code reloading during
|
8
10
|
# development.
|
9
11
|
#
|
10
|
-
# ActionDispatch::Reloader is included in the middleware stack only if
|
11
|
-
#
|
12
|
+
# ActionDispatch::Reloader is included in the middleware stack only if reloading
|
13
|
+
# is enabled, which it is by the default in `development` mode.
|
12
14
|
class Reloader < Executor
|
13
15
|
end
|
14
16
|
end
|
@@ -1,37 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "ipaddr"
|
4
6
|
|
5
7
|
module ActionDispatch
|
6
|
-
#
|
8
|
+
# # Action Dispatch RemoteIp
|
7
9
|
#
|
8
|
-
# This middleware calculates the IP address of the remote client that is
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
10
|
+
# This middleware calculates the IP address of the remote client that is making
|
11
|
+
# the request. It does this by checking various headers that could contain the
|
12
|
+
# address, and then picking the last-set address that is not on the list of
|
13
|
+
# trusted IPs. This follows the precedent set by e.g. [the Tomcat
|
14
|
+
# server](https://issues.apache.org/bugzilla/show_bug.cgi?id=50453). A more
|
15
|
+
# detailed explanation of the algorithm is given at GetIp#calculate_ip.
|
14
16
|
#
|
15
|
-
# Some Rack servers concatenate repeated headers, like
|
16
|
-
# requires.
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
17
|
+
# Some Rack servers concatenate repeated headers, like [HTTP RFC
|
18
|
+
# 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2) requires.
|
19
|
+
# Some Rack servers simply drop preceding headers, and only report the value
|
20
|
+
# that was [given in the last
|
21
|
+
# header](https://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-server
|
22
|
+
# s). If you are behind multiple proxy servers (like NGINX to HAProxy to
|
23
|
+
# Unicorn) then you should test your Rack server to make sure your data is good.
|
20
24
|
#
|
21
|
-
# IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
25
|
+
# IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING. This
|
26
|
+
# middleware assumes that there is at least one proxy sitting around and setting
|
27
|
+
# headers with the client's remote IP address. If you don't use a proxy, because
|
28
|
+
# you are hosted on e.g. Heroku without SSL, any client can claim to have any IP
|
29
|
+
# address by setting the `X-Forwarded-For` header. If you care about that, then
|
30
|
+
# you need to explicitly drop or ignore those headers sometime before this
|
31
|
+
# middleware runs. Alternatively, remove this middleware to avoid inadvertently
|
32
|
+
# relying on it.
|
29
33
|
class RemoteIp
|
30
34
|
class IpSpoofAttackError < StandardError; end
|
31
35
|
|
32
|
-
# The default trusted IPs list simply includes IP addresses that are
|
33
|
-
#
|
34
|
-
#
|
36
|
+
# The default trusted IPs list simply includes IP addresses that are guaranteed
|
37
|
+
# by the IP specification to be private addresses. Those will not be the
|
38
|
+
# ultimate client IP in production, and so are discarded. See
|
35
39
|
# https://en.wikipedia.org/wiki/Private_network for details.
|
36
40
|
TRUSTED_PROXIES = [
|
37
41
|
"127.0.0.0/8", # localhost IPv4 range, per RFC-3330
|
@@ -44,20 +48,20 @@ module ActionDispatch
|
|
44
48
|
|
45
49
|
attr_reader :check_ip, :proxies
|
46
50
|
|
47
|
-
# Create a new
|
51
|
+
# Create a new `RemoteIp` middleware instance.
|
48
52
|
#
|
49
|
-
# The
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
53
|
+
# The `ip_spoofing_check` option is on by default. When on, an exception is
|
54
|
+
# raised if it looks like the client is trying to lie about its own IP address.
|
55
|
+
# It makes sense to turn off this check on sites aimed at non-IP clients (like
|
56
|
+
# WAP devices), or behind proxies that set headers in an incorrect or confusing
|
57
|
+
# way (like AWS ELB).
|
54
58
|
#
|
55
|
-
# The
|
56
|
-
# instead of
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
59
|
+
# The `custom_proxies` argument can take an enumerable which will be used
|
60
|
+
# instead of `TRUSTED_PROXIES`. Any proxy setup will put the value you want in
|
61
|
+
# the middle (or at the beginning) of the `X-Forwarded-For` list, with your
|
62
|
+
# proxy servers after it. If your proxies aren't removed, pass them in via the
|
63
|
+
# `custom_proxies` parameter. That way, the middleware will ignore those IP
|
64
|
+
# addresses, and return the one that you want.
|
61
65
|
def initialize(app, ip_spoofing_check = true, custom_proxies = nil)
|
62
66
|
@app = app
|
63
67
|
@check_ip = ip_spoofing_check
|
@@ -82,19 +86,19 @@ module ActionDispatch
|
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
85
|
-
# Since the IP address may not be needed, we store the object here
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
+
# Since the IP address may not be needed, we store the object here without
|
90
|
+
# calculating the IP to keep from slowing down the majority of requests. For
|
91
|
+
# those requests that do need to know the IP, the GetIp#calculate_ip method will
|
92
|
+
# calculate the memoized client IP address.
|
89
93
|
def call(env)
|
90
94
|
req = ActionDispatch::Request.new env
|
91
95
|
req.remote_ip = GetIp.new(req, check_ip, proxies)
|
92
96
|
@app.call(req.env)
|
93
97
|
end
|
94
98
|
|
95
|
-
# The GetIp class exists as a way to defer processing of the request data
|
96
|
-
#
|
97
|
-
#
|
99
|
+
# The GetIp class exists as a way to defer processing of the request data into
|
100
|
+
# an actual IP address. If the ActionDispatch::Request#remote_ip method is
|
101
|
+
# called, this class will calculate the value and then memoize it.
|
98
102
|
class GetIp
|
99
103
|
def initialize(req, check_ip, proxies)
|
100
104
|
@req = req
|
@@ -102,24 +106,25 @@ module ActionDispatch
|
|
102
106
|
@proxies = proxies
|
103
107
|
end
|
104
108
|
|
105
|
-
# Sort through the various IP address headers, looking for the IP most
|
106
|
-
#
|
107
|
-
# request.
|
109
|
+
# Sort through the various IP address headers, looking for the IP most likely to
|
110
|
+
# be the address of the actual remote client making this request.
|
108
111
|
#
|
109
|
-
# REMOTE_ADDR will be correct if the request is made directly against the
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
112
|
+
# REMOTE_ADDR will be correct if the request is made directly against the Ruby
|
113
|
+
# process, on e.g. Heroku. When the request is proxied by another server like
|
114
|
+
# HAProxy or NGINX, the IP address that made the original request will be put in
|
115
|
+
# an `X-Forwarded-For` header. If there are multiple proxies, that header may
|
116
|
+
# contain a list of IPs. Other proxy services set the `Client-Ip` header
|
117
|
+
# instead, so we check that too.
|
115
118
|
#
|
116
|
-
# As discussed in
|
117
|
-
#
|
118
|
-
#
|
119
|
+
# As discussed in [this post about Rails IP
|
120
|
+
# Spoofing](https://web.archive.org/web/20170626095448/https://blog.gingerlime.c
|
121
|
+
# om/2012/rails-ip-spoofing-vulnerabilities-and-protection/), while the first IP
|
122
|
+
# in the list is likely to be the "originating" IP, it could also have been set
|
123
|
+
# by the client maliciously.
|
119
124
|
#
|
120
|
-
# In order to find the first address that is (probably) accurate, we
|
121
|
-
#
|
122
|
-
#
|
125
|
+
# In order to find the first address that is (probably) accurate, we take the
|
126
|
+
# list of IPs, remove known and trusted proxies, and then take the last address
|
127
|
+
# left, which was presumably set by one of those proxies.
|
123
128
|
def calculate_ip
|
124
129
|
# Set by the Rack web server, this is a single value.
|
125
130
|
remote_addr = ips_from(@req.remote_addr).last
|
@@ -128,19 +133,19 @@ module ActionDispatch
|
|
128
133
|
client_ips = ips_from(@req.client_ip).reverse!
|
129
134
|
forwarded_ips = ips_from(@req.x_forwarded_for).reverse!
|
130
135
|
|
131
|
-
#
|
132
|
-
#
|
136
|
+
# `Client-Ip` and `X-Forwarded-For` should not, generally, both be set. If they
|
137
|
+
# are both set, it means that either:
|
133
138
|
#
|
134
139
|
# 1) This request passed through two proxies with incompatible IP header
|
135
|
-
#
|
136
|
-
#
|
137
|
-
#
|
140
|
+
# conventions.
|
141
|
+
#
|
142
|
+
# 2) The client passed one of `Client-Ip` or `X-Forwarded-For`
|
143
|
+
# (whichever the proxy servers weren't using) themselves.
|
138
144
|
#
|
139
|
-
# Either way, there is no way for us to determine which header is the
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
# option off.)
|
145
|
+
# Either way, there is no way for us to determine which header is the right one
|
146
|
+
# after the fact. Since we have no idea, if we are concerned about IP spoofing
|
147
|
+
# we need to give up and explode. (If you're not concerned about IP spoofing you
|
148
|
+
# can turn the `ip_spoofing_check` option off.)
|
144
149
|
should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
|
145
150
|
if should_check_ip && !forwarded_ips.include?(client_ips.last)
|
146
151
|
# We don't know which came from the proxy, and which from the user
|
@@ -151,14 +156,14 @@ module ActionDispatch
|
|
151
156
|
|
152
157
|
# We assume these things about the IP headers:
|
153
158
|
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
159
|
+
# - X-Forwarded-For will be a list of IPs, one per proxy, or blank
|
160
|
+
# - Client-Ip is propagated from the outermost proxy, or is blank
|
161
|
+
# - REMOTE_ADDR will be the IP that made the request to Rack
|
157
162
|
ips = forwarded_ips + client_ips
|
158
163
|
ips.compact!
|
159
164
|
|
160
|
-
# If every single IP option is in the trusted list, return the IP
|
161
|
-
#
|
165
|
+
# If every single IP option is in the trusted list, return the IP that's
|
166
|
+
# furthest away
|
162
167
|
filter_proxies(ips + [remote_addr]).first || ips.last || remote_addr
|
163
168
|
end
|
164
169
|
|
@@ -1,21 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "securerandom"
|
4
6
|
require "active_support/core_ext/string/access"
|
5
7
|
|
6
8
|
module ActionDispatch
|
7
|
-
#
|
9
|
+
# # Action Dispatch RequestId
|
8
10
|
#
|
9
|
-
# Makes a unique request id available to the
|
10
|
-
#
|
11
|
-
# the same id to the client
|
11
|
+
# Makes a unique request id available to the `action_dispatch.request_id` env
|
12
|
+
# variable (which is then accessible through ActionDispatch::Request#request_id
|
13
|
+
# or the alias ActionDispatch::Request#uuid) and sends the same id to the client
|
14
|
+
# via the `X-Request-Id` header.
|
12
15
|
#
|
13
|
-
# The unique request id is either based on the
|
14
|
-
# by a firewall, load balancer, or
|
15
|
-
#
|
16
|
+
# The unique request id is either based on the `X-Request-Id` header in the
|
17
|
+
# request, which would typically be generated by a firewall, load balancer, or
|
18
|
+
# the web server, or, if this header is not available, a random uuid. If the
|
19
|
+
# header is accepted from the outside world, we sanitize it to a max of 255
|
20
|
+
# chars and alphanumeric and dashes only.
|
16
21
|
#
|
17
|
-
# The unique request id can be used to trace a request end-to-end and would
|
18
|
-
# from multiple pieces of the stack.
|
22
|
+
# The unique request id can be used to trace a request end-to-end and would
|
23
|
+
# typically end up being part of log files from multiple pieces of the stack.
|
19
24
|
class RequestId
|
20
25
|
def initialize(app, header:)
|
21
26
|
@app = app
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/notifications"
|
4
6
|
|
5
7
|
module ActionDispatch
|
@@ -29,8 +31,8 @@ module ActionDispatch
|
|
29
31
|
|
30
32
|
def ensure_subscribed
|
31
33
|
@mutex.synchronize do
|
32
|
-
# Subscribe to all events, except those beginning with "!"
|
33
|
-
#
|
34
|
+
# Subscribe to all events, except those beginning with "!" Ideally we would be
|
35
|
+
# more selective of what is being measured
|
34
36
|
@subscriber ||= ActiveSupport::Notifications.subscribe(/\A[^!]/, self)
|
35
37
|
end
|
36
38
|
end
|
@@ -1,19 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "action_dispatch/middleware/session/abstract_store"
|
4
6
|
|
5
7
|
module ActionDispatch
|
6
8
|
module Session
|
7
|
-
#
|
9
|
+
# # Action Dispatch Session CacheStore
|
10
|
+
#
|
11
|
+
# A session store that uses an ActiveSupport::Cache::Store to store the
|
12
|
+
# sessions. This store is most useful if you don't store critical data in your
|
13
|
+
# sessions and you don't need them to live for extended periods of time.
|
8
14
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
15
|
+
# #### Options
|
16
|
+
# * `cache` - The cache to use. If it is not specified, `Rails.cache`
|
17
|
+
# will be used.
|
18
|
+
# * `expire_after` - The length of time a session will be stored before
|
19
|
+
# automatically expiring. By default, the `:expires_in` option of the cache
|
20
|
+
# is used.
|
12
21
|
#
|
13
|
-
# ==== Options
|
14
|
-
# * <tt>cache</tt> - The cache to use. If it is not specified, <tt>Rails.cache</tt> will be used.
|
15
|
-
# * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring.
|
16
|
-
# By default, the <tt>:expires_in</tt> option of the cache is used.
|
17
22
|
class CacheStore < AbstractSecureStore
|
18
23
|
def initialize(app, options = {})
|
19
24
|
@cache = options[:cache] || Rails.cache
|