actionpack 4.2.8 → 5.2.4.2

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.

Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionController
2
4
  module Head
3
5
  # Returns a response that has no content (merely headers). The options
@@ -17,13 +19,17 @@ module ActionController
17
19
  #
18
20
  # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list of valid +status+ symbols.
19
21
  def head(status, options = {})
20
- options, status = status, nil if status.is_a?(Hash)
21
- status ||= options.delete(:status) || :ok
22
+ if status.is_a?(Hash)
23
+ raise ArgumentError, "#{status.inspect} is not a valid value for `status`."
24
+ end
25
+
26
+ status ||= :ok
27
+
22
28
  location = options.delete(:location)
23
29
  content_type = options.delete(:content_type)
24
30
 
25
31
  options.each do |key, value|
26
- headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
32
+ headers[key.to_s.dasherize.split("-").each { |v| v[0] = v[0].chr.upcase }.join("-")] = value.to_s
27
33
  end
28
34
 
29
35
  self.status = status
@@ -31,28 +37,24 @@ module ActionController
31
37
 
32
38
  self.response_body = ""
33
39
 
34
- if include_content?(self.response_code)
40
+ if include_content?(response_code)
35
41
  self.content_type = content_type || (Mime[formats.first] if formats)
36
- self.response.charset = false if self.response
37
- else
38
- headers.delete('Content-Type')
39
- headers.delete('Content-Length')
42
+ response.charset = false
40
43
  end
41
-
44
+
42
45
  true
43
46
  end
44
47
 
45
48
  private
46
- # :nodoc:
47
- def include_content?(status)
48
- case status
49
- when 100..199
50
- false
51
- when 204, 205, 304
52
- false
53
- else
54
- true
49
+ def include_content?(status)
50
+ case status
51
+ when 100..199
52
+ false
53
+ when 204, 205, 304
54
+ false
55
+ else
56
+ true
57
+ end
55
58
  end
56
- end
57
59
  end
58
60
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionController
2
4
  # The \Rails framework provides a large number of helpers for working with assets, dates, forms,
3
5
  # numbers and model objects, to name a few. These helpers are available to all templates
@@ -5,10 +7,10 @@ module ActionController
5
7
  #
6
8
  # In addition to using the standard template helpers provided, creating custom helpers to
7
9
  # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
8
- # will include all helpers. These helpers are only accessible on the controller through <tt>.helpers</tt>
10
+ # will include all helpers. These helpers are only accessible on the controller through <tt>#helpers</tt>
9
11
  #
10
- # In previous versions of \Rails the controller will include a helper whose
11
- # name matches that of the controller, e.g., <tt>MyController</tt> will automatically
12
+ # In previous versions of \Rails the controller will include a helper which
13
+ # matches the name of the controller, e.g., <tt>MyController</tt> will automatically
12
14
  # include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
13
15
  #
14
16
  # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
@@ -44,7 +46,7 @@ module ActionController
44
46
  # the output might look like this:
45
47
  #
46
48
  # 23 Aug 11:30 | Carolina Railhawks Soccer Match
47
- # N/A | Carolina Railhaws Training Workshop
49
+ # N/A | Carolina Railhawks Training Workshop
48
50
  #
49
51
  module Helpers
50
52
  extend ActiveSupport::Concern
@@ -53,9 +55,8 @@ module ActionController
53
55
  include AbstractController::Helpers
54
56
 
55
57
  included do
56
- class_attribute :helpers_path, :include_all_helpers
57
- self.helpers_path ||= []
58
- self.include_all_helpers = true
58
+ class_attribute :helpers_path, default: []
59
+ class_attribute :include_all_helpers, default: true
59
60
  end
60
61
 
61
62
  module ClassMethods
@@ -71,9 +72,9 @@ module ActionController
71
72
  attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
72
73
  end
73
74
 
74
- # Provides a proxy to access helpers methods from outside the view.
75
+ # Provides a proxy to access helper methods from outside the view.
75
76
  def helpers
76
- @helper_proxy ||= begin
77
+ @helper_proxy ||= begin
77
78
  proxy = ActionView::Base.new
78
79
  proxy.config = config.inheritable_copy
79
80
  proxy.extend(_helpers)
@@ -93,10 +94,14 @@ module ActionController
93
94
  super(args)
