actionpack 6.1.7.5 → 7.1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +355 -435
- data/MIT-LICENSE +2 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +33 -37
- data/lib/abstract_controller/caching/fragments.rb +4 -2
- data/lib/abstract_controller/caching.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +50 -11
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/error.rb +1 -1
- data/lib/abstract_controller/helpers.rb +78 -30
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +12 -14
- data/lib/abstract_controller/translation.rb +26 -7
- data/lib/abstract_controller/url_for.rb +6 -6
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +12 -10
- data/lib/action_controller/base.rb +8 -21
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +4 -2
- data/lib/action_controller/log_subscriber.rb +20 -7
- data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
- data/lib/action_controller/metal/conditional_get.rb +137 -102
- data/lib/action_controller/metal/content_security_policy.rb +37 -3
- data/lib/action_controller/metal/cookies.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +25 -31
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +27 -30
- data/lib/action_controller/metal/flash.rb +6 -2
- data/lib/action_controller/metal/head.rb +9 -7
- data/lib/action_controller/metal/helpers.rb +5 -16
- data/lib/action_controller/metal/http_authentication.rb +78 -42
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +62 -50
- data/lib/action_controller/metal/live.rb +67 -2
- data/lib/action_controller/metal/mime_responds.rb +5 -5
- data/lib/action_controller/metal/params_wrapper.rb +24 -13
- data/lib/action_controller/metal/permissions_policy.rb +20 -29
- data/lib/action_controller/metal/redirecting.rb +96 -23
- data/lib/action_controller/metal/renderers.rb +14 -15
- data/lib/action_controller/metal/rendering.rb +121 -16
- data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
- data/lib/action_controller/metal/rescue.rb +7 -4
- data/lib/action_controller/metal/streaming.rb +74 -36
- data/lib/action_controller/metal/strong_parameters.rb +254 -151
- data/lib/action_controller/metal/testing.rb +9 -2
- data/lib/action_controller/metal/url_for.rb +10 -5
- data/lib/action_controller/metal.rb +89 -34
- data/lib/action_controller/railtie.rb +66 -9
- data/lib/action_controller/renderer.rb +99 -85
- data/lib/action_controller/test_case.rb +42 -11
- data/lib/action_controller.rb +10 -6
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +21 -16
- data/lib/action_dispatch/http/content_security_policy.rb +122 -44
- data/lib/action_dispatch/http/filter_parameters.rb +14 -23
- data/lib/action_dispatch/http/headers.rb +3 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
- data/lib/action_dispatch/http/mime_type.rb +43 -22
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +6 -6
- data/lib/action_dispatch/http/permissions_policy.rb +57 -19
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +75 -51
- data/lib/action_dispatch/http/response.rb +81 -77
- data/lib/action_dispatch/http/upload.rb +15 -2
- data/lib/action_dispatch/http/url.rb +11 -19
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
- data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
- data/lib/action_dispatch/journey/nodes/node.rb +70 -5
- data/lib/action_dispatch/journey/path/pattern.rb +36 -27
- data/lib/action_dispatch/journey/route.rb +8 -14
- data/lib/action_dispatch/journey/router/utils.rb +2 -2
- data/lib/action_dispatch/journey/router.rb +10 -9
- data/lib/action_dispatch/journey/routes.rb +5 -5
- 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/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +97 -107
- data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
- data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
- data/lib/action_dispatch/middleware/executor.rb +3 -0
- data/lib/action_dispatch/middleware/flash.rb +24 -18
- data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
- data/lib/action_dispatch/middleware/request_id.rb +5 -3
- data/lib/action_dispatch/middleware/server_timing.rb +76 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +34 -11
- data/lib/action_dispatch/middleware/static.rb +16 -16
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
- 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 +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
- data/lib/action_dispatch/railtie.rb +20 -4
- data/lib/action_dispatch/request/session.rb +59 -19
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +55 -7
- data/lib/action_dispatch/routing/mapper.rb +117 -107
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +20 -8
- data/lib/action_dispatch/routing/route_set.rb +67 -27
- data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
- data/lib/action_dispatch/routing/url_for.rb +29 -26
- data/lib/action_dispatch/routing.rb +12 -13
- data/lib/action_dispatch/system_test_case.rb +8 -8
- data/lib/action_dispatch/system_testing/browser.rb +20 -29
- data/lib/action_dispatch/system_testing/driver.rb +34 -18
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +14 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
- data/lib/action_dispatch/testing/assertions.rb +3 -4
- data/lib/action_dispatch/testing/integration.rb +33 -25
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +5 -30
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +34 -2
- data/lib/action_dispatch.rb +38 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +67 -30
@@ -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,7 +22,7 @@ module ActionController
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
class UrlGenerationError < ActionControllerError
|
25
|
+
class UrlGenerationError < ActionControllerError # :nodoc:
|
26
26
|
attr_reader :routes, :route_name, :method_name
|
27
27
|
|
28
28
|
def initialize(message, routes = nil, route_name = nil, method_name = nil)
|
@@ -33,44 +33,33 @@ module ActionController
|
|
33
33
|
super(message)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
@error = error
|
39
|
-
end
|
36
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
37
|
+
include DidYouMean::Correctable
|
40
38
|
|
41
39
|
def corrections
|
42
|
-
|
43
|
-
maybe_these =
|
44
|
-
maybe_these -= [
|
45
|
-
|
46
|
-
maybe_these.
|
47
|
-
DidYouMean::Jaro.distance(@error.route_name, n)
|
48
|
-
}.reverse.first(4)
|
49
|
-
else
|
50
|
-
[]
|
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)
|
51
45
|
end
|
52
46
|
end
|
53
47
|
end
|
54
|
-
|
55
|
-
# We may not have DYM, and DYM might not let us register error handlers
|
56
|
-
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
57
|
-
DidYouMean.correct_error(self, Correction)
|
58
|
-
end
|
59
48
|
end
|
60
49
|
|
61
|
-
class MethodNotAllowed < ActionControllerError
|
50
|
+
class MethodNotAllowed < ActionControllerError # :nodoc:
|
62
51
|
def initialize(*allowed_methods)
|
63
52
|
super("Only #{allowed_methods.to_sentence} requests are allowed.")
|
64
53
|
end
|
65
54
|
end
|
66
55
|
|
67
|
-
class NotImplemented < MethodNotAllowed
|
56
|
+
class NotImplemented < MethodNotAllowed # :nodoc:
|
68
57
|
end
|
69
58
|
|
70
|
-
class MissingFile < ActionControllerError
|
59
|
+
class MissingFile < ActionControllerError # :nodoc:
|
71
60
|
end
|
72
61
|
|
73
|
-
class SessionOverflowError < ActionControllerError
|
62
|
+
class SessionOverflowError < ActionControllerError # :nodoc:
|
74
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."
|
75
64
|
|
76
65
|
def initialize(message = nil)
|
@@ -78,10 +67,10 @@ module ActionController
|
|
78
67
|
end
|
79
68
|
end
|
80
69
|
|
81
|
-
class UnknownHttpMethod < ActionControllerError
|
70
|
+
class UnknownHttpMethod < ActionControllerError # :nodoc:
|
82
71
|
end
|
83
72
|
|
84
|
-
class UnknownFormat < ActionControllerError
|
73
|
+
class UnknownFormat < ActionControllerError # :nodoc:
|
85
74
|
end
|
86
75
|
|
87
76
|
# Raised when a nested respond_to is triggered and the content types of each
|
@@ -102,6 +91,14 @@ module ActionController
|
|
102
91
|
end
|
103
92
|
end
|
104
93
|
|
105
|
-
class MissingExactTemplate < UnknownFormat
|
94
|
+
class MissingExactTemplate < UnknownFormat # :nodoc:
|
95
|
+
attr_reader :controller, :action_name
|
96
|
+
|
97
|
+
def initialize(message, controller, action_name)
|
98
|
+
@controller = controller
|
99
|
+
@action_name = action_name
|
100
|
+
|
101
|
+
super(message)
|
102
|
+
end
|
106
103
|
end
|
107
104
|
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
|
|
@@ -41,10 +41,14 @@ module ActionController #:nodoc:
|
|
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 = {}, response_options_and_flash = {})
|
51
|
+
def redirect_to(options = {}, response_options_and_flash = {}) # :doc:
|
48
52
|
self.class._flash_types.each do |flash_type|
|
49
53
|
if type = response_options_and_flash.delete(flash_type)
|
50
54
|
flash[flash_type] = type
|
@@ -17,19 +17,21 @@ module ActionController
|
|
17
17
|
# return head(:bad_request) unless valid_request?
|
18
18
|
# render
|
19
19
|
#
|
20
|
-
# See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list of valid +status+ symbols.
|
21
|
-
def head(status, options =
|
20
|
+
# See +Rack::Utils::SYMBOL_TO_STATUS_CODE+ for a full list of valid +status+ symbols.
|
21
|
+
def head(status, options = nil)
|
22
22
|
if status.is_a?(Hash)
|
23
23
|
raise ArgumentError, "#{status.inspect} is not a valid value for `status`."
|
24
24
|
end
|
25
25
|
|
26
26
|
status ||= :ok
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
if options
|
29
|
+
location = options.delete(:location)
|
30
|
+
content_type = options.delete(:content_type)
|
30
31
|
|
31
|
-
|
32
|
-
|
32
|
+
options.each do |key, value|
|
33
|
+
headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
|
34
|
+
end
|
33
35
|
end
|
34
36
|
|
35
37
|
self.status = status
|
@@ -37,7 +39,7 @@ module ActionController
|
|
37
39
|
|
38
40
|
if include_content?(response_code)
|
39
41
|
unless self.media_type
|
40
|
-
self.content_type = content_type || (Mime[
|
42
|
+
self.content_type = content_type || ((f = formats) && Mime[f.first]) || Mime[:html]
|
41
43
|
end
|
42
44
|
|
43
45
|
response.charset = false
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionController
|
4
|
+
# = Action Controller \Helpers
|
5
|
+
#
|
4
6
|
# The \Rails framework provides a large number of helpers for working with assets, dates, forms,
|
5
7
|
# numbers and model objects, to name a few. These helpers are available to all templates
|
6
8
|
# by default.
|
@@ -26,7 +28,7 @@ module ActionController
|
|
26
28
|
#
|
27
29
|
# module FormattedTimeHelper
|
28
30
|
# def format_time(time, format=:long, blank_message=" ")
|
29
|
-
# time.blank? ? blank_message : time.
|
31
|
+
# time.blank? ? blank_message : time.to_fs(format)
|
30
32
|
# end
|
31
33
|
# end
|
32
34
|
#
|
@@ -80,7 +82,7 @@ module ActionController
|
|
80
82
|
# Provides a proxy to access helper methods from outside the view.
|
81
83
|
#
|
82
84
|
# Note that the proxy is rendered under a different view context.
|
83
|
-
# This may cause incorrect
|
85
|
+
# This may cause incorrect behavior with capture methods. Consider
|
84
86
|
# using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper]
|
85
87
|
# instead when using +capture+.
|
86
88
|
def helpers
|
@@ -91,7 +93,7 @@ module ActionController
|
|
91
93
|
end
|
92
94
|
end
|
93
95
|
|
94
|
-
#
|
96
|
+
# Override modules_for_helpers to accept +:all+ as argument, which loads
|
95
97
|
# all helpers in helpers_path.
|
96
98
|
#
|
97
99
|
# ==== Parameters
|
@@ -104,19 +106,6 @@ module ActionController
|
|
104
106
|
super(args)
|
105
107
|
end
|
106
108
|
|
107
|
-
# Returns a list of helper names in a given path.
|
108
|
-
#
|
109
|
-
# ActionController::Base.all_helpers_from_path 'app/helpers'
|
110
|
-
# # => ["application", "chart", "rubygems"]
|
111
|
-
def all_helpers_from_path(path)
|
112
|
-
helpers = Array(path).flat_map do |_path|
|
113
|
-
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
|
114
|
-
names.sort!
|
115
|
-
end
|
116
|
-
helpers.uniq!
|
117
|
-
helpers
|
118
|
-
end
|
119
|
-
|
120
109
|
private
|
121
110
|
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
|
122
111
|
def all_application_helpers
|
@@ -5,9 +5,9 @@ require "active_support/security_utils"
|
|
5
5
|
require "active_support/core_ext/array/access"
|
6
6
|
|
7
7
|
module ActionController
|
8
|
-
#
|
8
|
+
# HTTP Basic, Digest, and Token authentication.
|
9
9
|
module HttpAuthentication
|
10
|
-
#
|
10
|
+
# = HTTP \Basic authentication
|
11
11
|
#
|
12
12
|
# === Simple \Basic example
|
13
13
|
#
|
@@ -21,12 +21,12 @@ module ActionController
|
|
21
21
|
# def edit
|
22
22
|
# render plain: "I'm only accessible if you know the password"
|
23
23
|
# end
|
24
|
-
#
|
24
|
+
# end
|
25
25
|
#
|
26
26
|
# === Advanced \Basic example
|
27
27
|
#
|
28
|
-
# Here is a more advanced \Basic example where only Atom feeds and the XML API
|
29
|
-
#
|
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:
|
30
30
|
#
|
31
31
|
# class ApplicationController < ActionController::Base
|
32
32
|
# before_action :set_account, :authenticate
|
@@ -70,7 +70,12 @@ module ActionController
|
|
70
70
|
extend ActiveSupport::Concern
|
71
71
|
|
72
72
|
module ClassMethods
|
73
|
+
# Enables HTTP \Basic authentication.
|
74
|
+
#
|
75
|
+
# See ActionController::HttpAuthentication::Basic for example usage.
|
73
76
|
def http_basic_authenticate_with(name:, password:, realm: nil, **options)
|
77
|
+
raise ArgumentError, "Expected name: to be a String, got #{name.class}" unless name.is_a?(String)
|
78
|
+
raise ArgumentError, "Expected password: to be a String, got #{password.class}" unless password.is_a?(String)
|
74
79
|
before_action(options) { http_basic_authenticate_or_request_with name: name, password: password, realm: realm }
|
75
80
|
end
|
76
81
|
end
|
@@ -79,8 +84,8 @@ module ActionController
|
|
79
84
|
authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
|
80
85
|
# This comparison uses & so that it doesn't short circuit and
|
81
86
|
# 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)
|
87
|
+
ActiveSupport::SecurityUtils.secure_compare(given_name.to_s, name) &
|
88
|
+
ActiveSupport::SecurityUtils.secure_compare(given_password.to_s, password)
|
84
89
|
end
|
85
90
|
end
|
86
91
|
|
@@ -135,15 +140,15 @@ module ActionController
|
|
135
140
|
end
|
136
141
|
end
|
137
142
|
|
138
|
-
#
|
143
|
+
# = HTTP \Digest authentication
|
139
144
|
#
|
140
145
|
# === Simple \Digest example
|
141
146
|
#
|
142
|
-
# require "
|
147
|
+
# require "openssl"
|
143
148
|
# class PostsController < ApplicationController
|
144
149
|
# REALM = "SuperSecret"
|
145
150
|
# USERS = {"dhh" => "secret", #plain text password
|
146
|
-
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
151
|
+
# "dap" => OpenSSL::Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
147
152
|
#
|
148
153
|
# before_action :authenticate, except: [:index]
|
149
154
|
#
|
@@ -181,22 +186,28 @@ module ActionController
|
|
181
186
|
extend self
|
182
187
|
|
183
188
|
module ControllerMethods
|
189
|
+
# Authenticate using an HTTP \Digest, or otherwise render an HTTP header
|
190
|
+
# requesting the client to send a \Digest.
|
191
|
+
#
|
192
|
+
# See ActionController::HttpAuthentication::Digest for example usage.
|
184
193
|
def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure)
|
185
194
|
authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message)
|
186
195
|
end
|
187
196
|
|
188
|
-
# Authenticate
|
197
|
+
# Authenticate using an HTTP \Digest. Returns true if authentication is
|
198
|
+
# successful, false otherwise.
|
189
199
|
def authenticate_with_http_digest(realm = "Application", &password_procedure)
|
190
200
|
HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
|
191
201
|
end
|
192
202
|
|
193
|
-
# Render
|
203
|
+
# Render an HTTP header requesting the client to send a \Digest for
|
204
|
+
# authentication.
|
194
205
|
def request_http_digest_authentication(realm = "Application", message = nil)
|
195
206
|
HttpAuthentication::Digest.authentication_request(self, realm, message)
|
196
207
|
end
|
197
208
|
end
|
198
209
|
|
199
|
-
# Returns false on a valid response, true otherwise
|
210
|
+
# Returns false on a valid response, true otherwise.
|
200
211
|
def authenticate(request, realm, &password_procedure)
|
201
212
|
request.authorization && validate_digest_response(request, realm, &password_procedure)
|
202
213
|
end
|
@@ -231,12 +242,12 @@ module ActionController
|
|
231
242
|
# of a plain-text password.
|
232
243
|
def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
|
233
244
|
ha1 = password_is_ha1 ? password : ha1(credentials, password)
|
234
|
-
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
|
235
|
-
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
|
245
|
+
ha2 = OpenSSL::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
|
246
|
+
OpenSSL::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
|
236
247
|
end
|
237
248
|
|
238
249
|
def ha1(credentials, password)
|
239
|
-
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
|
250
|
+
OpenSSL::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
|
240
251
|
end
|
241
252
|
|
242
253
|
def encode_credentials(http_method, credentials, password, password_is_ha1)
|
@@ -301,16 +312,16 @@ module ActionController
|
|
301
312
|
#
|
302
313
|
# An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
|
303
314
|
# protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
|
304
|
-
# POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
|
315
|
+
# POST, PUT, or PATCH requests, and a time-stamp for GET requests. For more details on the issues involved see Section 4
|
305
316
|
# of this document.
|
306
317
|
#
|
307
318
|
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
|
308
|
-
# key from the Rails session secret generated upon creation of project. Ensures
|
319
|
+
# key from the \Rails session secret generated upon creation of project. Ensures
|
309
320
|
# the time cannot be modified by client.
|
310
321
|
def nonce(secret_key, time = Time.now)
|
311
322
|
t = time.to_i
|
312
323
|
hashed = [t, secret_key]
|
313
|
-
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
|
324
|
+
digest = OpenSSL::Digest::MD5.hexdigest(hashed.join(":"))
|
314
325
|
::Base64.strict_encode64("#{t}:#{digest}")
|
315
326
|
end
|
316
327
|
|
@@ -327,13 +338,13 @@ module ActionController
|
|
327
338
|
|
328
339
|
# Opaque based on digest of secret key
|
329
340
|
def opaque(secret_key)
|
330
|
-
::Digest::MD5.hexdigest(secret_key)
|
341
|
+
OpenSSL::Digest::MD5.hexdigest(secret_key)
|
331
342
|
end
|
332
343
|
end
|
333
344
|
|
334
|
-
#
|
345
|
+
# = HTTP \Token authentication
|
335
346
|
#
|
336
|
-
# Simple Token example
|
347
|
+
# === Simple \Token example
|
337
348
|
#
|
338
349
|
# class PostsController < ApplicationController
|
339
350
|
# TOKEN = "secret"
|
@@ -359,8 +370,8 @@ module ActionController
|
|
359
370
|
# end
|
360
371
|
#
|
361
372
|
#
|
362
|
-
# Here is a more advanced Token example where only Atom feeds and the XML API
|
363
|
-
#
|
373
|
+
# Here is a more advanced Token example where only Atom feeds and the XML API are protected by HTTP token authentication.
|
374
|
+
# The regular HTML interface is protected by a session approach:
|
364
375
|
#
|
365
376
|
# class ApplicationController < ActionController::Base
|
366
377
|
# before_action :set_account, :authenticate
|
@@ -412,14 +423,27 @@ module ActionController
|
|
412
423
|
extend self
|
413
424
|
|
414
425
|
module ControllerMethods
|
426
|
+
# Authenticate using an HTTP Bearer token, or otherwise render an HTTP
|
427
|
+
# header requesting the client to send a Bearer token. For the authentication
|
428
|
+
# to be considered successful, +login_procedure+ should return a non-nil
|
429
|
+
# value. Typically, the authenticated user is returned.
|
430
|
+
#
|
431
|
+
# See ActionController::HttpAuthentication::Token for example usage.
|
415
432
|
def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
|
416
433
|
authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
|
417
434
|
end
|
418
435
|
|
436
|
+
# Authenticate using an HTTP Bearer token.
|
437
|
+
# Returns the return value of +login_procedure+ if a
|
438
|
+
# token is found. Returns +nil+ if no token is found.
|
439
|
+
#
|
440
|
+
# See ActionController::HttpAuthentication::Token for example usage.
|
419
441
|
def authenticate_with_http_token(&login_procedure)
|
420
442
|
Token.authenticate(self, &login_procedure)
|
421
443
|
end
|
422
444
|
|
445
|
+
# Render an HTTP header requesting the client to send a Bearer token for
|
446
|
+
# authentication.
|
423
447
|
def request_http_token_authentication(realm = "Application", message = nil)
|
424
448
|
Token.authentication_request(self, realm, message)
|
425
449
|
end
|
@@ -428,17 +452,17 @@ module ActionController
|
|
428
452
|
# If token Authorization header is present, call the login
|
429
453
|
# procedure with the present token and options.
|
430
454
|
#
|
431
|
-
#
|
432
|
-
#
|
455
|
+
# Returns the return value of +login_procedure+ if a
|
456
|
+
# token is found. Returns +nil+ if no token is found.
|
457
|
+
#
|
458
|
+
# ==== Parameters
|
433
459
|
#
|
434
|
-
#
|
435
|
-
#
|
460
|
+
# * +controller+ - ActionController::Base instance for the current request.
|
461
|
+
# * +login_procedure+ - Proc to call if a token is present. The Proc
|
462
|
+
# should take two arguments:
|
436
463
|
#
|
437
464
|
# authenticate(controller) { |token, options| ... }
|
438
465
|
#
|
439
|
-
# Returns the return value of <tt>login_procedure</tt> if a
|
440
|
-
# token is found. Returns <tt>nil</tt> if no token is found.
|
441
|
-
|
442
466
|
def authenticate(controller, &login_procedure)
|
443
467
|
token, options = token_and_options(controller.request)
|
444
468
|
unless token.blank?
|
@@ -449,14 +473,18 @@ module ActionController
|
|
449
473
|
# Parses the token and options out of the token Authorization header.
|
450
474
|
# The value for the Authorization header is expected to have the prefix
|
451
475
|
# <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
|
476
|
+
#
|
452
477
|
# Authorization: Token token="abc", nonce="def"
|
453
|
-
# Then the returned token is <tt>"abc"</tt>, and the options are
|
454
|
-
# <tt>{nonce: "def"}</tt>
|
455
478
|
#
|
456
|
-
#
|
479
|
+
# Then the returned token is <tt>"abc"</tt>, and the options are
|
480
|
+
# <tt>{nonce: "def"}</tt>.
|
457
481
|
#
|
458
482
|
# Returns an +Array+ of <tt>[String, Hash]</tt> if a token is present.
|
459
483
|
# Returns +nil+ if no token is found.
|
484
|
+
#
|
485
|
+
# ==== Parameters
|
486
|
+
#
|
487
|
+
# * +request+ - ActionDispatch::Request instance with the current headers.
|
460
488
|
def token_and_options(request)
|
461
489
|
authorization_request = request.authorization.to_s
|
462
490
|
if authorization_request[TOKEN_REGEX]
|
@@ -469,7 +497,7 @@ module ActionController
|
|
469
497
|
rewrite_param_values params_array_from raw_params auth
|
470
498
|
end
|
471
499
|
|
472
|
-
# Takes raw_params and turns it into an array of parameters
|
500
|
+
# Takes +raw_params+ and turns it into an array of parameters.
|
473
501
|
def params_array_from(raw_params)
|
474
502
|
raw_params.map { |param| param.split %r/=(.+)?/ }
|
475
503
|
end
|
@@ -479,11 +507,15 @@ module ActionController
|
|
479
507
|
array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
|
480
508
|
end
|
481
509
|
|
510
|
+
WHITESPACED_AUTHN_PAIR_DELIMITERS = /\s*#{AUTHN_PAIR_DELIMITERS}\s*/
|
511
|
+
private_constant :WHITESPACED_AUTHN_PAIR_DELIMITERS
|
512
|
+
|
482
513
|
# This method takes an authorization body and splits up the key-value
|
483
514
|
# pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
|
484
515
|
# delimiters defined in +AUTHN_PAIR_DELIMITERS+.
|
485
516
|
def raw_params(auth)
|
486
|
-
_raw_params = auth.sub(TOKEN_REGEX, "").split(
|
517
|
+
_raw_params = auth.sub(TOKEN_REGEX, "").split(WHITESPACED_AUTHN_PAIR_DELIMITERS)
|
518
|
+
_raw_params.reject!(&:empty?)
|
487
519
|
|
488
520
|
if !_raw_params.first&.start_with?(TOKEN_KEY)
|
489
521
|
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
@@ -494,10 +526,12 @@ module ActionController
|
|
494
526
|
|
495
527
|
# Encodes the given token and options into an Authorization header value.
|
496
528
|
#
|
497
|
-
# token - String token.
|
498
|
-
# options - optional Hash of the options.
|
499
|
-
#
|
500
529
|
# Returns String.
|
530
|
+
#
|
531
|
+
# ==== Parameters
|
532
|
+
#
|
533
|
+
# * +token+ - String token.
|
534
|
+
# * +options+ - Optional Hash of the options.
|
501
535
|
def encode_credentials(token, options = {})
|
502
536
|
values = ["#{TOKEN_KEY}#{token.to_s.inspect}"] + options.map do |key, value|
|
503
537
|
"#{key}=#{value.to_s.inspect}"
|
@@ -507,10 +541,12 @@ module ActionController
|
|
507
541
|
|
508
542
|
# Sets a WWW-Authenticate header to let the client know a token is desired.
|
509
543
|
#
|
510
|
-
# controller - ActionController::Base instance for the outgoing response.
|
511
|
-
# realm - String realm to use in the header.
|
512
|
-
#
|
513
544
|
# Returns nothing.
|
545
|
+
#
|
546
|
+
# ==== Parameters
|
547
|
+
#
|
548
|
+
# * +controller+ - ActionController::Base instance for the outgoing response.
|
549
|
+
# * +realm+ - String realm to use in the header.
|
514
550
|
def authentication_request(controller, realm, message = nil)
|
515
551
|
message ||= "HTTP Token: Access denied.\n"
|
516
552
|
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"', "")}")
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionController
|
4
|
+
# = Action Controller Implicit Render
|
5
|
+
#
|
4
6
|
# Handles implicit rendering for a controller action that does not
|
5
7
|
# explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
|
6
8
|
#
|
@@ -17,12 +19,12 @@ module ActionController
|
|
17
19
|
# Second, if we DON'T find a template but the controller action does have
|
18
20
|
# templates for other formats, variants, etc., then we trust that you meant
|
19
21
|
# to provide a template for this response, too, and we raise
|
20
|
-
#
|
22
|
+
# ActionController::UnknownFormat with an explanation.
|
21
23
|
#
|
22
24
|
# Third, if we DON'T find a template AND the request is a page load in a web
|
23
25
|
# browser (technically, a non-XHR GET request for an HTML response) where
|
24
26
|
# you reasonably expect to have rendered a template, then we raise
|
25
|
-
#
|
27
|
+
# ActionController::MissingExactTemplate with an explanation.
|
26
28
|
#
|
27
29
|
# Finally, if we DON'T find a template AND the request isn't a browser page
|
28
30
|
# load, then we implicitly respond with <tt>204 No Content</tt>.
|
@@ -42,7 +44,7 @@ module ActionController
|
|
42
44
|
raise ActionController::UnknownFormat, message
|
43
45
|
elsif interactive_browser_request?
|
44
46
|
message = "#{self.class.name}\##{action_name} is missing a template for request formats: #{request.formats.map(&:to_s).join(',')}"
|
45
|
-
raise ActionController::MissingExactTemplate,
|
47
|
+
raise ActionController::MissingExactTemplate.new(message, self.class, action_name)
|
46
48
|
else
|
47
49
|
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
|
48
50
|
super
|