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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -435
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +33 -37
  7. data/lib/abstract_controller/caching/fragments.rb +4 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +50 -11
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/deprecator.rb +7 -0
  12. data/lib/abstract_controller/error.rb +1 -1
  13. data/lib/abstract_controller/helpers.rb +78 -30
  14. data/lib/abstract_controller/logger.rb +1 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +12 -14
  17. data/lib/abstract_controller/translation.rb +26 -7
  18. data/lib/abstract_controller/url_for.rb +6 -6
  19. data/lib/abstract_controller.rb +6 -0
  20. data/lib/action_controller/api.rb +12 -10
  21. data/lib/action_controller/base.rb +8 -21
  22. data/lib/action_controller/caching.rb +2 -0
  23. data/lib/action_controller/deprecator.rb +7 -0
  24. data/lib/action_controller/form_builder.rb +4 -2
  25. data/lib/action_controller/log_subscriber.rb +20 -7
  26. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  27. data/lib/action_controller/metal/conditional_get.rb +137 -102
  28. data/lib/action_controller/metal/content_security_policy.rb +37 -3
  29. data/lib/action_controller/metal/cookies.rb +1 -1
  30. data/lib/action_controller/metal/data_streaming.rb +25 -31
  31. data/lib/action_controller/metal/default_headers.rb +2 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  34. data/lib/action_controller/metal/exceptions.rb +27 -30
  35. data/lib/action_controller/metal/flash.rb +6 -2
  36. data/lib/action_controller/metal/head.rb +9 -7
  37. data/lib/action_controller/metal/helpers.rb +5 -16
  38. data/lib/action_controller/metal/http_authentication.rb +78 -42
  39. data/lib/action_controller/metal/implicit_render.rb +5 -3
  40. data/lib/action_controller/metal/instrumentation.rb +62 -50
  41. data/lib/action_controller/metal/live.rb +67 -2
  42. data/lib/action_controller/metal/mime_responds.rb +5 -5
  43. data/lib/action_controller/metal/params_wrapper.rb +24 -13
  44. data/lib/action_controller/metal/permissions_policy.rb +20 -29
  45. data/lib/action_controller/metal/redirecting.rb +96 -23
  46. data/lib/action_controller/metal/renderers.rb +14 -15
  47. data/lib/action_controller/metal/rendering.rb +121 -16
  48. data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
  49. data/lib/action_controller/metal/rescue.rb +7 -4
  50. data/lib/action_controller/metal/streaming.rb +74 -36
  51. data/lib/action_controller/metal/strong_parameters.rb +254 -151
  52. data/lib/action_controller/metal/testing.rb +9 -2
  53. data/lib/action_controller/metal/url_for.rb +10 -5
  54. data/lib/action_controller/metal.rb +89 -34
  55. data/lib/action_controller/railtie.rb +66 -9
  56. data/lib/action_controller/renderer.rb +99 -85
  57. data/lib/action_controller/test_case.rb +42 -11
  58. data/lib/action_controller.rb +10 -6
  59. data/lib/action_dispatch/constants.rb +32 -0
  60. data/lib/action_dispatch/deprecator.rb +7 -0
  61. data/lib/action_dispatch/http/cache.rb +21 -16
  62. data/lib/action_dispatch/http/content_security_policy.rb +122 -44
  63. data/lib/action_dispatch/http/filter_parameters.rb +14 -23
  64. data/lib/action_dispatch/http/headers.rb +3 -1
  65. data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
  66. data/lib/action_dispatch/http/mime_type.rb +43 -22
  67. data/lib/action_dispatch/http/mime_types.rb +3 -1
  68. data/lib/action_dispatch/http/parameters.rb +6 -6
  69. data/lib/action_dispatch/http/permissions_policy.rb +57 -19
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +75 -51
  72. data/lib/action_dispatch/http/response.rb +81 -77
  73. data/lib/action_dispatch/http/upload.rb +15 -2
  74. data/lib/action_dispatch/http/url.rb +11 -19
  75. data/lib/action_dispatch/journey/formatter.rb +8 -2
  76. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  79. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  80. data/lib/action_dispatch/journey/path/pattern.rb +36 -27
  81. data/lib/action_dispatch/journey/route.rb +8 -14
  82. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  83. data/lib/action_dispatch/journey/router.rb +10 -9
  84. data/lib/action_dispatch/journey/routes.rb +5 -5
  85. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  86. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  87. data/lib/action_dispatch/log_subscriber.rb +23 -0
  88. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
  89. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  90. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  91. data/lib/action_dispatch/middleware/cookies.rb +97 -107
  92. data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
  93. data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
  94. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  95. data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
  96. data/lib/action_dispatch/middleware/executor.rb +3 -0
  97. data/lib/action_dispatch/middleware/flash.rb +24 -18
  98. data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
  99. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  100. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  101. data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
  102. data/lib/action_dispatch/middleware/request_id.rb +5 -3
  103. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
  105. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  106. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
  107. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
  109. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  110. data/lib/action_dispatch/middleware/stack.rb +34 -11
  111. data/lib/action_dispatch/middleware/static.rb +16 -16
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  113. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  115. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  116. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  119. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
  120. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  122. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
  123. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
  125. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
  126. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
  127. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
  131. data/lib/action_dispatch/railtie.rb +20 -4
  132. data/lib/action_dispatch/request/session.rb +59 -19
  133. data/lib/action_dispatch/request/utils.rb +8 -3
  134. data/lib/action_dispatch/routing/inspector.rb +55 -7
  135. data/lib/action_dispatch/routing/mapper.rb +117 -107
  136. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  137. data/lib/action_dispatch/routing/redirection.rb +20 -8
  138. data/lib/action_dispatch/routing/route_set.rb +67 -27
  139. data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
  140. data/lib/action_dispatch/routing/url_for.rb +29 -26
  141. data/lib/action_dispatch/routing.rb +12 -13
  142. data/lib/action_dispatch/system_test_case.rb +8 -8
  143. data/lib/action_dispatch/system_testing/browser.rb +20 -29
  144. data/lib/action_dispatch/system_testing/driver.rb +34 -18
  145. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
  146. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  147. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  148. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  149. data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
  150. data/lib/action_dispatch/testing/assertions.rb +3 -4
  151. data/lib/action_dispatch/testing/integration.rb +33 -25
  152. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  153. data/lib/action_dispatch/testing/test_process.rb +5 -30
  154. data/lib/action_dispatch/testing/test_request.rb +1 -1
  155. data/lib/action_dispatch/testing/test_response.rb +34 -2
  156. data/lib/action_dispatch.rb +38 -4
  157. data/lib/action_pack/gem_version.rb +4 -4
  158. data/lib/action_pack/version.rb +1 -1
  159. data/lib/action_pack.rb +1 -1
  160. metadata +67 -30
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionController
4
- class ActionControllerError < StandardError #:nodoc:
4
+ class ActionControllerError < StandardError # :nodoc:
5
5
  end