94
95
  end
95
96
 
97
+ # Returns a list of helper names in a given path.
98
+ #
99
+ # ActionController::Base.all_helpers_from_path 'app/helpers'
100
+ # # => ["application", "chart", "rubygems"]
96
101
  def all_helpers_from_path(path)
97
102
  helpers = Array(path).flat_map do |_path|
98
103
  extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
99
- names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
104
+ names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
100
105
  names.sort!
101
106
  end
102
107
  helpers.uniq!
@@ -104,10 +109,15 @@ module ActionController
104
109
  end
105
110
 
106
111
  private
107
- # Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
108
- def all_application_helpers
109
- all_helpers_from_path(helpers_path)
110
- end
112
+ # Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
113
+ def all_application_helpers
114
+ all_helpers_from_path(helpers_path)
115
+ end
116
+ end
117
+
118
+ # Provides a proxy to access helper methods from outside the view.
119
+ def helpers
120
+ @_helper_proxy ||= view_context
111
121
  end
112
122
  end
113
123
  end
@@ -1,5 +1,7 @@
1
- require 'base64'
2
- require 'active_support/security_utils'
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "active_support/security_utils"
3
5
 
4
6
  module ActionController
5
7
  # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
@@ -28,14 +30,14 @@ module ActionController
28
30
  # class ApplicationController < ActionController::Base
29
31
  # before_action :set_account, :authenticate
30
32
  #
31
- # protected
33
+ # private
32
34
  # def set_account
33
35
  # @account = Account.find_by(url_name: request.subdomains.first)
34
36
  # end
35
37
  #
36
38
  # def authenticate
37
39
  # case request.format
38
- # when Mime::XML, Mime::ATOM
40
+ # when Mime[:xml], Mime[:atom]
39
41
  # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
40
42
  # @current_user = user
41
43
  # else
@@ -70,25 +72,25 @@ module ActionController
70
72
  before_action(options.except(:name, :password, :realm)) do
71
73
  authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
72
74
  # This comparison uses & so that it doesn't short circuit and
73
- # uses `variable_size_secure_compare` so that length information
75
+ # uses `secure_compare` so that length information
74
76
  # isn't leaked.
75
- ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
76
- ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
77
+ ActiveSupport::SecurityUtils.secure_compare(name, options[:name]) &
78
+ ActiveSupport::SecurityUtils.secure_compare(password, options[:password])
77
79
  end
78
80
  end
79
81
  end
80
82
  end
81
83
 
82
- def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
83
- authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
84
+ def authenticate_or_request_with_http_basic(realm = "Application", message = nil, &login_procedure)
85
+ authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
84
86
  end
85
87
 
86
88
  def authenticate_with_http_basic(&login_procedure)
87
89
  HttpAuthentication::Basic.authenticate(request, &login_procedure)
88
90
  end
89
91
 
90
- def request_http_basic_authentication(realm = "Application")
91
- HttpAuthentication::Basic.authentication_request(self, realm)
92
+ def request_http_basic_authentication(realm = "Application", message = nil)
93
+ HttpAuthentication::Basic.authentication_request(self, realm, message)
92
94
  end
93
95
  end
94
96
 
@@ -99,33 +101,34 @@ module ActionController
99
101
  end
100
102
 
101
103
  def has_basic_credentials?(request)
102
- request.authorization.present? && (auth_scheme(request) == 'Basic')
104
+ request.authorization.present? && (auth_scheme(request).downcase == "basic")
103
105
  end
104
106
 
105
107
  def user_name_and_password(request)
106
- decode_credentials(request).split(':', 2)
108
+ decode_credentials(request).split(":", 2)
107
109
  end
108
110
 
109
111
  def decode_credentials(request)
110
- ::Base64.decode64(auth_param(request) || '')
112
+ ::Base64.decode64(auth_param(request) || "")
111
113
  end
112
114
 
113
115
  def auth_scheme(request)
114
- request.authorization.split(' ', 2).first
116
+ request.authorization.to_s.split(" ", 2).first
115
117
  end
116
118
 
117
119
  def auth_param(request)
118
- request.authorization.split(' ', 2).second
120
+ request.authorization.to_s.split(" ", 2).second
119
121
  end
120
122
 
121
123
  def encode_credentials(user_name, password)
122
124
  "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
