actionpack 7.0.0.alpha1 → 7.0.0.rc3
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 +153 -0
- data/lib/abstract_controller/callbacks.rb +15 -2
- data/lib/abstract_controller/translation.rb +4 -1
- data/lib/action_controller/log_subscriber.rb +1 -2
- data/lib/action_controller/metal/helpers.rb +1 -1
- data/lib/action_controller/metal/http_authentication.rb +2 -1
- data/lib/action_controller/metal/instrumentation.rb +2 -0
- data/lib/action_controller/metal/params_wrapper.rb +13 -4
- data/lib/action_controller/metal/redirecting.rb +58 -22
- data/lib/action_controller/metal/request_forgery_protection.rb +30 -34
- data/lib/action_controller/metal/strong_parameters.rb +60 -19
- data/lib/action_controller/railtie.rb +16 -10
- data/lib/action_controller/test_case.rb +13 -2
- data/lib/action_controller.rb +0 -1
- data/lib/action_dispatch/http/response.rb +0 -12
- data/lib/action_dispatch/http/url.rb +2 -9
- data/lib/action_dispatch/journey/nodes/node.rb +2 -2
- data/lib/action_dispatch/journey/route.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +1 -1
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +46 -28
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +10 -0
- data/lib/action_dispatch/middleware/stack.rb +1 -24
- data/lib/action_dispatch/middleware/static.rb +0 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +10 -6
- data/lib/action_dispatch/routing/route_set.rb +5 -0
- data/lib/action_dispatch/system_test_case.rb +7 -1
- data/lib/action_dispatch/system_testing/browser.rb +2 -12
- data/lib/action_dispatch/system_testing/driver.rb +13 -9
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/test_process.rb +1 -27
- data/lib/action_dispatch.rb +1 -0
- data/lib/action_pack/gem_version.rb +1 -1
- metadata +17 -16
- data/lib/action_controller/metal/query_tags.rb +0 -16
@@ -81,7 +81,7 @@ module ActionController
|
|
81
81
|
# })
|
82
82
|
#
|
83
83
|
# permitted = params.require(:person).permit(:name, :age)
|
84
|
-
# permitted # =>
|
84
|
+
# permitted # => #<ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
|
85
85
|
# permitted.permitted? # => true
|
86
86
|
#
|
87
87
|
# Person.first.update!(permitted)
|
@@ -111,7 +111,7 @@ module ActionController
|
|
111
111
|
#
|
112
112
|
# params = ActionController::Parameters.new(a: "123", b: "456")
|
113
113
|
# params.permit(:c)
|
114
|
-
# # =>
|
114
|
+
# # => #<ActionController::Parameters {} permitted: true>
|
115
115
|
#
|
116
116
|
# ActionController::Parameters.action_on_unpermitted_parameters = :raise
|
117
117
|
#
|
@@ -442,7 +442,7 @@ module ActionController
|
|
442
442
|
# either present or the singleton +false+, returns said value:
|
443
443
|
#
|
444
444
|
# ActionController::Parameters.new(person: { name: "Francesco" }).require(:person)
|
445
|
-
# # =>
|
445
|
+
# # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
446
446
|
#
|
447
447
|
# Otherwise raises <tt>ActionController::ParameterMissing</tt>:
|
448
448
|
#
|
@@ -570,13 +570,48 @@ module ActionController
|
|
570
570
|
# })
|
571
571
|
#
|
572
572
|
# params.require(:person).permit(:contact)
|
573
|
-
# # =>
|
573
|
+
# # => #<ActionController::Parameters {} permitted: true>
|
574
574
|
#
|
575
575
|
# params.require(:person).permit(contact: :phone)
|
576
|
-
# # =>
|
576
|
+
# # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
|
577
577
|
#
|
578
578
|
# params.require(:person).permit(contact: [ :email, :phone ])
|
579
|
-
# # =>
|
579
|
+
# # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
|
580
|
+
#
|
581
|
+
# If your parameters specify multiple parameters indexed by a number,
|
582
|
+
# you can permit each set of parameters under the numeric key to be the same using the same syntax as permitting a single item.
|
583
|
+
#
|
584
|
+
# params = ActionController::Parameters.new({
|
585
|
+
# person: {
|
586
|
+
# '0': {
|
587
|
+
# email: "none@test.com",
|
588
|
+
# phone: "555-1234"
|
589
|
+
# },
|
590
|
+
# '1': {
|
591
|
+
# email: "nothing@test.com",
|
592
|
+
# phone: "555-6789"
|
593
|
+
# },
|
594
|
+
# }
|
595
|
+
# })
|
596
|
+
# params.permit(person: [:email]).to_h
|
597
|
+
# # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"email"=>"nothing@test.com"}}}
|
598
|
+
#
|
599
|
+
# If you want to specify what keys you want from each numeric key, you can instead specify each one individually
|
600
|
+
#
|
601
|
+
# params = ActionController::Parameters.new({
|
602
|
+
# person: {
|
603
|
+
# '0': {
|
604
|
+
# email: "none@test.com",
|
605
|
+
# phone: "555-1234"
|
606
|
+
# },
|
607
|
+
# '1': {
|
608
|
+
# email: "nothing@test.com",
|
609
|
+
# phone: "555-6789"
|
610
|
+
# },
|
611
|
+
# }
|
612
|
+
# })
|
613
|
+
# params.permit(person: { '0': [:email], '1': [:phone]}).to_h
|
614
|
+
# # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"phone"=>"555-6789"}}}
|
580
615
|
def permit(*filters)
|
581
616
|
params = self.class.new
|
582
617
|
|
@@ -598,7 +633,7 @@ module ActionController
|
|
598
633
|
# returns +nil+.
|
599
634
|
#
|
600
635
|
# params = ActionController::Parameters.new(person: { name: "Francesco" })
|
601
|
-
# params[:person] # =>
|
636
|
+
# params[:person] # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
602
637
|
# params[:none] # => nil
|
603
638
|
def [](key)
|
604
639
|
convert_hashes_to_parameters(key, @parameters[key])
|
@@ -618,9 +653,9 @@ module ActionController
|
|
618
653
|
# is given, then that will be run and its result returned.
|
619
654
|
#
|
620
655
|
# params = ActionController::Parameters.new(person: { name: "Francesco" })
|
621
|
-
# params.fetch(:person) # =>
|
656
|
+
# params.fetch(:person) # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
622
657
|
# params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
|
623
|
-
# params.fetch(:none, {}) # =>
|
658
|
+
# params.fetch(:none, {}) # => #<ActionController::Parameters {} permitted: false>
|
624
659
|
# params.fetch(:none, "Francesco") # => "Francesco"
|
625
660
|
# params.fetch(:none) { "Francesco" } # => "Francesco"
|
626
661
|
def fetch(key, *args)
|
@@ -654,8 +689,8 @@ module ActionController
|
|
654
689
|
# don't exist, returns an empty hash.
|
655
690
|
#
|
656
691
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
657
|
-
# params.slice(:a, :b) # =>
|
658
|
-
# params.slice(:d) # =>
|
692
|
+
# params.slice(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
|
693
|
+
# params.slice(:d) # => #<ActionController::Parameters {} permitted: false>
|
659
694
|
def slice(*keys)
|
660
695
|
new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
|
661
696
|
end
|
@@ -671,8 +706,8 @@ module ActionController
|
|
671
706
|
# filters out the given +keys+.
|
672
707
|
#
|
673
708
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
674
|
-
# params.except(:a, :b) # =>
|
675
|
-
# params.except(:d) # =>
|
709
|
+
# params.except(:a, :b) # => #<ActionController::Parameters {"c"=>3} permitted: false>
|
710
|
+
# params.except(:d) # => #<ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
|
676
711
|
def except(*keys)
|
677
712
|
new_instance_with_inherited_permitted_status(@parameters.except(*keys))
|
678
713
|
end
|
@@ -680,8 +715,8 @@ module ActionController
|
|
680
715
|
# Removes and returns the key/value pairs matching the given keys.
|
681
716
|
#
|
682
717
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
683
|
-
# params.extract!(:a, :b) # =>
|
684
|
-
# params # =>
|
718
|
+
# params.extract!(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
|
719
|
+
# params # => #<ActionController::Parameters {"c"=>3} permitted: false>
|
685
720
|
def extract!(*keys)
|
686
721
|
new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
|
687
722
|
end
|
@@ -691,7 +726,7 @@ module ActionController
|
|
691
726
|
#
|
692
727
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
693
728
|
# params.transform_values { |x| x * 2 }
|
694
|
-
# # =>
|
729
|
+
# # => #<ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
|
695
730
|
def transform_values
|
696
731
|
return to_enum(:transform_values) unless block_given?
|
697
732
|
new_instance_with_inherited_permitted_status(
|
@@ -937,12 +972,18 @@ module ActionController
|
|
937
972
|
end
|
938
973
|
end
|
939
974
|
|
940
|
-
def
|
975
|
+
def specify_numeric_keys?(filter)
|
976
|
+
if filter.respond_to?(:keys)
|
977
|
+
filter.keys.any? { |key| /\A-?\d+\z/.match?(key) }
|
978
|
+
end
|
979
|
+
end
|
980
|
+
|
981
|
+
def each_element(object, filter, &block)
|
941
982
|
case object
|
942
983
|
when Array
|
943
984
|
object.grep(Parameters).filter_map(&block)
|
944
985
|
when Parameters
|
945
|
-
if object.nested_attributes?
|
986
|
+
if object.nested_attributes? && !specify_numeric_keys?(filter)
|
946
987
|
object.each_nested_attribute(&block)
|
947
988
|
else
|
948
989
|
yield object
|
@@ -1055,7 +1096,7 @@ module ActionController
|
|
1055
1096
|
end
|
1056
1097
|
elsif non_scalar?(value)
|
1057
1098
|
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
|
1058
|
-
params[key] = each_element(value) do |element|
|
1099
|
+
params[key] = each_element(value, filter[key]) do |element|
|
1059
1100
|
element.permit(*Array.wrap(filter[key]))
|
1060
1101
|
end
|
1061
1102
|
end
|
@@ -12,6 +12,7 @@ module ActionController
|
|
12
12
|
config.action_controller = ActiveSupport::OrderedOptions.new
|
13
13
|
config.action_controller.raise_on_open_redirects = false
|
14
14
|
config.action_controller.log_query_tags_around_actions = true
|
15
|
+
config.action_controller.wrap_parameters_by_default = false
|
15
16
|
|
16
17
|
config.eager_load_namespaces << ActionController
|
17
18
|
|
@@ -62,15 +63,18 @@ module ActionController
|
|
62
63
|
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
63
64
|
extend ::ActionController::Railties::Helpers
|
64
65
|
|
66
|
+
wrap_parameters format: [:json] if options.wrap_parameters_by_default && respond_to?(:wrap_parameters)
|
67
|
+
|
65
68
|
# Configs used in other initializers
|
66
|
-
|
69
|
+
filtered_options = options.except(
|
67
70
|
:log_query_tags_around_actions,
|
68
71
|
:permit_all_parameters,
|
69
72
|
:action_on_unpermitted_parameters,
|
70
|
-
:always_permitted_parameters
|
73
|
+
:always_permitted_parameters,
|
74
|
+
:wrap_parameters_by_default
|
71
75
|
)
|
72
76
|
|
73
|
-
|
77
|
+
filtered_options.each do |k, v|
|
74
78
|
k = "#{k}="
|
75
79
|
if respond_to?(k)
|
76
80
|
send(k, v)
|
@@ -109,18 +113,20 @@ module ActionController
|
|
109
113
|
if query_logs_tags_enabled
|
110
114
|
app.config.active_record.query_log_tags += [:controller, :action]
|
111
115
|
|
112
|
-
ActiveSupport.on_load(:action_controller) do
|
113
|
-
include ActionController::QueryTags
|
114
|
-
end
|
115
|
-
|
116
116
|
ActiveSupport.on_load(:active_record) do
|
117
117
|
ActiveRecord::QueryLogs.taggings.merge!(
|
118
|
-
controller: ->(context) { context[:controller]
|
119
|
-
action: ->(context) { context[:controller]
|
120
|
-
namespaced_controller: ->(context) { context[:controller].class.name }
|
118
|
+
controller: ->(context) { context[:controller]&.controller_name },
|
119
|
+
action: ->(context) { context[:controller]&.action_name },
|
120
|
+
namespaced_controller: ->(context) { context[:controller].class.name if context[:controller] }
|
121
121
|
)
|
122
122
|
end
|
123
123
|
end
|
124
124
|
end
|
125
|
+
|
126
|
+
initializer "action_controller.test_case" do |app|
|
127
|
+
ActiveSupport.on_load(:action_controller_test_case) do
|
128
|
+
ActionController::TestCase.executor_around_each_request = app.config.active_support.executor_around_test_case
|
129
|
+
end
|
130
|
+
end
|
125
131
|
end
|
126
132
|
end
|
@@ -333,6 +333,8 @@ module ActionController
|
|
333
333
|
#
|
334
334
|
# assert_redirected_to page_url(title: 'foo')
|
335
335
|
class TestCase < ActiveSupport::TestCase
|
336
|
+
singleton_class.attr_accessor :executor_around_each_request
|
337
|
+
|
336
338
|
module Behavior
|
337
339
|
extend ActiveSupport::Concern
|
338
340
|
include ActionDispatch::TestProcess
|
@@ -578,10 +580,19 @@ module ActionController
|
|
578
580
|
end
|
579
581
|
end
|
580
582
|
|
583
|
+
def wrap_execution(&block)
|
584
|
+
if ActionController::TestCase.executor_around_each_request && defined?(Rails.application) && Rails.application
|
585
|
+
Rails.application.executor.wrap(&block)
|
586
|
+
else
|
587
|
+
yield
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
581
591
|
def process_controller_response(action, cookies, xhr)
|
582
592
|
begin
|
583
593
|
@controller.recycle!
|
584
|
-
|
594
|
+
|
595
|
+
wrap_execution { @controller.dispatch(action, @request, @response) }
|
585
596
|
ensure
|
586
597
|
@request = @controller.request
|
587
598
|
@response = @controller.response
|
@@ -627,7 +638,7 @@ module ActionController
|
|
627
638
|
end
|
628
639
|
|
629
640
|
def check_required_ivars
|
630
|
-
#
|
641
|
+
# Check for required instance variables so we can give an
|
631
642
|
# understandable error message.
|
632
643
|
[:@routes, :@controller, :@request, :@response].each do |iv_name|
|
633
644
|
if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
|
data/lib/action_controller.rb
CHANGED
@@ -86,18 +86,6 @@ module ActionDispatch # :nodoc:
|
|
86
86
|
cattr_accessor :default_charset, default: "utf-8"
|
87
87
|
cattr_accessor :default_headers
|
88
88
|
|
89
|
-
def self.return_only_media_type_on_content_type=(*)
|
90
|
-
ActiveSupport::Deprecation.warn(
|
91
|
-
".return_only_media_type_on_content_type= is deprecated with no replacement and will be removed in 7.0."
|
92
|
-
)
|
93
|
-
end
|
94
|
-
|
95
|
-
def self.return_only_media_type_on_content_type
|
96
|
-
ActiveSupport::Deprecation.warn(
|
97
|
-
".return_only_media_type_on_content_type is deprecated with no replacement and will be removed in 7.0."
|
98
|
-
)
|
99
|
-
end
|
100
|
-
|
101
89
|
include Rack::Response::Helpers
|
102
90
|
# Aliasing these off because AD::Http::Cache::Response defines them.
|
103
91
|
alias :_cache_control :cache_control
|
@@ -71,7 +71,8 @@ module ActionDispatch
|
|
71
71
|
path = options[:script_name].to_s.chomp("/")
|
72
72
|
path << options[:path] if options.key?(:path)
|
73
73
|
|
74
|
-
|
74
|
+
path = "/" if options[:trailing_slash] && path.blank?
|
75
|
+
|
75
76
|
add_params(path, options[:params]) if options.key?(:params)
|
76
77
|
add_anchor(path, options[:anchor]) if options.key?(:anchor)
|
77
78
|
|
@@ -101,14 +102,6 @@ module ActionDispatch
|
|
101
102
|
parts[0..-(tld_length + 2)]
|
102
103
|
end
|
103
104
|
|
104
|
-
def add_trailing_slash(path)
|
105
|
-
if path.include?("?")
|
106
|
-
path.sub!(/\?/, '/\&')
|
107
|
-
elsif !path.include?(".")
|
108
|
-
path.sub!(/[^\/]\z|\A\z/, '\&/')
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
105
|
def build_host_url(host, port, protocol, options, path)
|
113
106
|
if match = host.match(HOST_REGEXP)
|
114
107
|
protocol ||= match[1] unless protocol == false
|
@@ -53,7 +53,7 @@ module ActionDispatch
|
|
53
53
|
if formatted != false
|
54
54
|
# Add a constraint for wildcard route to make it non-greedy and
|
55
55
|
# match the optional format part of the route by default.
|
56
|
-
wildcard_options[node.name.to_sym] ||= /.+?/
|
56
|
+
wildcard_options[node.name.to_sym] ||= /.+?/m
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -166,7 +166,7 @@ module ActionDispatch
|
|
166
166
|
super(left)
|
167
167
|
|
168
168
|
# By default wildcard routes are non-greedy and must match something.
|
169
|
-
@regexp = /.+?/
|
169
|
+
@regexp = /.+?/m
|
170
170
|
end
|
171
171
|
|
172
172
|
def star?; true; end
|
@@ -439,7 +439,7 @@ module ActionDispatch
|
|
439
439
|
end
|
440
440
|
|
441
441
|
def write_cookie?(cookie)
|
442
|
-
request.ssl? || !cookie[:secure] || always_write_cookie
|
442
|
+
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
443
443
|
end
|
444
444
|
|
445
445
|
def handle_options(options)
|
@@ -13,6 +13,9 @@ module ActionDispatch
|
|
13
13
|
begin
|
14
14
|
response = @app.call(env)
|
15
15
|
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
16
|
+
rescue => error
|
17
|
+
@executor.error_reporter.report(error, handled: false)
|
18
|
+
raise
|
16
19
|
ensure
|
17
20
|
state.complete! unless returned
|
18
21
|
end
|
@@ -11,8 +11,13 @@ module ActionDispatch
|
|
11
11
|
#
|
12
12
|
# When a request comes to an unauthorized host, the +response_app+
|
13
13
|
# application will be executed and rendered. If no +response_app+ is given, a
|
14
|
-
# default one will run
|
14
|
+
# default one will run.
|
15
|
+
# The default response app logs blocked host info with level 'error' and
|
16
|
+
# responds with <tt>403 Forbidden</tt>. The body of the response contains debug info
|
17
|
+
# if +config.consider_all_requests_local+ is set to true, otherwise the body is empty.
|
15
18
|
class HostAuthorization
|
19
|
+
ALLOWED_HOSTS_IN_DEVELOPMENT = [".localhost", /\A([a-z0-9-]+\.)?localhost:\d+\z/, IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")]
|
20
|
+
|
16
21
|
class Permissions # :nodoc:
|
17
22
|
def initialize(hosts)
|
18
23
|
@hosts = sanitize_hosts(hosts)
|
@@ -49,41 +54,58 @@ module ActionDispatch
|
|
49
54
|
|
50
55
|
def sanitize_string(host)
|
51
56
|
if host.start_with?(".")
|
52
|
-
/\A(
|
57
|
+
/\A([a-z0-9-]+\.)?#{Regexp.escape(host[1..-1])}\z/i
|
53
58
|
else
|
54
59
|
/\A#{Regexp.escape host}\z/i
|
55
60
|
end
|
56
61
|
end
|
57
62
|
end
|
58
63
|
|
59
|
-
|
60
|
-
|
64
|
+
class DefaultResponseApp # :nodoc:
|
65
|
+
RESPONSE_STATUS = 403
|
66
|
+
|
67
|
+
def call(env)
|
68
|
+
request = Request.new(env)
|
69
|
+
format = request.xhr? ? "text/plain" : "text/html"
|
70
|
+
|
71
|
+
log_error(request)
|
72
|
+
response(format, response_body(request))
|
73
|
+
end
|
61
74
|
|
62
|
-
|
63
|
-
|
64
|
-
|
75
|
+
private
|
76
|
+
def response_body(request)
|
77
|
+
return "" unless request.get_header("action_dispatch.show_detailed_exceptions")
|
78
|
+
|
79
|
+
template = DebugView.new(host: request.host)
|
80
|
+
template.render(template: "rescues/blocked_host", layout: "rescues/layout")
|
81
|
+
end
|
82
|
+
|
83
|
+
def response(format, body)
|
84
|
+
[RESPONSE_STATUS,
|
85
|
+
{ "Content-Type" => "#{format}; charset=#{Response.default_charset}",
|
86
|
+
"Content-Length" => body.bytesize.to_s },
|
87
|
+
[body]]
|
88
|
+
end
|
65
89
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
90
|
+
def log_error(request)
|
91
|
+
logger = available_logger(request)
|
92
|
+
|
93
|
+
return unless logger
|
94
|
+
|
95
|
+
logger.error("[#{self.class.name}] Blocked host: #{request.host}")
|
96
|
+
end
|
97
|
+
|
98
|
+
def available_logger(request)
|
99
|
+
request.logger || ActionView::Base.logger
|
100
|
+
end
|
70
101
|
end
|
71
102
|
|
72
|
-
def initialize(app, hosts,
|
103
|
+
def initialize(app, hosts, exclude: nil, response_app: nil)
|
73
104
|
@app = app
|
74
105
|
@permissions = Permissions.new(hosts)
|
75
106
|
@exclude = exclude
|
76
107
|
|
77
|
-
|
78
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
79
|
-
`action_dispatch.hosts_response_app` is deprecated and will be ignored in Rails 7.0.
|
80
|
-
Use the Host Authorization `response_app` setting instead.
|
81
|
-
MSG
|
82
|
-
|
83
|
-
response_app ||= deprecated_response_app
|
84
|
-
end
|
85
|
-
|
86
|
-
@response_app = response_app || DEFAULT_RESPONSE_APP
|
108
|
+
@response_app = response_app || DefaultResponseApp.new
|
87
109
|
end
|
88
110
|
|
89
111
|
def call(env)
|
@@ -100,13 +122,9 @@ module ActionDispatch
|
|
100
122
|
end
|
101
123
|
|
102
124
|
private
|
103
|
-
HOSTNAME = /[a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9.:]+\]/i
|
104
|
-
VALID_ORIGIN_HOST = /\A(#{HOSTNAME})(?::\d+)?\z/
|
105
|
-
VALID_FORWARDED_HOST = /(?:\A|,[ ]?)(#{HOSTNAME})(?::\d+)?\z/
|
106
|
-
|
107
125
|
def authorized?(request)
|
108
|
-
origin_host = request.get_header("HTTP_HOST")
|
109
|
-
forwarded_host = request.x_forwarded_host&.
|
126
|
+
origin_host = request.get_header("HTTP_HOST")
|
127
|
+
forwarded_host = request.x_forwarded_host&.split(/,\s?/)&.last
|
110
128
|
|
111
129
|
@permissions.allows?(origin_host) && (forwarded_host.blank? || @permissions.allows?(forwarded_host))
|
112
130
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/notifications"
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
class ServerTiming
|
7
|
+
SERVER_TIMING_HEADER = "Server-Timing"
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
events = []
|
15
|
+
subscriber = ActiveSupport::Notifications.subscribe(/.*/) do |*args|
|
16
|
+
events << ActiveSupport::Notifications::Event.new(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
status, headers, body = begin
|
20
|
+
@app.call(env)
|
21
|
+
ensure
|
22
|
+
ActiveSupport::Notifications.unsubscribe(subscriber)
|
23
|
+
end
|
24
|
+
|
25
|
+
header_info = events.group_by(&:name).map do |event_name, events_collection|
|
26
|
+
"#{event_name};dur=#{events_collection.sum(&:duration)}"
|
27
|
+
end
|
28
|
+
headers[SERVER_TIMING_HEADER] = header_info.join(", ")
|
29
|
+
|
30
|
+
[ status, headers, body ]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -40,6 +40,7 @@ module ActionDispatch
|
|
40
40
|
request.set_header "action_dispatch.exception", wrapper.unwrapped_exception
|
41
41
|
request.set_header "action_dispatch.original_path", request.path_info
|
42
42
|
request.set_header "action_dispatch.original_request_method", request.raw_request_method
|
43
|
+
fallback_to_html_format_if_invalid_mime_type(request)
|
43
44
|
request.path_info = "/#{status}"
|
44
45
|
request.request_method = "GET"
|
45
46
|
response = @exceptions_app.call(request.env)
|
@@ -54,6 +55,15 @@ module ActionDispatch
|
|
54
55
|
"went wrong."]]
|
55
56
|
end
|
56
57
|
|
58
|
+
def fallback_to_html_format_if_invalid_mime_type(request)
|
59
|
+
# If the MIME type for the request is invalid then the
|
60
|
+
# @exceptions_app may not be able to handle it. To make it
|
61
|
+
# easier to handle, we switch to HTML.
|
62
|
+
request.formats
|
63
|
+
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
64
|
+
request.set_header "HTTP_ACCEPT", "text/html"
|
65
|
+
end
|
66
|
+
|
57
67
|
def pass_response(status)
|
58
68
|
[status, { "Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0" }, []]
|
59
69
|
end
|
@@ -5,16 +5,6 @@ require "active_support/dependencies"
|
|
5
5
|
|
6
6
|
module ActionDispatch
|
7
7
|
class MiddlewareStack
|
8
|
-
class FakeRuntime # :nodoc:
|
9
|
-
def initialize(app)
|
10
|
-
@app = app
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(env)
|
14
|
-
@app.call(env)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
8
|
class Middleware
|
19
9
|
attr_reader :args, :block, :klass
|
20
10
|
|
@@ -79,7 +69,6 @@ module ActionDispatch
|
|
79
69
|
|
80
70
|
def initialize(*args)
|
81
71
|
@middlewares = []
|
82
|
-
@rack_runtime_deprecated = true
|
83
72
|
yield(self) if block_given?
|
84
73
|
end
|
85
74
|
|
@@ -187,24 +176,12 @@ module ActionDispatch
|
|
187
176
|
end
|
188
177
|
|
189
178
|
def build_middleware(klass, args, block)
|
190
|
-
@rack_runtime_deprecated = false if klass == Rack::Runtime
|
191
|
-
|
192
179
|
Middleware.new(klass, args, block)
|
193
180
|
end
|
194
181
|
|
195
182
|
def index_of(klass)
|
196
|
-
raise "ActionDispatch::MiddlewareStack::FakeRuntime can not be referenced in middleware operations" if klass == FakeRuntime
|
197
|
-
|
198
|
-
if klass == Rack::Runtime && @rack_runtime_deprecated
|
199
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
200
|
-
Rack::Runtime is removed from the default middleware stack in Rails
|
201
|
-
and referencing it in middleware operations without adding it back
|
202
|
-
is deprecated and will throw an error in Rails 7.1
|
203
|
-
MSG
|
204
|
-
end
|
205
|
-
|
206
183
|
middlewares.index do |m|
|
207
|
-
m.name == klass.name
|
184
|
+
m.name == klass.name
|
208
185
|
end
|
209
186
|
end
|
210
187
|
end
|
@@ -4,4 +4,5 @@
|
|
4
4
|
<main role="main" id="container">
|
5
5
|
<h2>To allow requests to <%= @host %> make sure it is a valid hostname (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:</h2>
|
6
6
|
<pre>config.hosts << "<%= @host %>"</pre>
|
7
|
+
<p>For more details view: <a href="https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization">the Host Authorization guide</a></p>
|
7
8
|
</main>
|
@@ -3,3 +3,5 @@ Blocked host: <%= @host %>
|
|
3
3
|
To allow requests to <%= @host %> make sure it is a valid hostname (containing only numbers, letters, dashes and dots), then add the following to your environment configuration:
|
4
4
|
|
5
5
|
config.hosts << "<%= @host %>"
|
6
|
+
|
7
|
+
For more details on host authorization view: https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization
|
@@ -146,7 +146,7 @@ module ActionDispatch
|
|
146
146
|
end
|
147
147
|
|
148
148
|
requirements, conditions = split_constraints ast.path_params, constraints
|
149
|
-
verify_regexp_requirements requirements.
|
149
|
+
verify_regexp_requirements requirements, ast.wildcard_options
|
150
150
|
|
151
151
|
formats = normalize_format(formatted)
|
152
152
|
|
@@ -246,14 +246,18 @@ module ActionDispatch
|
|
246
246
|
end
|
247
247
|
end
|
248
248
|
|
249
|
-
def verify_regexp_requirements(requirements)
|
250
|
-
requirements.each do |requirement|
|
251
|
-
|
249
|
+
def verify_regexp_requirements(requirements, wildcard_options)
|
250
|
+
requirements.each do |requirement, regex|
|
251
|
+
next unless regex.is_a? Regexp
|
252
|
+
|
253
|
+
if ANCHOR_CHARACTERS_REGEX.match?(regex.source)
|
252
254
|
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
|
253
255
|
end
|
254
256
|
|
255
|
-
if
|
256
|
-
|
257
|
+
if regex.multiline?
|
258
|
+
next if wildcard_options.key?(requirement)
|
259
|
+
|
260
|
+
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{regex.inspect}"
|
257
261
|
end
|
258
262
|
end
|
259
263
|
end
|