actionpack 4.2.10 → 7.2.0.rc1
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 +5 -5
- data/CHANGELOG.md +86 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -14
- data/lib/abstract_controller/asset_paths.rb +5 -1
- data/lib/abstract_controller/base.rb +166 -136
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +126 -57
- data/lib/abstract_controller/collector.rb +13 -15
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +181 -132
- data/lib/abstract_controller/logger.rb +5 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
- data/lib/abstract_controller/rendering.rb +56 -56
- data/lib/abstract_controller/translation.rb +29 -15
- data/lib/abstract_controller/url_for.rb +15 -11
- data/lib/abstract_controller.rb +21 -5
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +154 -0
- data/lib/action_controller/base.rb +219 -155
- data/lib/action_controller/caching.rb +28 -68
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +35 -22
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +259 -122
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +9 -5
- data/lib/action_controller/metal/data_streaming.rb +87 -104
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
- data/lib/action_controller/metal/exceptions.rb +71 -24
- data/lib/action_controller/metal/flash.rb +26 -19
- data/lib/action_controller/metal/head.rb +45 -36
- data/lib/action_controller/metal/helpers.rb +80 -64
- data/lib/action_controller/metal/http_authentication.rb +297 -244
- data/lib/action_controller/metal/implicit_render.rb +57 -9
- data/lib/action_controller/metal/instrumentation.rb +76 -64
- data/lib/action_controller/metal/live.rb +238 -176
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +177 -166
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +145 -118
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +203 -64
- data/lib/action_controller/metal/renderers.rb +108 -65
- data/lib/action_controller/metal/rendering.rb +216 -56
- data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
- data/lib/action_controller/metal/rescue.rb +19 -21
- data/lib/action_controller/metal/streaming.rb +179 -138
- data/lib/action_controller/metal/strong_parameters.rb +1058 -382
- data/lib/action_controller/metal/testing.rb +11 -17
- data/lib/action_controller/metal/url_for.rb +37 -21
- data/lib/action_controller/metal.rb +236 -138
- data/lib/action_controller/railtie.rb +89 -11
- data/lib/action_controller/railties/helpers.rb +5 -1
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +425 -497
- data/lib/action_controller.rb +44 -22
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +119 -63
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +364 -0
- data/lib/action_dispatch/http/filter_parameters.rb +36 -34
- data/lib/action_dispatch/http/filter_redirect.rb +24 -12
- data/lib/action_dispatch/http/headers.rb +66 -31
- data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
- data/lib/action_dispatch/http/mime_type.rb +196 -136
- data/lib/action_dispatch/http/mime_types.rb +25 -7
- data/lib/action_dispatch/http/parameters.rb +97 -45
- data/lib/action_dispatch/http/permissions_policy.rb +187 -0
- data/lib/action_dispatch/http/rack_cache.rb +6 -0
- data/lib/action_dispatch/http/request.rb +299 -170
- data/lib/action_dispatch/http/response.rb +311 -160
- data/lib/action_dispatch/http/upload.rb +52 -23
- data/lib/action_dispatch/http/url.rb +201 -125
- data/lib/action_dispatch/journey/formatter.rb +110 -50
- data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
- data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
- data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
- data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
- data/lib/action_dispatch/journey/nodes/node.rb +100 -20
- data/lib/action_dispatch/journey/parser.rb +19 -17
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +14 -4
- data/lib/action_dispatch/journey/path/pattern.rb +79 -63
- data/lib/action_dispatch/journey/route.rb +108 -44
- data/lib/action_dispatch/journey/router/utils.rb +41 -29
- data/lib/action_dispatch/journey/router.rb +64 -57
- data/lib/action_dispatch/journey/routes.rb +23 -21
- data/lib/action_dispatch/journey/scanner.rb +28 -17
- data/lib/action_dispatch/journey/visitors.rb +100 -54
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +7 -6
- data/lib/action_dispatch/middleware/cookies.rb +471 -328
- data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +143 -101
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/reloader.rb +10 -92
- data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
- data/lib/action_dispatch/middleware/request_id.rb +29 -15
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
- data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
- data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
- data/lib/action_dispatch/middleware/ssl.rb +134 -36
- data/lib/action_dispatch/middleware/stack.rb +109 -44
- data/lib/action_dispatch/middleware/static.rb +159 -90
- 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 +7 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -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 +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
- data/lib/action_dispatch/railtie.rb +44 -16
- data/lib/action_dispatch/request/session.rb +159 -69
- data/lib/action_dispatch/request/utils.rb +97 -23
- data/lib/action_dispatch/routing/endpoint.rb +11 -2
- data/lib/action_dispatch/routing/inspector.rb +195 -106
- data/lib/action_dispatch/routing/mapper.rb +1338 -955
- data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
- data/lib/action_dispatch/routing/redirection.rb +78 -51
- data/lib/action_dispatch/routing/route_set.rb +460 -374
- data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
- data/lib/action_dispatch/routing/url_for.rb +172 -124
- data/lib/action_dispatch/routing.rb +159 -158
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +84 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +71 -39
- data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
- data/lib/action_dispatch/testing/assertions.rb +9 -6
- data/lib/action_dispatch/testing/integration.rb +486 -306
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +35 -22
- data/lib/action_dispatch/testing/test_request.rb +29 -34
- data/lib/action_dispatch/testing/test_response.rb +48 -15
- data/lib/action_dispatch.rb +82 -40
- data/lib/action_pack/gem_version.rb +8 -4
- data/lib/action_pack/version.rb +6 -2
- data/lib/action_pack.rb +21 -18
- metadata +146 -56
- data/lib/action_controller/caching/fragments.rb +0 -103
- 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/middleware/templates/rescues/_source.erb +0 -27
- 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,53 +1,93 @@
|
|
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
|
+
# :markup: markdown
|
12
4
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
5
|
+
require "active_support/core_ext/hash/keys"
|
6
|
+
|
7
|
+
module ActionDispatch
|
8
|
+
# # Action Dispatch Flash
|
17
9
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
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.
|
17
|
+
#
|
18
|
+
# class PostsController < ActionController::Base
|
19
|
+
# def create
|
20
|
+
# # save post
|
21
|
+
# flash[:notice] = "Post successfully created"
|
22
|
+
# redirect_to @post
|
23
|
+
# end
|
24
24
|
#
|
25
|
-
#
|
26
|
-
#
|
25
|
+
# def show
|
26
|
+
# # doesn't need to assign the flash notice to the template, that's done automatically
|
27
|
+
# end
|
27
28
|
# end
|
28
|
-
# end
|
29
29
|
#
|
30
|
-
#
|
30
|
+
# Then in `show.html.erb`:
|
31
|
+
#
|
31
32
|
# <% if flash[:notice] %>
|
32
33
|
# <div class="notice"><%= flash[:notice] %></div>
|
33
34
|
# <% end %>
|
34
35
|
#
|
35
|
-
# Since the
|
36
|
+
# Since the `notice` and `alert` keys are a common idiom, convenience accessors
|
37
|
+
# are available:
|
36
38
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
+
# flash.alert = "You must be logged in"
|
40
|
+
# flash.notice = "Post successfully created"
|
39
41
|
#
|
40
|
-
# This example places a string in the flash. And of course, you can put as many
|
41
|
-
#
|
42
|
-
#
|
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.
|
43
46
|
#
|
44
47
|
# Just remember: They'll be gone by the time the next action has been performed.
|
45
48
|
#
|
46
49
|
# See docs on the FlashHash class for more details about the flash.
|
47
50
|
class Flash
|
48
|
-
KEY =
|
51
|
+
KEY = "action_dispatch.request.flash_hash"
|
52
|
+
|
53
|
+
module RequestMethods
|
54
|
+
# Access the contents of the flash. Returns a ActionDispatch::Flash::FlashHash.
|
55
|
+
#
|
56
|
+
# See ActionDispatch::Flash for example usage.
|
57
|
+
def flash
|
58
|
+
flash = flash_hash
|
59
|
+
return flash if flash
|
60
|
+
self.flash = Flash::FlashHash.from_session_value(session["flash"])
|
61
|
+
end
|
62
|
+
|
63
|
+
def flash=(flash)
|
64
|
+
set_header Flash::KEY, flash
|
65
|
+
end
|
66
|
+
|
67
|
+
def flash_hash # :nodoc:
|
68
|
+
get_header Flash::KEY
|
69
|
+
end
|
49
70
|
|
50
|
-
|
71
|
+
def commit_flash # :nodoc:
|
72
|
+
return unless session.enabled?
|
73
|
+
|
74
|
+
if flash_hash && (flash_hash.present? || session.key?("flash"))
|
75
|
+
session["flash"] = flash_hash.to_session_value
|
76
|
+
self.flash = flash_hash.dup
|
77
|
+
end
|
78
|
+
|
79
|
+
if session.loaded? && session.key?("flash") && session["flash"].nil?
|
80
|
+
session.delete("flash")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def reset_session # :nodoc:
|
85
|
+
super
|
86
|
+
self.flash = nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class FlashNow # :nodoc:
|
51
91
|
attr_accessor :flash
|
52
92
|
|
53
93
|
def initialize(flash)
|
@@ -65,12 +105,12 @@ module ActionDispatch
|
|
65
105
|
@flash[k.to_s]
|
66
106
|
end
|
67
107
|
|
68
|
-
# Convenience accessor for
|
108
|
+
# Convenience accessor for `flash.now[:alert]=`.
|
69
109
|
def alert=(message)
|
70
110
|
self[:alert] = message
|
71
111
|
end
|
72
112
|
|
73
|
-
# Convenience accessor for
|
113
|
+
# Convenience accessor for `flash.now[:notice]=`.
|
74
114
|
def notice=(message)
|
75
115
|
self[:notice] = message
|
76
116
|
end
|
@@ -79,28 +119,34 @@ module ActionDispatch
|
|
79
119
|
class FlashHash
|
80
120
|
include Enumerable
|
81
121
|
|
82
|
-
def self.from_session_value(value)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
122
|
+
def self.from_session_value(value) # :nodoc:
|
123
|
+
case value
|
124
|
+
when FlashHash # Rails 3.1, 3.2
|
125
|
+
flashes = value.instance_variable_get(:@flashes)
|
126
|
+
if discard = value.instance_variable_get(:@used)
|
127
|
+
flashes.except!(*discard)
|
128
|
+
end
|
129
|
+
new(flashes, flashes.keys)
|
130
|
+
when Hash # Rails 4.0
|
131
|
+
flashes = value["flashes"]
|
132
|
+
if discard = value["discard"]
|
133
|
+
flashes.except!(*discard)
|
134
|
+
end
|
135
|
+
new(flashes, flashes.keys)
|
136
|
+
else
|
137
|
+
new
|
138
|
+
end
|
93
139
|
end
|
94
|
-
|
95
|
-
# Builds a hash containing the
|
96
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
return nil if empty?
|
100
|
-
{
|
140
|
+
|
141
|
+
# Builds a hash containing the flashes to keep for the next request. If there
|
142
|
+
# are none to keep, returns `nil`.
|
143
|
+
def to_session_value # :nodoc:
|
144
|
+
flashes_to_keep = @flashes.except(*@discard)
|
145
|
+
return nil if flashes_to_keep.empty?
|
146
|
+
{ "discard" => [], "flashes" => flashes_to_keep }
|
101
147
|
end
|
102
148
|
|
103
|
-
def initialize(flashes = {}, discard = [])
|
149
|
+
def initialize(flashes = {}, discard = []) # :nodoc:
|
104
150
|
@discard = Set.new(stringify_array(discard))
|
105
151
|
@flashes = flashes.stringify_keys
|
106
152
|
@now = nil
|
@@ -124,7 +170,7 @@ module ActionDispatch
|
|
124
170
|
@flashes[k.to_s]
|
125
171
|
end
|
126
172
|
|
127
|
-
def update(h)
|
173
|
+
def update(h) # :nodoc:
|
128
174
|
@discard.subtract stringify_array(h.keys)
|
129
175
|
@flashes.update h.stringify_keys
|
130
176
|
self
|
@@ -138,6 +184,8 @@ module ActionDispatch
|
|
138
184
|
@flashes.key? name.to_s
|
139
185
|
end
|
140
186
|
|
187
|
+
# Immediately deletes the single flash entry. Use this method when you want
|
188
|
+
# remove the message within the current action. See also #discard.
|
141
189
|
def delete(key)
|
142
190
|
key = key.to_s
|
143
191
|
@discard.delete key
|
@@ -164,48 +212,55 @@ module ActionDispatch
|
|
164
212
|
|
165
213
|
alias :merge! :update
|
166
214
|
|
167
|
-
def replace(h)
|
215
|
+
def replace(h) # :nodoc:
|
168
216
|
@discard.clear
|
169
217
|
@flashes.replace h.stringify_keys
|
170
218
|
self
|
171
219
|
end
|
172
220
|
|
173
|
-
# 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.
|
174
223
|
#
|
175
224
|
# flash.now[:message] = "Hello current action"
|
176
225
|
#
|
177
|
-
# This method enables you to use the flash as a central messaging system in your
|
178
|
-
# When you need to pass an object to the next action, you use the standard
|
179
|
-
# When you need to pass an object to the current action,
|
180
|
-
# 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.
|
181
230
|
#
|
182
|
-
# Entries set via
|
231
|
+
# Entries set via `now` are accessed the same way as standard entries:
|
232
|
+
# `flash['my-key']`.
|
183
233
|
#
|
184
234
|
# Also, brings two convenience accessors:
|
185
235
|
#
|
186
|
-
#
|
187
|
-
#
|
236
|
+
# flash.now.alert = "Beware now!"
|
237
|
+
# # Equivalent to flash.now[:alert] = "Beware now!"
|
188
238
|
#
|
189
|
-
#
|
190
|
-
#
|
239
|
+
# flash.now.notice = "Good luck now!"
|
240
|
+
# # Equivalent to flash.now[:notice] = "Good luck now!"
|
191
241
|
def now
|
192
242
|
@now ||= FlashNow.new(self)
|
193
243
|
end
|
194
244
|
|
195
|
-
# 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:
|
196
247
|
#
|
197
|
-
#
|
198
|
-
#
|
248
|
+
# flash.keep # keeps the entire flash
|
249
|
+
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
|
199
250
|
def keep(k = nil)
|
200
251
|
k = k.to_s if k
|
201
252
|
@discard.subtract Array(k || keys)
|
202
253
|
k ? self[k] : self
|
203
254
|
end
|
204
255
|
|
205
|
-
# 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:
|
206
258
|
#
|
207
259
|
# flash.discard # discard the entire flash at the end of the current action
|
208
260
|
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
|
261
|
+
#
|
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.
|
209
264
|
def discard(k = nil)
|
210
265
|
k = k.to_s if k
|
211
266
|
@discard.merge Array(k || keys)
|
@@ -214,63 +269,50 @@ module ActionDispatch
|
|
214
269
|
|
215
270
|
# Mark for removal entries that were kept, and delete unkept ones.
|
216
271
|
#
|
217
|
-
# This method is called automatically by filters, so you generally don't need to
|
218
|
-
|
272
|
+
# This method is called automatically by filters, so you generally don't need to
|
273
|
+
# care about it.
|
274
|
+
def sweep # :nodoc:
|
219
275
|
@discard.each { |k| @flashes.delete k }
|
220
276
|
@discard.replace @flashes.keys
|
221
277
|
end
|
222
278
|
|
223
|
-
# Convenience accessor for
|
279
|
+
# Convenience accessor for `flash[:alert]`.
|
224
280
|
def alert
|
225
281
|
self[:alert]
|
226
282
|
end
|
227
283
|
|
228
|
-
# Convenience accessor for
|
284
|
+
# Convenience accessor for `flash[:alert]=`.
|
229
285
|
def alert=(message)
|
230
286
|
self[:alert] = message
|
231
287
|
end
|
232
288
|
|
233
|
-
# Convenience accessor for
|
289
|
+
# Convenience accessor for `flash[:notice]`.
|
234
290
|
def notice
|
235
291
|
self[:notice]
|
236
292
|
end
|
237
293
|
|
238
|
-
# Convenience accessor for
|
294
|
+
# Convenience accessor for `flash[:notice]=`.
|
239
295
|
def notice=(message)
|
240
296
|
self[:notice] = message
|
241
297
|
end
|
242
298
|
|
243
299
|
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
|
300
|
+
def now_is_loaded?
|
301
|
+
@now
|
251
302
|
end
|
252
|
-
end
|
253
|
-
end
|
254
303
|
|
255
|
-
|
256
|
-
|
304
|
+
private
|
305
|
+
def stringify_array(array) # :doc:
|
306
|
+
array.map do |item|
|
307
|
+
item.kind_of?(Symbol) ? item.to_s : item
|
308
|
+
end
|
309
|
+
end
|
257
310
|
end
|
258
311
|
|
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
|
312
|
+
def self.new(app) app; end
|
313
|
+
end
|
269
314
|
|
270
|
-
|
271
|
-
|
272
|
-
session.delete('flash')
|
273
|
-
end
|
274
|
-
end
|
315
|
+
class Request
|
316
|
+
prepend Flash::RequestMethods
|
275
317
|
end
|
276
318
|
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
# # Action Dispatch HostAuthorization
|
7
|
+
#
|
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`.
|
11
|
+
#
|
12
|
+
# Requests can opt-out of Host Authorization with `exclude`:
|
13
|
+
#
|
14
|
+
# config.host_authorization = { exclude: ->(request) { request.path =~ /healthcheck/ } }
|
15
|
+
#
|
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.
|
22
|
+
class HostAuthorization
|
23
|
+
ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", ".test", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
|
24
|
+
PORT_REGEX = /(?::\d+)/ # :nodoc:
|
25
|
+
SUBDOMAIN_REGEX = /(?:[a-z0-9-]+\.)/i # :nodoc:
|
26
|
+
IPV4_HOSTNAME = /(?<host>\d+\.\d+\.\d+\.\d+)#{PORT_REGEX}?/ # :nodoc:
|
27
|
+
IPV6_HOSTNAME = /(?<host>[a-f0-9]*:[a-f0-9.:]+)/i # :nodoc:
|
28
|
+
IPV6_HOSTNAME_WITH_PORT = /\[#{IPV6_HOSTNAME}\]#{PORT_REGEX}/i # :nodoc:
|
29
|
+
VALID_IP_HOSTNAME = Regexp.union( # :nodoc:
|
30
|
+
/\A#{IPV4_HOSTNAME}\z/,
|
31
|
+
/\A#{IPV6_HOSTNAME}\z/,
|
32
|
+
/\A#{IPV6_HOSTNAME_WITH_PORT}\z/,
|
33
|
+
)
|
34
|
+
|
35
|
+
class Permissions # :nodoc:
|
36
|
+
def initialize(hosts)
|
37
|
+
@hosts = sanitize_hosts(hosts)
|
38
|
+
end
|
39
|
+
|
40
|
+
def empty?
|
41
|
+
@hosts.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def allows?(host)
|
45
|
+
@hosts.any? do |allowed|
|
46
|
+
if allowed.is_a?(IPAddr)
|
47
|
+
begin
|
48
|
+
allowed === extract_hostname(host)
|
49
|
+
rescue
|
50
|
+
# IPAddr#=== raises an error if you give it a hostname instead of IP. Treat
|
51
|
+
# similar errors as blocked access.
|
52
|
+
false
|
53
|
+
end
|
54
|
+
else
|
55
|
+
allowed === host
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def sanitize_hosts(hosts)
|
62
|
+
Array(hosts).map do |host|
|
63
|
+
case host
|
64
|
+
when Regexp then sanitize_regexp(host)
|
65
|
+
when String then sanitize_string(host)
|
66
|
+
else host
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def sanitize_regexp(host)
|
72
|
+
/\A#{host}#{PORT_REGEX}?\z/
|
73
|
+
end
|
74
|
+
|
75
|
+
def sanitize_string(host)
|
76
|
+
if host.start_with?(".")
|
77
|
+
/\A#{SUBDOMAIN_REGEX}?#{Regexp.escape(host[1..-1])}#{PORT_REGEX}?\z/i
|
78
|
+
else
|
79
|
+
/\A#{Regexp.escape host}#{PORT_REGEX}?\z/i
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def extract_hostname(host)
|
84
|
+
host.slice(VALID_IP_HOSTNAME, "host") || host
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class DefaultResponseApp # :nodoc:
|
89
|
+
RESPONSE_STATUS = 403
|
90
|
+
|
91
|
+
def call(env)
|
92
|
+
request = Request.new(env)
|
93
|
+
format = request.xhr? ? "text/plain" : "text/html"
|
94
|
+
|
95
|
+
log_error(request)
|
96
|
+
response(format, response_body(request))
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def response_body(request)
|
101
|
+
return "" unless request.get_header("action_dispatch.show_detailed_exceptions")
|
102
|
+
|
103
|
+
template = DebugView.new(hosts: request.env["action_dispatch.blocked_hosts"])
|
104
|
+
template.render(template: "rescues/blocked_host", layout: "rescues/layout")
|
105
|
+
end
|
106
|
+
|
107
|
+
def response(format, body)
|
108
|
+
[RESPONSE_STATUS,
|
109
|
+
{ Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}",
|
110
|
+
Rack::CONTENT_LENGTH => body.bytesize.to_s },
|
111
|
+
[body]]
|
112
|
+
end
|
113
|
+
|
114
|
+
def log_error(request)
|
115
|
+
logger = available_logger(request)
|
116
|
+
|
117
|
+
return unless logger
|
118
|
+
|
119
|
+
logger.error("[#{self.class.name}] Blocked hosts: #{request.env["action_dispatch.blocked_hosts"].join(", ")}")
|
120
|
+
end
|
121
|
+
|
122
|
+
def available_logger(request)
|
123
|
+
request.logger || ActionView::Base.logger
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def initialize(app, hosts, exclude: nil, response_app: nil)
|
128
|
+
@app = app
|
129
|
+
@permissions = Permissions.new(hosts)
|
130
|
+
@exclude = exclude
|
131
|
+
|
132
|
+
@response_app = response_app || DefaultResponseApp.new
|
133
|
+
end
|
134
|
+
|
135
|
+
def call(env)
|
136
|
+
return @app.call(env) if @permissions.empty?
|
137
|
+
|
138
|
+
request = Request.new(env)
|
139
|
+
hosts = blocked_hosts(request)
|
140
|
+
|
141
|
+
if hosts.empty? || excluded?(request)
|
142
|
+
mark_as_authorized(request)
|
143
|
+
@app.call(env)
|
144
|
+
else
|
145
|
+
env["action_dispatch.blocked_hosts"] = hosts
|
146
|
+
@response_app.call(env)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
def blocked_hosts(request)
|
152
|
+
hosts = []
|
153
|
+
|
154
|
+
origin_host = request.get_header("HTTP_HOST")
|
155
|
+
hosts << origin_host unless @permissions.allows?(origin_host)
|
156
|
+
|
157
|
+
forwarded_host = request.x_forwarded_host&.split(/,\s?/)&.last
|
158
|
+
hosts << forwarded_host unless forwarded_host.blank? || @permissions.allows?(forwarded_host)
|
159
|
+
|
160
|
+
hosts
|
161
|
+
end
|
162
|
+
|
163
|
+
def excluded?(request)
|
164
|
+
@exclude && @exclude.call(request)
|
165
|
+
end
|
166
|
+
|
167
|
+
def mark_as_authorized(request)
|
168
|
+
request.set_header("action_dispatch.authorized_host", request.host)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -1,11 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
1
5
|
module ActionDispatch
|
6
|
+
# # Action Dispatch PublicExceptions
|
7
|
+
#
|
2
8
|
# When called, this middleware renders an error page. By default if an HTML
|
3
9
|
# response is expected it will render static error pages from the `/public`
|
4
10
|
# directory. For example when this middleware receives a 500 response it will
|
5
|
-
# render the template found in `/public/500.html`.
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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`.
|
9
15
|
#
|
10
16
|
# When a request with a content type other than HTML is made, this middleware
|
11
17
|
# will attempt to convert error information into the appropriate response type.
|
@@ -17,39 +23,42 @@ module ActionDispatch
|
|
17
23
|
end
|
18
24
|
|
19
25
|
def call(env)
|
20
|
-
status = env["PATH_INFO"][1..-1]
|
21
26
|
request = ActionDispatch::Request.new(env)
|
22
|
-
|
23
|
-
|
27
|
+
status = request.path_info[1..-1].to_i
|
28
|
+
begin
|
29
|
+
content_type = request.formats.first
|
30
|
+
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
31
|
+
content_type = Mime[:text]
|
32
|
+
end
|
33
|
+
body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
|
24
34
|
|
25
35
|
render(status, content_type, body)
|
26
36
|
end
|
27
37
|
|
28
38
|
private
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
39
|
+
def render(status, content_type, body)
|
40
|
+
format = "to_#{content_type.to_sym}" if content_type
|
41
|
+
if format && body.respond_to?(format)
|
42
|
+
render_format(status, content_type, body.public_send(format))
|
43
|
+
else
|
44
|
+
render_html(status)
|
45
|
+
end
|
36
46
|
end
|
37
|
-
end
|
38
47
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
def render_format(status, content_type, body)
|
49
|
+
[status, { Rack::CONTENT_TYPE => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
|
50
|
+
Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
51
|
+
end
|
43
52
|
|
44
|
-
|
45
|
-
|
46
|
-
|
53
|
+
def render_html(status)
|
54
|
+
path = "#{public_path}/#{status}.#{I18n.locale}.html"
|
55
|
+
path = "#{public_path}/#{status}.html" unless (found = File.exist?(path))
|
47
56
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
57
|
+
if found || File.exist?(path)
|
58
|
+
render_format(status, "text/html", File.read(path))
|
59
|
+
else
|
60
|
+
[404, { Constants::X_CASCADE => "pass" }, []]
|
61
|
+
end
|
52
62
|
end
|
53
|
-
end
|
54
63
|
end
|
55
64
|
end
|