123
125
  end
124
126
 
125
- def authentication_request(controller, realm)
126
- controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
127
+ def authentication_request(controller, realm, message)
128
+ message ||= "HTTP Basic: Access denied.\n"
129
+ controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'.freeze, "".freeze)}")
127
130
  controller.status = 401
128
- controller.response_body = "HTTP Basic: Access denied.\n"
131
+ controller.response_body = message
129
132
  end
130
133
  end
131
134
 
@@ -175,8 +178,8 @@ module ActionController
175
178
  extend self
176
179
 
177
180
  module ControllerMethods
178
- def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
179
- authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
181
+ def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure)
182
+ authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message)
180
183
  end
181
184
 
182
185
  # Authenticate with HTTP Digest, returns true or false
@@ -207,7 +210,7 @@ module ActionController
207
210
  password = password_procedure.call(credentials[:username])
208
211
  return false unless password
209
212
 
210
- method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
213
+ method = request.get_header("rack.methodoverride.original_method") || request.get_header("REQUEST_METHOD")
211
214
  uri = credentials[:uri]
212
215
 
213
216
  [true, false].any? do |trailing_question_mark|
@@ -223,19 +226,19 @@ module ActionController
223
226
  # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
224
227
  # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
225
228
  # of a plain-text password.
226
- def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
229
+ def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
227
230
  ha1 = password_is_ha1 ? password : ha1(credentials, password)
228
- ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
229
- ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
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(":"))
230
233
  end
231
234
 
232
235
  def ha1(credentials, password)
233
- ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
236
+ ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
234
237
  end
235
238
 
236
239
  def encode_credentials(http_method, credentials, password, password_is_ha1)
237
240
  credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
238
- "Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
241
+ "Digest " + credentials.sort_by { |x| x[0].to_s }.map { |v| "#{v[0]}='#{v[1]}'" }.join(", ")
239
242
  end
240
243
 
241
244
  def decode_credentials_header(request)
@@ -243,9 +246,9 @@ module ActionController
243
246
  end
244
247
 
245
248
  def decode_credentials(header)
246
- ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, '').split(',').map do |pair|
247
- key, value = pair.split('=', 2)
248
- [key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
249
+ ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair|
250
+ key, value = pair.split("=", 2)
251
+ [key.strip, value.to_s.gsub(/^"|"$/, "").delete("'")]
249
252
  end]
250
253
  end
251
254
 
@@ -264,8 +267,8 @@ module ActionController
264
267
  end
265
268
 
266
269
  def secret_token(request)
267
- key_generator = request.env["action_dispatch.key_generator"]
268
- http_auth_salt = request.env["action_dispatch.http_auth_salt"]
270
+ key_generator = request.key_generator
271
+ http_auth_salt = request.http_auth_salt
269
272
  key_generator.generate_key(http_auth_salt)
270
273
  end
271
274
 
@@ -309,21 +312,20 @@ module ActionController
309
312
  end
310
313
 
311
314
  # Might want a shorter timeout depending on whether the request
312
- # is a PATCH, PUT, or POST, and if client is browser or web service.
315
+ # is a PATCH, PUT, or POST, and if the client is a browser or web service.
313
316
  # Can be much shorter if the Stale directive is implemented. This would
314
- # allow a user to use new nonce without prompting user again for their
317
+ # allow a user to use new nonce without prompting the user again for their
315
318
  # username and password.
316
- def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
319
+ def validate_nonce(secret_key, request, value, seconds_to_timeout = 5 * 60)
317
320
  return false if value.nil?
318
321
  t = ::Base64.decode64(value).split(":").first.to_i
319
322
  nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
320
323
  end
321
324
 
322
- # Opaque based on random generation - but changing each request?
325
+ # Opaque based on digest of secret key
323
326
  def opaque(secret_key)
324
327
  ::Digest::MD5.hexdigest(secret_key)
325
328
  end
326
-
327
329
  end
328
330
 
329
331
  # Makes it dead easy to do HTTP Token authentication.
@@ -346,7 +348,9 @@ module ActionController
346
348
  # private
347
349
  # def authenticate
348
350
  # authenticate_or_request_with_http_token do |token, options|
349
- # token == TOKEN
351
+ # # Compare the tokens in a time-constant manner, to mitigate
352
+ # # timing attacks.
353
+ # ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
350
354
  # end
