actionpack 7.1.5.1 → 7.2.0.beta1
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 +61 -642
- 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 +12 -13
- 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 +155 -117
- 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 +119 -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 +214 -203
- 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 +483 -478
- 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 +58 -57
- 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 +5 -1
- 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 +48 -59
- data/lib/action_dispatch/http/filter_parameters.rb +13 -14
- data/lib/action_dispatch/http/filter_redirect.rb +15 -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 +25 -21
- 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 +26 -36
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +75 -95
- data/lib/action_dispatch/http/response.rb +61 -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 +2 -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 +16 -16
- 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/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +2 -3
- 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 +6 -4
- data/lib/action_dispatch/routing/mapper.rb +623 -625
- 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 +60 -46
- 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 +4 -2
- data/lib/action_dispatch/system_testing/driver.rb +2 -0
- 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 -32
- 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 +33 -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
|