6
6
 
7
- class BadRequest < ActionControllerError #:nodoc:
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 #:nodoc:
14
+ class RenderError < ActionControllerError # :nodoc:
15
15
  end
16
16
 
17
- class RoutingError < ActionControllerError #:nodoc:
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 #:nodoc:
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
- class Correction
37
- def initialize(error)
38
- @error = error
39
- end
36
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
37
+ include DidYouMean::Correctable
40
38
 
41
39
  def corrections
42
- if @error.method_name
43
- maybe_these = @error.routes.named_routes.helper_names.grep(/#{@error.route_name}/)
44
- maybe_these -= [@error.method_name.to_s] # remove exact match
45
-
46
- maybe_these.sort_by { |n|
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 #:nodoc:
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 #:nodoc:
56
+ class NotImplemented < MethodNotAllowed # :nodoc:
68
57
  end
69
58
 
70
- class MissingFile < ActionControllerError #:nodoc:
59
+ class MissingFile < ActionControllerError # :nodoc:
71
60
  end
72
61
 
73
- class SessionOverflowError < ActionControllerError #:nodoc:
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 #:nodoc:
70
+ class UnknownHttpMethod < ActionControllerError # :nodoc:
82
71
  end
83
72
 
84
- class UnknownFormat < ActionControllerError #:nodoc:
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 #:nodoc:
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 #:nodoc:
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 = {}) #:doc:
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
- location = options.delete(:location)
29
- content_type = options.delete(:content_type)
28
+ if options
29
+ location = options.delete(:location)
30
+ content_type = options.delete(:content_type)
30
31
 
31
- options.each do |key, value|
32
- headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
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[formats.first] if formats) || Mime[:html]
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="&nbsp;")
29
- # time.blank? ? blank_message : time.to_s(format)
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 behaviour with capture methods. Consider
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
- # Overwrite modules_for_helpers to accept :all as argument, which loads
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
- # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
8
+ # HTTP Basic, Digest, and Token authentication.
9
9
  module HttpAuthentication
10
- # Makes it dead easy to do HTTP \Basic authentication.
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
- # end
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 is protected by HTTP authentication,
29
- # the regular HTML interface is protected by a session approach:
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
- # Makes it dead easy to do HTTP \Digest authentication.
143
+ # = HTTP \Digest authentication
139
144
  #
140
145
  # === Simple \Digest example
141
146
  #
142
- # require "digest/md5"
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 with HTTP Digest, returns true or false
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 output including the HTTP Digest authentication header
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
- # Makes it dead easy to do HTTP Token authentication.
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 is protected by HTTP token authentication,
363
- # the regular HTML interface is protected by a session approach:
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
- # [controller]
432
- # ActionController::Base instance for the current request.
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
- # [login_procedure]
435
- # Proc to call if a token is present. The Proc should take two arguments:
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
- # request - ActionDispatch::Request instance with the current headers.
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(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
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
- # <tt>ActionController::UnknownFormat</tt> with an explanation.
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
- # <tt>ActionController::MissingExactTemplate</tt> with an explanation.
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, message
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