actionpack 5.2.1 → 7.0.2.4
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 +264 -220
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +24 -4
- data/lib/abstract_controller/caching/fragments.rb +8 -24
- data/lib/abstract_controller/caching.rb +2 -2
- data/lib/abstract_controller/callbacks.rb +34 -8
- data/lib/abstract_controller/collector.rb +5 -4
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +107 -90
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +19 -1
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +12 -5
- data/lib/abstract_controller/url_for.rb +4 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api.rb +5 -4
- data/lib/action_controller/base.rb +6 -9
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/log_subscriber.rb +13 -9
- data/lib/action_controller/metal/basic_implicit_render.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +57 -6
- data/lib/action_controller/metal/content_security_policy.rb +2 -3
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +9 -18
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +4 -6
- data/lib/action_controller/metal/exceptions.rb +55 -12
- data/lib/action_controller/metal/flash.rb +10 -6
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +41 -39
- data/lib/action_controller/metal/implicit_render.rb +5 -15
- data/lib/action_controller/metal/instrumentation.rb +59 -55
- data/lib/action_controller/metal/live.rb +80 -33
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +22 -7
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +50 -31
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +93 -23
- data/lib/action_controller/metal/renderers.rb +4 -4
- data/lib/action_controller/metal/rendering.rb +14 -9
- data/lib/action_controller/metal/request_forgery_protection.rb +160 -58
- data/lib/action_controller/metal/rescue.rb +2 -2
- data/lib/action_controller/metal/streaming.rb +1 -4
- data/lib/action_controller/metal/strong_parameters.rb +236 -88
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +1 -1
- data/lib/action_controller/metal.rb +16 -17
- data/lib/action_controller/railtie.rb +49 -6
- data/lib/action_controller/railties/helpers.rb +1 -1
- data/lib/action_controller/renderer.rb +37 -13
- data/lib/action_controller/template_assertions.rb +1 -1
- data/lib/action_controller/test_case.rb +98 -68
- data/lib/action_controller.rb +4 -5
- data/lib/action_dispatch/http/cache.rb +45 -32
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +69 -56
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -3
- data/lib/action_dispatch/http/headers.rb +4 -4
- data/lib/action_dispatch/http/mime_negotiation.rb +44 -16
- data/lib/action_dispatch/http/mime_type.rb +47 -30
- data/lib/action_dispatch/http/parameters.rb +18 -27
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/request.rb +49 -35
- data/lib/action_dispatch/http/response.rb +34 -26
- data/lib/action_dispatch/http/upload.rb +9 -1
- data/lib/action_dispatch/http/url.rb +86 -94
- data/lib/action_dispatch/journey/formatter.rb +55 -31
- data/lib/action_dispatch/journey/gtg/builder.rb +30 -46
- data/lib/action_dispatch/journey/gtg/simulator.rb +15 -8
- data/lib/action_dispatch/journey/gtg/transition_table.rb +78 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +83 -16
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +42 -34
- data/lib/action_dispatch/journey/route.rb +14 -31
- data/lib/action_dispatch/journey/router/utils.rb +16 -14
- data/lib/action_dispatch/journey/router.rb +27 -35
- data/lib/action_dispatch/journey/routes.rb +3 -5
- data/lib/action_dispatch/journey/scanner.rb +10 -4
- data/lib/action_dispatch/journey/visitors.rb +1 -4
- 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 +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +45 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -4
- data/lib/action_dispatch/middleware/cookies.rb +136 -113
- data/lib/action_dispatch/middleware/debug_exceptions.rb +47 -68
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +79 -30
- data/lib/action_dispatch/middleware/executor.rb +4 -1
- data/lib/action_dispatch/middleware/flash.rb +10 -12
- data/lib/action_dispatch/middleware/host_authorization.rb +159 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +6 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +30 -20
- data/lib/action_dispatch/middleware/request_id.rb +5 -6
- data/lib/action_dispatch/middleware/server_timing.rb +33 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +16 -3
- data/lib/action_dispatch/middleware/session/cache_store.rb +11 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +24 -19
- data/lib/action_dispatch/middleware/show_exceptions.rb +20 -11
- data/lib/action_dispatch/middleware/ssl.rb +20 -15
- data/lib/action_dispatch/middleware/stack.rb +79 -7
- data/lib/action_dispatch/middleware/static.rb +150 -94
- 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 +6 -11
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +25 -6
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +9 -6
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +4 -1
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +16 -2
- data/lib/action_dispatch/railtie.rb +16 -4
- data/lib/action_dispatch/request/session.rb +59 -22
- data/lib/action_dispatch/request/utils.rb +28 -2
- data/lib/action_dispatch/routing/inspector.rb +102 -54
- data/lib/action_dispatch/routing/mapper.rb +184 -156
- data/lib/action_dispatch/routing/polymorphic_routes.rb +21 -19
- data/lib/action_dispatch/routing/redirection.rb +4 -6
- data/lib/action_dispatch/routing/route_set.rb +83 -73
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +2 -3
- data/lib/action_dispatch/routing.rb +23 -22
- data/lib/action_dispatch/system_test_case.rb +65 -16
- data/lib/action_dispatch/system_testing/browser.rb +43 -16
- data/lib/action_dispatch/system_testing/driver.rb +42 -10
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +58 -12
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +3 -10
- data/lib/action_dispatch/testing/assertion_response.rb +0 -1
- data/lib/action_dispatch/testing/assertions/response.rb +4 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +20 -8
- data/lib/action_dispatch/testing/assertions.rb +3 -6
- data/lib/action_dispatch/testing/integration.rb +61 -30
- data/lib/action_dispatch/testing/request_encoder.rb +2 -2
- data/lib/action_dispatch/testing/test_process.rb +8 -6
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch/testing/test_response.rb +4 -32
- data/lib/action_dispatch.rb +15 -7
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack.rb +1 -1
- metadata +44 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -99
- data/lib/action_dispatch/http/parameter_filter.rb +0 -86
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -49
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -120
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +0 -26
@@ -1,20 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionController
|
4
|
-
class ActionControllerError < StandardError
|
4
|
+
class ActionControllerError < StandardError # :nodoc:
|
5
5
|
end
|
6
6
|
|
7
|
-
class BadRequest < ActionControllerError
|
7
|
+
class BadRequest < ActionControllerError # :nodoc:
|
8
8
|
def initialize(msg = nil)
|
9
9
|
super(msg)
|
10
10
|
set_backtrace $!.backtrace if $!
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
class RenderError < ActionControllerError
|
14
|
+
class RenderError < ActionControllerError # :nodoc:
|
15
15
|
end
|
16
16
|
|
17
|
-
class RoutingError < ActionControllerError
|
17
|
+
class RoutingError < ActionControllerError # :nodoc:
|
18
18
|
attr_reader :failures
|
19
19
|
def initialize(message, failures = [])
|
20
20
|
super(message)
|
@@ -22,22 +22,44 @@ module ActionController
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
class
|
25
|
+
class UrlGenerationError < ActionControllerError # :nodoc:
|
26
|
+
attr_reader :routes, :route_name, :method_name
|
27
|
+
|
28
|
+
def initialize(message, routes = nil, route_name = nil, method_name = nil)
|
29
|
+
@routes = routes
|
30
|
+
@route_name = route_name
|
31
|
+
@method_name = method_name
|
32
|
+
|
33
|
+
super(message)
|
34
|
+
end
|
35
|
+
|
36
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
37
|
+
include DidYouMean::Correctable
|
38
|
+
|
39
|
+
def corrections
|
40
|
+
@corrections ||= begin
|
41
|
+
maybe_these = routes&.named_routes&.helper_names&.grep(/#{route_name}/) || []
|
42
|
+
maybe_these -= [method_name.to_s] # remove exact match
|
43
|
+
|
44
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(route_name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
26
48
|
end
|
27
49
|
|
28
|
-
class MethodNotAllowed < ActionControllerError
|
50
|
+
class MethodNotAllowed < ActionControllerError # :nodoc:
|
29
51
|
def initialize(*allowed_methods)
|
30
|
-
super("Only #{allowed_methods.to_sentence
|
52
|
+
super("Only #{allowed_methods.to_sentence} requests are allowed.")
|
31
53
|
end
|
32
54
|
end
|
33
55
|
|
34
|
-
class NotImplemented < MethodNotAllowed
|
56
|
+
class NotImplemented < MethodNotAllowed # :nodoc:
|
35
57
|
end
|
36
58
|
|
37
|
-
class MissingFile < ActionControllerError
|
59
|
+
class MissingFile < ActionControllerError # :nodoc:
|
38
60
|
end
|
39
61
|
|
40
|
-
class SessionOverflowError < ActionControllerError
|
62
|
+
class SessionOverflowError < ActionControllerError # :nodoc:
|
41
63
|
DEFAULT_MESSAGE = "Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data."
|
42
64
|
|
43
65
|
def initialize(message = nil)
|
@@ -45,9 +67,30 @@ module ActionController
|
|
45
67
|
end
|
46
68
|
end
|
47
69
|
|
48
|
-
class UnknownHttpMethod < ActionControllerError
|
70
|
+
class UnknownHttpMethod < ActionControllerError # :nodoc:
|
71
|
+
end
|
72
|
+
|
73
|
+
class UnknownFormat < ActionControllerError # :nodoc:
|
74
|
+
end
|
75
|
+
|
76
|
+
# Raised when a nested respond_to is triggered and the content types of each
|
77
|
+
# are incompatible. For example:
|
78
|
+
#
|
79
|
+
# respond_to do |outer_type|
|
80
|
+
# outer_type.js do
|
81
|
+
# respond_to do |inner_type|
|
82
|
+
# inner_type.html { render body: "HTML" }
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
class RespondToMismatchError < ActionControllerError
|
87
|
+
DEFAULT_MESSAGE = "respond_to was called multiple times and matched with conflicting formats in this action. Please note that you may only call respond_to and match on a single format per action."
|
88
|
+
|
89
|
+
def initialize(message = nil)
|
90
|
+
super(message || DEFAULT_MESSAGE)
|
91
|
+
end
|
49
92
|
end
|
50
93
|
|
51
|
-
class
|
94
|
+
class MissingExactTemplate < UnknownFormat # :nodoc:
|
52
95
|
end
|
53
96
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionController
|
3
|
+
module ActionController # :nodoc:
|
4
4
|
module Flash
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
@@ -36,26 +36,30 @@ module ActionController #:nodoc:
|
|
36
36
|
define_method(type) do
|
37
37
|
request.flash[type]
|
38
38
|
end
|
39
|
-
helper_method
|
39
|
+
helper_method(type) if respond_to?(:helper_method)
|
40
40
|
|
41
41
|
self._flash_types += [type]
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
45
|
+
def action_methods # :nodoc:
|
46
|
+
@action_methods ||= super - _flash_types.map(&:to_s).to_set
|
47
|
+
end
|
44
48
|
end
|
45
49
|
|
46
50
|
private
|
47
|
-
def redirect_to(options = {},
|
51
|
+
def redirect_to(options = {}, response_options_and_flash = {}) # :doc:
|
48
52
|
self.class._flash_types.each do |flash_type|
|
49
|
-
if type =
|
53
|
+
if type = response_options_and_flash.delete(flash_type)
|
50
54
|
flash[flash_type] = type
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
54
|
-
if other_flashes =
|
58
|
+
if other_flashes = response_options_and_flash.delete(:flash)
|
55
59
|
flash.update(other_flashes)
|
56
60
|
end
|
57
61
|
|
58
|
-
super(options,
|
62
|
+
super(options, response_options_and_flash)
|
59
63
|
end
|
60
64
|
end
|
61
65
|
end
|
@@ -29,19 +29,22 @@ module ActionController
|
|
29
29
|
content_type = options.delete(:content_type)
|
30
30
|
|
31
31
|
options.each do |key, value|
|
32
|
-
headers[key.to_s.
|
32
|
+
headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
|
33
33
|
end
|
34
34
|
|
35
35
|
self.status = status
|
36
36
|
self.location = url_for(location) if location
|
37
37
|
|
38
|
-
self.response_body = ""
|
39
|
-
|
40
38
|
if include_content?(response_code)
|
41
|
-
self.
|
39
|
+
unless self.media_type
|
40
|
+
self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
|
41
|
+
end
|
42
|
+
|
42
43
|
response.charset = false
|
43
44
|
end
|
44
45
|
|
46
|
+
self.response_body = ""
|
47
|
+
|
45
48
|
true
|
46
49
|
end
|
47
50
|
|
@@ -11,7 +11,12 @@ module ActionController
|
|
11
11
|
#
|
12
12
|
# In previous versions of \Rails the controller will include a helper which
|
13
13
|
# matches the name of the controller, e.g., <tt>MyController</tt> will automatically
|
14
|
-
# include <tt>MyHelper</tt>.
|
14
|
+
# include <tt>MyHelper</tt>. You can revert to the old behavior with the following:
|
15
|
+
#
|
16
|
+
# # config/application.rb
|
17
|
+
# class Application < Rails::Application
|
18
|
+
# config.action_controller.include_all_helpers = false
|
19
|
+
# end
|
15
20
|
#
|
16
21
|
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
|
17
22
|
# controller which inherits from it.
|
@@ -21,7 +26,7 @@ module ActionController
|
|
21
26
|
#
|
22
27
|
# module FormattedTimeHelper
|
23
28
|
# def format_time(time, format=:long, blank_message=" ")
|
24
|
-
# time.blank? ? blank_message : time.
|
29
|
+
# time.blank? ? blank_message : time.to_fs(format)
|
25
30
|
# end
|
26
31
|
# end
|
27
32
|
#
|
@@ -34,7 +39,7 @@ module ActionController
|
|
34
39
|
# end
|
35
40
|
# end
|
36
41
|
#
|
37
|
-
# Then, in any view rendered by <tt>
|
42
|
+
# Then, in any view rendered by <tt>EventsController</tt>, the <tt>format_time</tt> method can be called:
|
38
43
|
#
|
39
44
|
# <% @events.each do |event| -%>
|
40
45
|
# <p>
|
@@ -73,9 +78,14 @@ module ActionController
|
|
73
78
|
end
|
74
79
|
|
75
80
|
# Provides a proxy to access helper methods from outside the view.
|
81
|
+
#
|
82
|
+
# Note that the proxy is rendered under a different view context.
|
83
|
+
# This may cause incorrect behaviour with capture methods. Consider
|
84
|
+
# using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper]
|
85
|
+
# instead when using +capture+.
|
76
86
|
def helpers
|
77
87
|
@helper_proxy ||= begin
|
78
|
-
proxy = ActionView::Base.
|
88
|
+
proxy = ActionView::Base.empty
|
79
89
|
proxy.config = config.inheritable_copy
|
80
90
|
proxy.extend(_helpers)
|
81
91
|
end
|
@@ -100,8 +110,7 @@ module ActionController
|
|
100
110
|
# # => ["application", "chart", "rubygems"]
|
101
111
|
def all_helpers_from_path(path)
|
102
112
|
helpers = Array(path).flat_map do |_path|
|
103
|
-
|
104
|
-
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
|
113
|
+
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
|
105
114
|
names.sort!
|
106
115
|
end
|
107
116
|
helpers.uniq!
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
require "base64"
|
4
4
|
require "active_support/security_utils"
|
5
|
+
require "active_support/core_ext/array/access"
|
5
6
|
|
6
7
|
module ActionController
|
7
|
-
#
|
8
|
+
# HTTP Basic, Digest and Token authentication.
|
8
9
|
module HttpAuthentication
|
9
|
-
#
|
10
|
+
# HTTP \Basic authentication.
|
10
11
|
#
|
11
12
|
# === Simple \Basic example
|
12
13
|
#
|
@@ -24,8 +25,8 @@ module ActionController
|
|
24
25
|
#
|
25
26
|
# === Advanced \Basic example
|
26
27
|
#
|
27
|
-
# Here is a more advanced \Basic example where only Atom feeds and the XML API
|
28
|
-
#
|
28
|
+
# Here is a more advanced \Basic example where only Atom feeds and the XML API are protected by HTTP authentication.
|
29
|
+
# The regular HTML interface is protected by a session approach:
|
29
30
|
#
|
30
31
|
# class ApplicationController < ActionController::Base
|
31
32
|
# before_action :set_account, :authenticate
|
@@ -56,8 +57,9 @@ module ActionController
|
|
56
57
|
# In your integration tests, you can do something like this:
|
57
58
|
#
|
58
59
|
# def test_access_granted_from_xml
|
59
|
-
#
|
60
|
-
#
|
60
|
+
# authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
61
|
+
#
|
62
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
61
63
|
#
|
62
64
|
# assert_equal 200, status
|
63
65
|
# end
|
@@ -68,21 +70,22 @@ module ActionController
|
|
68
70
|
extend ActiveSupport::Concern
|
69
71
|
|
70
72
|
module ClassMethods
|
71
|
-
def http_basic_authenticate_with(
|
72
|
-
before_action(options
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
73
|
+
def http_basic_authenticate_with(name:, password:, realm: nil, **options)
|
74
|
+
before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
|
79
|
+
authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
|
80
|
+
# This comparison uses & so that it doesn't short circuit and
|
81
|
+
# uses `secure_compare` so that length information isn't leaked.
|
82
|
+
ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
|
83
|
+
ActiveSupport::SecurityUtils.secure_compare(given_password, password)
|
81
84
|
end
|
82
85
|
end
|
83
86
|
|
84
|
-
def authenticate_or_request_with_http_basic(realm =
|
85
|
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
|
87
|
+
def authenticate_or_request_with_http_basic(realm = nil, message = nil, &login_procedure)
|
88
|
+
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm || "Application", message)
|
86
89
|
end
|
87
90
|
|
88
91
|
def authenticate_with_http_basic(&login_procedure)
|
@@ -101,7 +104,7 @@ module ActionController
|
|
101
104
|
end
|
102
105
|
|
103
106
|
def has_basic_credentials?(request)
|
104
|
-
request.authorization.present? && (auth_scheme(request).downcase == "basic")
|
107
|
+
request.authorization.present? && (auth_scheme(request).downcase == "basic") && user_name_and_password(request).length == 2
|
105
108
|
end
|
106
109
|
|
107
110
|
def user_name_and_password(request)
|
@@ -126,21 +129,21 @@ module ActionController
|
|
126
129
|
|
127
130
|
def authentication_request(controller, realm, message)
|
128
131
|
message ||= "HTTP Basic: Access denied.\n"
|
129
|
-
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'
|
132
|
+
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"', "")}")
|
130
133
|
controller.status = 401
|
131
134
|
controller.response_body = message
|
132
135
|
end
|
133
136
|
end
|
134
137
|
|
135
|
-
#
|
138
|
+
# HTTP \Digest authentication.
|
136
139
|
#
|
137
140
|
# === Simple \Digest example
|
138
141
|
#
|
139
|
-
# require
|
142
|
+
# require "openssl"
|
140
143
|
# class PostsController < ApplicationController
|
141
144
|
# REALM = "SuperSecret"
|
142
145
|
# USERS = {"dhh" => "secret", #plain text password
|
143
|
-
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
146
|
+
# "dap" => OpenSSL::Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
144
147
|
#
|
145
148
|
# before_action :authenticate, except: [:index]
|
146
149
|
#
|
@@ -228,12 +231,12 @@ module ActionController
|
|
228
231
|
# of a plain-text password.
|
229
232
|
def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
|
230
233
|
ha1 = password_is_ha1 ? password : ha1(credentials, password)
|
231
|
-
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
|
232
|
-
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
|
234
|
+
ha2 = OpenSSL::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
|
235
|
+
OpenSSL::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
|
233
236
|
end
|
234
237
|
|
235
238
|
def ha1(credentials, password)
|
236
|
-
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
|
239
|
+
OpenSSL::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
|
237
240
|
end
|
238
241
|
|
239
242
|
def encode_credentials(http_method, credentials, password, password_is_ha1)
|
@@ -307,7 +310,7 @@ module ActionController
|
|
307
310
|
def nonce(secret_key, time = Time.now)
|
308
311
|
t = time.to_i
|
309
312
|
hashed = [t, secret_key]
|
310
|
-
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
|
313
|
+
digest = OpenSSL::Digest::MD5.hexdigest(hashed.join(":"))
|
311
314
|
::Base64.strict_encode64("#{t}:#{digest}")
|
312
315
|
end
|
313
316
|
|
@@ -324,11 +327,11 @@ module ActionController
|
|
324
327
|
|
325
328
|
# Opaque based on digest of secret key
|
326
329
|
def opaque(secret_key)
|
327
|
-
::Digest::MD5.hexdigest(secret_key)
|
330
|
+
OpenSSL::Digest::MD5.hexdigest(secret_key)
|
328
331
|
end
|
329
332
|
end
|
330
333
|
|
331
|
-
#
|
334
|
+
# HTTP Token authentication.
|
332
335
|
#
|
333
336
|
# Simple Token example:
|
334
337
|
#
|
@@ -356,8 +359,8 @@ module ActionController
|
|
356
359
|
# end
|
357
360
|
#
|
358
361
|
#
|
359
|
-
# Here is a more advanced Token example where only Atom feeds and the XML API
|
360
|
-
#
|
362
|
+
# Here is a more advanced Token example where only Atom feeds and the XML API are protected by HTTP token authentication.
|
363
|
+
# The regular HTML interface is protected by a session approach:
|
361
364
|
#
|
362
365
|
# class ApplicationController < ActionController::Base
|
363
366
|
# before_action :set_account, :authenticate
|
@@ -389,10 +392,9 @@ module ActionController
|
|
389
392
|
# In your integration tests, you can do something like this:
|
390
393
|
#
|
391
394
|
# def test_access_granted_from_xml
|
392
|
-
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
# )
|
395
|
+
# authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
|
396
|
+
#
|
397
|
+
# get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
|
396
398
|
#
|
397
399
|
# assert_equal 200, status
|
398
400
|
# end
|
@@ -406,7 +408,7 @@ module ActionController
|
|
406
408
|
module Token
|
407
409
|
TOKEN_KEY = "token="
|
408
410
|
TOKEN_REGEX = /^(Token|Bearer)\s+/
|
409
|
-
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t
|
411
|
+
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t)/
|
410
412
|
extend self
|
411
413
|
|
412
414
|
module ControllerMethods
|
@@ -474,7 +476,7 @@ module ActionController
|
|
474
476
|
|
475
477
|
# This removes the <tt>"</tt> characters wrapping the value.
|
476
478
|
def rewrite_param_values(array_params)
|
477
|
-
array_params.each { |param| (param[1] || ""
|
479
|
+
array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
|
478
480
|
end
|
479
481
|
|
480
482
|
# This method takes an authorization body and splits up the key-value
|
@@ -483,7 +485,7 @@ module ActionController
|
|
483
485
|
def raw_params(auth)
|
484
486
|
_raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
485
487
|
|
486
|
-
if !
|
488
|
+
if !_raw_params.first&.start_with?(TOKEN_KEY)
|
487
489
|
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
488
490
|
end
|
489
491
|
|
@@ -511,7 +513,7 @@ module ActionController
|
|
511
513
|
# Returns nothing.
|
512
514
|
def authentication_request(controller, realm, message = nil)
|
513
515
|
message ||= "HTTP Token: Access denied.\n"
|
514
|
-
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'
|
516
|
+
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
|
515
517
|
controller.__send__ :render, plain: message, status: :unauthorized
|
516
518
|
end
|
517
519
|
end
|
@@ -22,7 +22,7 @@ module ActionController
|
|
22
22
|
# Third, if we DON'T find a template AND the request is a page load in a web
|
23
23
|
# browser (technically, a non-XHR GET request for an HTML response) where
|
24
24
|
# you reasonably expect to have rendered a template, then we raise
|
25
|
-
# <tt>
|
25
|
+
# <tt>ActionController::MissingExactTemplate</tt> with an explanation.
|
26
26
|
#
|
27
27
|
# Finally, if we DON'T find a template AND the request isn't a browser page
|
28
28
|
# load, then we implicitly respond with <tt>204 No Content</tt>.
|
@@ -30,9 +30,9 @@ module ActionController
|
|
30
30
|
# :stopdoc:
|
31
31
|
include BasicImplicitRender
|
32
32
|
|
33
|
-
def default_render
|
33
|
+
def default_render
|
34
34
|
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
|
35
|
-
render
|
35
|
+
render
|
36
36
|
elsif any_templates?(action_name.to_s, _prefixes)
|
37
37
|
message = "#{self.class.name}\##{action_name} is missing a template " \
|
38
38
|
"for this request format and variant.\n" \
|
@@ -41,18 +41,8 @@ module ActionController
|
|
41
41
|
|
42
42
|
raise ActionController::UnknownFormat, message
|
43
43
|
elsif interactive_browser_request?
|
44
|
-
message = "#{self.class.name}\##{action_name} is missing a template "
|
45
|
-
|
46
|
-
"request.formats: #{request.formats.map(&:to_s).inspect}\n" \
|
47
|
-
"request.variant: #{request.variant.inspect}\n\n" \
|
48
|
-
"NOTE! For XHR/Ajax or API requests, this action would normally " \
|
49
|
-
"respond with 204 No Content: an empty white screen. Since you're " \
|
50
|
-
"loading it in a web browser, we assume that you expected to " \
|
51
|
-
"actually render a template, not nothing, so we're showing an " \
|
52
|
-
"error to be extra-clear. If you expect 204 No Content, carry on. " \
|
53
|
-
"That's what you'll get from an XHR or API request. Give it a shot."
|
54
|
-
|
55
|
-
raise ActionController::UnknownFormat, message
|
44
|
+
message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
|
45
|
+
raise ActionController::MissingExactTemplate, message
|
56
46
|
else
|
57
47
|
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
|
58
48
|
super
|
@@ -16,31 +16,7 @@ module ActionController
|
|
16
16
|
|
17
17
|
attr_internal :view_runtime
|
18
18
|
|
19
|
-
def
|
20
|
-
raw_payload = {
|
21
|
-
controller: self.class.name,
|
22
|
-
action: action_name,
|
23
|
-
params: request.filtered_parameters,
|
24
|
-
headers: request.headers,
|
25
|
-
format: request.format.ref,
|
26
|
-
method: request.request_method,
|
27
|
-
path: request.fullpath
|
28
|
-
}
|
29
|
-
|
30
|
-
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
31
|
-
|
32
|
-
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
33
|
-
begin
|
34
|
-
result = super
|
35
|
-
payload[:status] = response.status
|
36
|
-
result
|
37
|
-
ensure
|
38
|
-
append_info_to_payload(payload)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def render(*args)
|
19
|
+
def render(*)
|
44
20
|
render_output = nil
|
45
21
|
self.view_runtime = cleanup_view_runtime do
|
46
22
|
Benchmark.ms { render_output = super }
|
@@ -61,8 +37,8 @@ module ActionController
|
|
61
37
|
end
|
62
38
|
end
|
63
39
|
|
64
|
-
def redirect_to(*
|
65
|
-
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
|
40
|
+
def redirect_to(*)
|
41
|
+
ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
|
66
42
|
result = super
|
67
43
|
payload[:status] = response.status
|
68
44
|
payload[:location] = response.filtered_location
|
@@ -70,38 +46,66 @@ module ActionController
|
|
70
46
|
end
|
71
47
|
end
|
72
48
|
|
73
|
-
|
49
|
+
private
|
50
|
+
def process_action(*)
|
51
|
+
ActiveSupport::ExecutionContext[:controller] = self
|
74
52
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
53
|
+
raw_payload = {
|
54
|
+
controller: self.class.name,
|
55
|
+
action: action_name,
|
56
|
+
request: request,
|
57
|
+
params: request.filtered_parameters,
|
58
|
+
headers: request.headers,
|
59
|
+
format: request.format.ref,
|
60
|
+
method: request.request_method,
|
61
|
+
path: request.fullpath
|
62
|
+
}
|
79
63
|
|
80
|
-
|
81
|
-
# views, like database querying time.
|
82
|
-
#
|
83
|
-
# def cleanup_view_runtime
|
84
|
-
# super - time_taken_in_something_expensive
|
85
|
-
# end
|
86
|
-
def cleanup_view_runtime # :doc:
|
87
|
-
yield
|
88
|
-
end
|
64
|
+
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
|
89
65
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
66
|
+
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
67
|
+
result = super
|
68
|
+
payload[:response] = response
|
69
|
+
payload[:status] = response.status
|
70
|
+
result
|
71
|
+
rescue => error
|
72
|
+
payload[:status] = ActionDispatch::ExceptionWrapper.status_code_for_exception(error.class.name)
|
73
|
+
raise
|
74
|
+
ensure
|
75
|
+
append_info_to_payload(payload)
|
76
|
+
end
|
77
|
+
end
|
95
78
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
79
|
+
# A hook invoked every time a before callback is halted.
|
80
|
+
def halted_callback_hook(filter, _)
|
81
|
+
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
82
|
+
end
|
83
|
+
|
84
|
+
# A hook which allows you to clean up any time, wrongly taken into account in
|
85
|
+
# views, like database querying time.
|
86
|
+
#
|
87
|
+
# def cleanup_view_runtime
|
88
|
+
# super - time_taken_in_something_expensive
|
89
|
+
# end
|
90
|
+
def cleanup_view_runtime # :doc:
|
91
|
+
yield
|
92
|
+
end
|
93
|
+
|
94
|
+
# Every time after an action is processed, this method is invoked
|
95
|
+
# with the payload, so you can add more information.
|
96
|
+
def append_info_to_payload(payload) # :doc:
|
97
|
+
payload[:view_runtime] = view_runtime
|
98
|
+
end
|
99
|
+
|
100
|
+
module ClassMethods
|
101
|
+
# A hook which allows other frameworks to log what happened during
|
102
|
+
# controller process action. This method should return an array
|
103
|
+
# with the messages to be added.
|
104
|
+
def log_process_action(payload) # :nodoc:
|
105
|
+
messages, view_runtime = [], payload[:view_runtime]
|
106
|
+
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
|
107
|
+
messages
|
108
|
+
end
|
104
109
|
end
|
105
|
-
end
|
106
110
|
end
|
107
111
|
end
|