351
355
  # end
352
356
  # end
@@ -358,14 +362,14 @@ module ActionController
358
362
  # class ApplicationController < ActionController::Base
359
363
  # before_action :set_account, :authenticate
360
364
  #
361
- # protected
365
+ # private
362
366
  # def set_account
363
367
  # @account = Account.find_by(url_name: request.subdomains.first)
364
368
  # end
365
369
  #
366
370
  # def authenticate
367
371
  # case request.format
368
- # when Mime::XML, Mime::ATOM
372
+ # when Mime[:xml], Mime[:atom]
369
373
  # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
370
374
  # @current_user = user
371
375
  # else
@@ -400,22 +404,22 @@ module ActionController
400
404
  #
401
405
  # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
402
406
  module Token
403
- TOKEN_KEY = 'token='
404
- TOKEN_REGEX = /^Token /
407
+ TOKEN_KEY = "token="
408
+ TOKEN_REGEX = /^(Token|Bearer)\s+/
405
409
  AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
406
410
  extend self
407
411
 
408
412
  module ControllerMethods
409
- def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
410
- authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
413
+ def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
414
+ authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
411
415
  end
412
416
 
413
417
  def authenticate_with_http_token(&login_procedure)
414
418
  Token.authenticate(self, &login_procedure)
415
419
  end
416
420
 
417
- def request_http_token_authentication(realm = "Application")
418
- Token.authentication_request(self, realm)
421
+ def request_http_token_authentication(realm = "Application", message = nil)
422
+ Token.authentication_request(self, realm, message)
419
423
  end
420
424
  end
421
425
 
@@ -440,15 +444,17 @@ module ActionController
440
444
  end
441
445
  end
442
446
 
443
- # Parses the token and options out of the token authorization header. If
444
- # the header looks like this:
447
+ # Parses the token and options out of the token Authorization header.
448
+ # The value for the Authorization header is expected to have the prefix
449
+ # <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
445
450
  # Authorization: Token token="abc", nonce="def"
446
- # Then the returned token is "abc", and the options is {nonce: "def"}
451
+ # Then the returned token is <tt>"abc"</tt>, and the options are
452
+ # <tt>{nonce: "def"}</tt>
447
453
  #
448
454
  # request - ActionDispatch::Request instance with the current headers.
449
455
  #
450
- # Returns an Array of [String, Hash] if a token is present.
451
- # Returns nil if no token is found.
456
+ # Returns an +Array+ of <tt>[String, Hash]</tt> if a token is present.
457
+ # Returns +nil+ if no token is found.
452
458
  def token_and_options(request)
453
459
  authorization_request = request.authorization.to_s
454
460
  if authorization_request[TOKEN_REGEX]
@@ -468,14 +474,14 @@ module ActionController
468
474
 
469
475
  # This removes the <tt>"</tt> characters wrapping the value.
470
476
  def rewrite_param_values(array_params)
471
- array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' }
477
+ array_params.each { |param| (param[1] || "".dup).gsub! %r/^"|"$/, "" }
472
478
  end
473
479
 
474
480
  # This method takes an authorization body and splits up the key-value
475
481
  # pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
476
482
  # delimiters defined in +AUTHN_PAIR_DELIMITERS+.
477
483
  def raw_params(auth)
478
- _raw_params = auth.sub(TOKEN_REGEX, '').split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
484
+ _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
479
485
 
480
486
  if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
481
487
  _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
@@ -497,15 +503,16 @@ module ActionController
497
503
  "Token #{values * ", "}"
498
504
  end
499
505
 
500
- # Sets a WWW-Authenticate to let the client know a token is desired.
506
+ # Sets a WWW-Authenticate header to let the client know a token is desired.
501
507
  #
502
508
  # controller - ActionController::Base instance for the outgoing response.
503
509
  # realm - String realm to use in the header.
504
510
  #
505
511
  # Returns nothing.
506
- def authentication_request(controller, realm)
507
- controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
508
- controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
512
+ def authentication_request(controller, realm, message = nil)
513
+ message ||= "HTTP Token: Access denied.\n"
514
+ controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
515
+ controller.__send__ :render, plain: message, status: :unauthorized
509
516
  end
510
517
  end
511
518
  end