actionpack 4.2.10 → 5.0.0

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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +553 -401
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/base.rb +28 -38
  6. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
  7. data/lib/abstract_controller/caching.rb +62 -0
  8. data/lib/abstract_controller/callbacks.rb +52 -19
  9. data/lib/abstract_controller/collector.rb +4 -9
  10. data/lib/abstract_controller/error.rb +4 -0
  11. data/lib/abstract_controller/helpers.rb +4 -3
  12. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  13. data/lib/abstract_controller/rendering.rb +28 -18
  14. data/lib/abstract_controller/translation.rb +8 -7
  15. data/lib/abstract_controller.rb +6 -2
  16. data/lib/action_controller/api/api_rendering.rb +14 -0
  17. data/lib/action_controller/api.rb +147 -0
  18. data/lib/action_controller/base.rb +10 -13
  19. data/lib/action_controller/caching.rb +13 -58
  20. data/lib/action_controller/form_builder.rb +48 -0
  21. data/lib/action_controller/log_subscriber.rb +3 -10
  22. data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
  23. data/lib/action_controller/metal/conditional_get.rb +106 -34
  24. data/lib/action_controller/metal/cookies.rb +1 -3
  25. data/lib/action_controller/metal/data_streaming.rb +11 -32
  26. data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
  27. data/lib/action_controller/metal/exceptions.rb +11 -6
  28. data/lib/action_controller/metal/force_ssl.rb +10 -10
  29. data/lib/action_controller/metal/head.rb +14 -8
  30. data/lib/action_controller/metal/helpers.rb +15 -6
  31. data/lib/action_controller/metal/http_authentication.rb +44 -35
  32. data/lib/action_controller/metal/implicit_render.rb +61 -6
  33. data/lib/action_controller/metal/instrumentation.rb +5 -5
  34. data/lib/action_controller/metal/live.rb +66 -88
  35. data/lib/action_controller/metal/mime_responds.rb +27 -42
  36. data/lib/action_controller/metal/params_wrapper.rb +8 -8
  37. data/lib/action_controller/metal/redirecting.rb +32 -9
  38. data/lib/action_controller/metal/renderers.rb +85 -40
  39. data/lib/action_controller/metal/rendering.rb +38 -6
  40. data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
  41. data/lib/action_controller/metal/rescue.rb +3 -12
  42. data/lib/action_controller/metal/streaming.rb +4 -4
  43. data/lib/action_controller/metal/strong_parameters.rb +293 -90
  44. data/lib/action_controller/metal/testing.rb +1 -12
  45. data/lib/action_controller/metal/url_for.rb +12 -5
  46. data/lib/action_controller/metal.rb +88 -63
  47. data/lib/action_controller/renderer.rb +111 -0
  48. data/lib/action_controller/template_assertions.rb +9 -0
  49. data/lib/action_controller/test_case.rb +288 -368
  50. data/lib/action_controller.rb +12 -9
  51. data/lib/action_dispatch/http/cache.rb +73 -34
  52. data/lib/action_dispatch/http/filter_parameters.rb +15 -11
  53. data/lib/action_dispatch/http/filter_redirect.rb +7 -8
  54. data/lib/action_dispatch/http/headers.rb +44 -13
  55. data/lib/action_dispatch/http/mime_negotiation.rb +41 -23
  56. data/lib/action_dispatch/http/mime_type.rb +126 -90
  57. data/lib/action_dispatch/http/mime_types.rb +3 -4
  58. data/lib/action_dispatch/http/parameter_filter.rb +18 -8
  59. data/lib/action_dispatch/http/parameters.rb +54 -41
  60. data/lib/action_dispatch/http/request.rb +149 -82
  61. data/lib/action_dispatch/http/response.rb +206 -102
  62. data/lib/action_dispatch/http/url.rb +117 -8
  63. data/lib/action_dispatch/journey/formatter.rb +39 -28
  64. data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
  65. data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
  66. data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
  67. data/lib/action_dispatch/journey/nodes/node.rb +14 -4
  68. data/lib/action_dispatch/journey/parser_extras.rb +4 -0
  69. data/lib/action_dispatch/journey/path/pattern.rb +38 -42
  70. data/lib/action_dispatch/journey/route.rb +74 -19
  71. data/lib/action_dispatch/journey/router/utils.rb +5 -5
  72. data/lib/action_dispatch/journey/router.rb +5 -9
  73. data/lib/action_dispatch/journey/routes.rb +14 -15
  74. data/lib/action_dispatch/journey/visitors.rb +86 -43
  75. data/lib/action_dispatch/middleware/callbacks.rb +10 -1
  76. data/lib/action_dispatch/middleware/cookies.rb +189 -135
  77. data/lib/action_dispatch/middleware/debug_exceptions.rb +124 -49
  78. data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
  79. data/lib/action_dispatch/middleware/executor.rb +19 -0
  80. data/lib/action_dispatch/middleware/flash.rb +66 -45
  81. data/lib/action_dispatch/middleware/params_parser.rb +32 -46
  82. data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
  83. data/lib/action_dispatch/middleware/reloader.rb +14 -58
  84. data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
  85. data/lib/action_dispatch/middleware/request_id.rb +11 -6
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
  89. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
  90. data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
  91. data/lib/action_dispatch/middleware/ssl.rb +115 -36
  92. data/lib/action_dispatch/middleware/stack.rb +44 -40
  93. data/lib/action_dispatch/middleware/static.rb +51 -35
  94. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  95. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  96. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  97. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  98. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  99. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
  100. data/lib/action_dispatch/railtie.rb +2 -2
  101. data/lib/action_dispatch/request/session.rb +69 -33
  102. data/lib/action_dispatch/request/utils.rb +51 -19
  103. data/lib/action_dispatch/routing/inspector.rb +32 -43
  104. data/lib/action_dispatch/routing/mapper.rb +491 -338
  105. data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
  106. data/lib/action_dispatch/routing/redirection.rb +3 -3
  107. data/lib/action_dispatch/routing/route_set.rb +145 -238
  108. data/lib/action_dispatch/routing/url_for.rb +27 -10
  109. data/lib/action_dispatch/routing.rb +17 -13
  110. data/lib/action_dispatch/testing/assertion_response.rb +45 -0
  111. data/lib/action_dispatch/testing/assertions/response.rb +38 -20
  112. data/lib/action_dispatch/testing/assertions/routing.rb +11 -10
  113. data/lib/action_dispatch/testing/assertions.rb +1 -1
  114. data/lib/action_dispatch/testing/integration.rb +368 -97
  115. data/lib/action_dispatch/testing/test_process.rb +5 -6
  116. data/lib/action_dispatch/testing/test_request.rb +22 -31
  117. data/lib/action_dispatch/testing/test_response.rb +7 -4
  118. data/lib/action_dispatch.rb +3 -1
  119. data/lib/action_pack/gem_version.rb +3 -3
  120. data/lib/action_pack.rb +1 -1
  121. metadata +30 -34
  122. data/lib/action_controller/metal/hide_actions.rb +0 -40
  123. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  124. data/lib/action_controller/middleware.rb +0 -39
  125. data/lib/action_controller/model_naming.rb +0 -12
  126. data/lib/action_dispatch/journey/backwards.rb +0 -5
  127. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  128. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  129. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  130. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
  131. /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -25,14 +25,13 @@ module ActionController #:nodoc:
25
25
  # * <tt>:filename</tt> - suggests a filename for the browser to use.
26
26
  # Defaults to <tt>File.basename(path)</tt>.
27
27
  # * <tt>:type</tt> - specifies an HTTP content type.
28
- # You can specify either a string or a symbol for a registered type register with
29
- # <tt>Mime::Type.register</tt>, for example :json
30
- # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
31
- # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
28
+ # You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
29
+ # If omitted, the type will be inferred from the file extension specified in <tt>:filename</tt>.
30
+ # If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
32
31
  # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
33
32
  # Valid values are 'inline' and 'attachment' (default).
34
33
  # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
35
- # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
34
+ # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser to guess the filename from
36
35
  # the URL, which is necessary for i18n filenames on certain browsers
37
36
  # (setting <tt>:filename</tt> overrides this option).
38
37
  #
@@ -72,41 +71,21 @@ module ActionController #:nodoc:
72
71
 
73
72
  self.status = options[:status] || 200
74
73
  self.content_type = options[:content_type] if options.key?(:content_type)
75
- self.response_body = FileBody.new(path)
76
- end
77
-
78
- # Avoid having to pass an open file handle as the response body.
79
- # Rack::Sendfile will usually intercept the response and uses
80
- # the path directly, so there is no reason to open the file.
81
- class FileBody #:nodoc:
82
- attr_reader :to_path
83
-
84
- def initialize(path)
85
- @to_path = path
86
- end
87
-
88
- # Stream the file's contents if Rack::Sendfile isn't present.
89
- def each
90
- File.open(to_path, 'rb') do |file|
91
- while chunk = file.read(16384)
92
- yield chunk
93
- end
94
- end
95
- end
74
+ response.send_file path
96
75
  end
97
76
 
98
77
  # Sends the given binary data to the browser. This method is similar to
99
78
  # <tt>render plain: data</tt>, but also allows you to specify whether
100
79
  # the browser should display the response as a file attachment (i.e. in a
101
80
  # download dialog) or as inline data. You may also set the content type,
102
- # the apparent file name, and other things.
81
+ # the file name, and other things.
103
82
  #
104
83
  # Options:
105
84
  # * <tt>:filename</tt> - suggests a filename for the browser to use.
106
- # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
107
- # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
108
- # If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
109
- # If no content type is registered for the extension, default type 'application/octet-stream' will be used.
85
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
86
+ # You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
87
+ # If omitted, type will be inferred from the file extension specified in <tt>:filename</tt>.
88
+ # If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
110
89
  # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
111
90
  # Valid values are 'inline' and 'attachment' (default).
112
91
  # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
@@ -126,7 +105,7 @@ module ActionController #:nodoc:
126
105
  # See +send_file+ for more information on HTTP Content-* headers and caching.
127
106
  def send_data(data, options = {}) #:doc:
128
107
  send_file_headers! options
129
- render options.slice(:status, :content_type).merge(:text => data)
108
+ render options.slice(:status, :content_type).merge(body: data)
130
109
  end
131
110
 
132
111
  private
@@ -25,7 +25,7 @@ module ActionController
25
25
  class_attribute :etag_with_template_digest
26
26
  self.etag_with_template_digest = true
27
27
 
28
- ActiveSupport.on_load :action_view, yield: true do |action_view_base|
28
+ ActiveSupport.on_load :action_view, yield: true do
29
29
  etag do |options|
30
30
  determine_template_etag(options) if etag_with_template_digest
31
31
  end
@@ -3,14 +3,19 @@ module ActionController
3
3
  end
4
4
 
5
5
  class BadRequest < ActionControllerError #:nodoc:
6
- attr_reader :original_exception
6
+ def initialize(msg = nil, e = nil)
7
+ if e
8
+ ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
9
+ "Exceptions will automatically capture the original exception.", caller)
10
+ end
7
11
 
8
- def initialize(type = nil, e = nil)
9
- return super() unless type && e
12
+ super(msg)
13
+ set_backtrace $!.backtrace if $!
14
+ end
10
15
 
11
- super("Invalid #{type} parameters: #{e.message}")
12
- @original_exception = e
13
- set_backtrace e.backtrace
16
+ def original_exception
17
+ ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
18
+ cause
14
19
  end
15
20
  end
16
21
 
@@ -2,17 +2,17 @@ require 'active_support/core_ext/hash/except'
2
2
  require 'active_support/core_ext/hash/slice'
3
3
 
4
4
  module ActionController
5
- # This module provides a method which will redirect browser to use HTTPS
5
+ # This module provides a method which will redirect the browser to use HTTPS
6
6
  # protocol. This will ensure that user's sensitive information will be
7
- # transferred safely over the internet. You _should_ always force browser
7
+ # transferred safely over the internet. You _should_ always force the browser
8
8
  # to use HTTPS when you're transferring sensitive information such as
9
9
  # user authentication, account information, or credit card information.
10
10
  #
11
11
  # Note that if you are really concerned about your application security,
12
12
  # you might consider using +config.force_ssl+ in your config file instead.
13
13
  # That will ensure all the data transferred via HTTPS protocol and prevent
14
- # user from getting session hijacked when accessing the site under unsecured
15
- # HTTP protocol.
14
+ # the user from getting their session hijacked when accessing the site over
15
+ # unsecured HTTP protocol.
16
16
  module ForceSSL
17
17
  extend ActiveSupport::Concern
18
18
  include AbstractController::Callbacks
@@ -55,10 +55,10 @@ module ActionController
55
55
  # You can pass any of the following options to affect the before_action callback
56
56
  # * <tt>only</tt> - The callback should be run only for this action
57
57
  # * <tt>except</tt> - The callback should be run for all actions except this action
58
- # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
59
- # will be called only when it returns a true value.
60
- # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
61
- # will be called only when it returns a false value.
58
+ # * <tt>if</tt> - A symbol naming an instance method or a proc; the
59
+ # callback will be called only when it returns a true value.
60
+ # * <tt>unless</tt> - A symbol naming an instance method or a proc; the
61
+ # callback will be called only when it returns a false value.
62
62
  def force_ssl(options = {})
63
63
  action_options = options.slice(*ACTION_OPTIONS)
64
64
  redirect_options = options.except(*ACTION_OPTIONS)
@@ -71,8 +71,8 @@ module ActionController
71
71
  # Redirect the existing request to use the HTTPS protocol.
72
72
  #
73
73
  # ==== Parameters
74
- # * <tt>host_or_options</tt> - Either a host name or any of the url & redirect options
75
- # available to the <tt>force_ssl</tt> method.
74
+ # * <tt>host_or_options</tt> - Either a host name or any of the url &
75
+ # redirect options available to the <tt>force_ssl</tt> method.
76
76
  def force_ssl_redirect(host_or_options = nil)
77
77
  unless request.ssl?
78
78
  options = {
@@ -17,8 +17,18 @@ module ActionController
17
17
  #
18
18
  # See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list of valid +status+ symbols.
19
19
  def head(status, options = {})
20
- options, status = status, nil if status.is_a?(Hash)
21
- status ||= options.delete(:status) || :ok
20
+ if status.is_a?(Hash)
21
+ msg = status[:status] ? 'The :status option' : 'The implicit :ok status'
22
+ options, status = status, status.delete(:status)
23
+
24
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
25
+ #{msg} on `head` has been deprecated and will be removed in Rails 5.1.
26
+ Please pass the status as a separate parameter before the options, instead.
27
+ MSG
28
+ end
29
+
30
+ status ||= :ok
31
+
22
32
  location = options.delete(:location)
23
33
  content_type = options.delete(:content_type)
24
34
 
@@ -33,17 +43,13 @@ module ActionController
33
43
 
34
44
  if include_content?(self.response_code)
35
45
  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')
46
+ self.response.charset = false
40
47
  end
41
-
48
+
42
49
  true
43
50
  end
44
51
 
45
52
  private
46
- # :nodoc:
47
53
  def include_content?(status)
48
54
  case status
49
55
  when 100..199
@@ -5,10 +5,10 @@ module ActionController
5
5
  #
6
6
  # In addition to using the standard template helpers provided, creating custom helpers to
7
7
  # 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>
8
+ # will include all helpers. These helpers are only accessible on the controller through <tt>#helpers</tt>
9
9
  #
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
10
+ # In previous versions of \Rails the controller will include a helper which
11
+ # matches the name of the controller, e.g., <tt>MyController</tt> will automatically
12
12
  # include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
13
13
  #
14
14
  # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
@@ -44,7 +44,7 @@ module ActionController
44
44
  # the output might look like this:
45
45
  #
46
46
  # 23 Aug 11:30 | Carolina Railhawks Soccer Match
47
- # N/A | Carolina Railhaws Training Workshop
47
+ # N/A | Carolina Railhawks Training Workshop
48
48
  #
49
49
  module Helpers
50
50
  extend ActiveSupport::Concern
@@ -73,7 +73,7 @@ module ActionController
73
73
 
74
74
  # Provides a proxy to access helpers methods from outside the view.
75
75
  def helpers
76
- @helper_proxy ||= begin
76
+ @helper_proxy ||= begin
77
77
  proxy = ActionView::Base.new
78
78
  proxy.config = config.inheritable_copy
79
79
  proxy.extend(_helpers)
@@ -93,10 +93,14 @@ module ActionController
93
93
  super(args)
94
94
  end
95
95
 
96
+ # Returns a list of helper names in a given path.
97
+ #
98
+ # ActionController::Base.all_helpers_from_path 'app/helpers'
99
+ # # => ["application", "chart", "rubygems"]
96
100
  def all_helpers_from_path(path)
97
101
  helpers = Array(path).flat_map do |_path|
98
102
  extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
99
- names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
103
+ names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
100
104
  names.sort!
101
105
  end
102
106
  helpers.uniq!
@@ -109,5 +113,10 @@ module ActionController
109
113
  all_helpers_from_path(helpers_path)
110
114
  end
111
115
  end
116
+
117
+ # Provides a proxy to access helpers methods from outside the view.
118
+ def helpers
119
+ @_helper_proxy ||= view_context
120
+ end
112
121
  end
113
122
  end
@@ -35,7 +35,7 @@ module ActionController
35
35
  #
36
36
  # def authenticate
37
37
  # case request.format
38
- # when Mime::XML, Mime::ATOM
38
+ # when Mime[:xml], Mime[:atom]
39
39
  # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
40
40
  # @current_user = user
41
41
  # else
@@ -79,16 +79,16 @@ module ActionController
79
79
  end
80
80
  end
81
81
 
82
- def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
83
- authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
82
+ def authenticate_or_request_with_http_basic(realm = "Application", message = nil, &login_procedure)
83
+ authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
84
84
  end
85
85
 
86
86
  def authenticate_with_http_basic(&login_procedure)
87
87
  HttpAuthentication::Basic.authenticate(request, &login_procedure)
88
88
  end
89
89
 
90
- def request_http_basic_authentication(realm = "Application")
91
- HttpAuthentication::Basic.authentication_request(self, realm)
90
+ def request_http_basic_authentication(realm = "Application", message = nil)
91
+ HttpAuthentication::Basic.authentication_request(self, realm, message)
92
92
  end
93
93
  end
94
94
 
@@ -99,7 +99,7 @@ module ActionController
99
99
  end
100
100
 
101
101
  def has_basic_credentials?(request)
102
- request.authorization.present? && (auth_scheme(request) == 'Basic')
102
+ request.authorization.present? && (auth_scheme(request).downcase == 'basic')
103
103
  end
104
104
 
105
105
  def user_name_and_password(request)
@@ -111,21 +111,22 @@ module ActionController
111
111
  end
112
112
 
113
113
  def auth_scheme(request)
114
- request.authorization.split(' ', 2).first
114
+ request.authorization.to_s.split(' ', 2).first
115
115
  end
116
116
 
117
117
  def auth_param(request)
118
- request.authorization.split(' ', 2).second
118
+ request.authorization.to_s.split(' ', 2).second
119
119
  end
120
120
 
121
121
  def encode_credentials(user_name, password)
122
122
  "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
123
123
  end
124
124
 
125
- def authentication_request(controller, realm)
126
- controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
125
+ def authentication_request(controller, realm, message)
126
+ message ||= "HTTP Basic: Access denied.\n"
127
+ controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'.freeze, "".freeze)}")
127
128
  controller.status = 401
128
- controller.response_body = "HTTP Basic: Access denied.\n"
129
+ controller.response_body = message
129
130
  end
130
131
  end
131
132
 
@@ -175,8 +176,8 @@ module ActionController
175
176
  extend self
176
177
 
177
178
  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)
179
+ def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure)
180
+ authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message)
180
181
  end
181
182
 
182
183
  # Authenticate with HTTP Digest, returns true or false
@@ -207,7 +208,7 @@ module ActionController
207
208
  password = password_procedure.call(credentials[:username])
208
209
  return false unless password
209
210
 
210
- method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
211
+ method = request.get_header('rack.methodoverride.original_method') || request.get_header('REQUEST_METHOD')
211
212
  uri = credentials[:uri]
212
213
 
213
214
  [true, false].any? do |trailing_question_mark|
@@ -264,8 +265,8 @@ module ActionController
264
265
  end
265
266
 
266
267
  def secret_token(request)
267
- key_generator = request.env["action_dispatch.key_generator"]
268
- http_auth_salt = request.env["action_dispatch.http_auth_salt"]
268
+ key_generator = request.key_generator
269
+ http_auth_salt = request.http_auth_salt
269
270
  key_generator.generate_key(http_auth_salt)
270
271
  end
271
272
 
@@ -309,9 +310,9 @@ module ActionController
309
310
  end
310
311
 
311
312
  # 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.
313
+ # is a PATCH, PUT, or POST, and if the client is a browser or web service.
313
314
  # 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
315
+ # allow a user to use new nonce without prompting the user again for their
315
316
  # username and password.
316
317
  def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
317
318
  return false if value.nil?
@@ -319,7 +320,7 @@ module ActionController
319
320
  nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
320
321
  end
321
322
 
322
- # Opaque based on random generation - but changing each request?
323
+ # Opaque based on digest of secret key
323
324
  def opaque(secret_key)
324
325
  ::Digest::MD5.hexdigest(secret_key)
325
326
  end
@@ -346,7 +347,12 @@ module ActionController
346
347
  # private
347
348
  # def authenticate
348
349
  # authenticate_or_request_with_http_token do |token, options|
349
- # token == TOKEN
350
+ # # Compare the tokens in a time-constant manner, to mitigate
351
+ # # timing attacks.
352
+ # ActiveSupport::SecurityUtils.secure_compare(
353
+ # ::Digest::SHA256.hexdigest(token),
354
+ # ::Digest::SHA256.hexdigest(TOKEN)
355
+ # )
350
356
  # end
351
357
  # end
352
358
  # end
@@ -365,7 +371,7 @@ module ActionController
365
371
  #
366
372
  # def authenticate
367
373
  # case request.format
368
- # when Mime::XML, Mime::ATOM
374
+ # when Mime[:xml], Mime[:atom]
369
375
  # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
370
376
  # @current_user = user
371
377
  # else
@@ -401,21 +407,21 @@ module ActionController
401
407
  # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
402
408
  module Token
403
409
  TOKEN_KEY = 'token='
404
- TOKEN_REGEX = /^Token /
410
+ TOKEN_REGEX = /^(Token|Bearer)\s+/
405
411
  AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
406
412
  extend self
407
413
 
408
414
  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)
415
+ def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
416
+ authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
411
417
  end
412
418
 
413
419
  def authenticate_with_http_token(&login_procedure)
414
420
  Token.authenticate(self, &login_procedure)
415
421
  end
416
422
 
417
- def request_http_token_authentication(realm = "Application")
418
- Token.authentication_request(self, realm)
423
+ def request_http_token_authentication(realm = "Application", message = nil)
424
+ Token.authentication_request(self, realm, message)
419
425
  end
420
426
  end
421
427
 
@@ -440,15 +446,17 @@ module ActionController
440
446
  end
441
447
  end
442
448
 
443
- # Parses the token and options out of the token authorization header. If
444
- # the header looks like this:
449
+ # Parses the token and options out of the token authorization header.
450
+ # The value for the Authorization header is expected to have the prefix
451
+ # <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
445
452
  # Authorization: Token token="abc", nonce="def"
446
- # Then the returned token is "abc", and the options is {nonce: "def"}
453
+ # Then the returned token is <tt>"abc"</tt>, and the options are
454
+ # <tt>{nonce: "def"}</tt>
447
455
  #
448
456
  # request - ActionDispatch::Request instance with the current headers.
449
457
  #
450
- # Returns an Array of [String, Hash] if a token is present.
451
- # Returns nil if no token is found.
458
+ # Returns an +Array+ of <tt>[String, Hash]</tt> if a token is present.
459
+ # Returns +nil+ if no token is found.
452
460
  def token_and_options(request)
453
461
  authorization_request = request.authorization.to_s
454
462
  if authorization_request[TOKEN_REGEX]
@@ -497,15 +505,16 @@ module ActionController
497
505
  "Token #{values * ", "}"
498
506
  end
499
507
 
500
- # Sets a WWW-Authenticate to let the client know a token is desired.
508
+ # Sets a WWW-Authenticate header to let the client know a token is desired.
501
509
  #
502
510
  # controller - ActionController::Base instance for the outgoing response.
503
511
  # realm - String realm to use in the header.
504
512
  #
505
513
  # 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
514
+ def authentication_request(controller, realm, message = nil)
515
+ message ||= "HTTP Token: Access denied.\n"
516
+ controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
517
+ controller.__send__ :render, plain: message, status: :unauthorized
509
518
  end
510
519
  end
511
520
  end
@@ -1,13 +1,63 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
1
3
  module ActionController
4
+ # Handles implicit rendering for a controller action that does not
5
+ # explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
6
+ #
7
+ # For API controllers, the implicit response is always 204 No Content.
8
+ #
9
+ # For all other controllers, we use these heuristics to decide whether to
10
+ # render a template, raise an error for a missing template, or respond with
11
+ # 204 No Content:
12
+ #
13
+ # First, if we DO find a template, it's rendered. Template lookup accounts
14
+ # for the action name, locales, format, variant, template handlers, and more
15
+ # (see +render+ for details).
16
+ #
17
+ # Second, if we DON'T find a template but the controller action does have
18
+ # templates for other formats, variants, etc., then we trust that you meant
19
+ # to provide a template for this response, too, and we raise
20
+ # <tt>ActionController::UnknownFormat</tt> with an explanation.
21
+ #
22
+ # Third, if we DON'T find a template AND the request is a page load in a web
23
+ # browser (technically, a non-XHR GET request for an HTML response) where
24
+ # you reasonably expect to have rendered a template, then we raise
25
+ # <tt>ActionView::UnknownFormat</tt> with an explanation.
26
+ #
27
+ # Finally, if we DON'T find a template AND the request isn't a browser page
28
+ # load, then we implicitly respond with 204 No Content.
2
29
  module ImplicitRender
3
- def send_action(method, *args)
4
- ret = super
5
- default_render unless performed?
6
- ret
7
- end
30
+
31
+ # :stopdoc:
32
+ include BasicImplicitRender
8
33
 
9
34
  def default_render(*args)
10
- render(*args)
35
+ if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
36
+ render(*args)
37
+ elsif any_templates?(action_name.to_s, _prefixes)
38
+ message = "#{self.class.name}\##{action_name} is missing a template " \
39
+ "for this request format and variant.\n" \
40
+ "\nrequest.formats: #{request.formats.map(&:to_s).inspect}" \
41
+ "\nrequest.variant: #{request.variant.inspect}"
42
+
43
+ raise ActionController::UnknownFormat, message
44
+ elsif interactive_browser_request?
45
+ message = "#{self.class.name}\##{action_name} is missing a template " \
46
+ "for this request format and variant.\n\n" \
47
+ "request.formats: #{request.formats.map(&:to_s).inspect}\n" \
48
+ "request.variant: #{request.variant.inspect}\n\n" \
49
+ "NOTE! For XHR/Ajax or API requests, this action would normally " \
50
+ "respond with 204 No Content: an empty white screen. Since you're " \
51
+ "loading it in a web browser, we assume that you expected to " \
52
+ "actually render a template, not… nothing, so we're showing an " \
53
+ "error to be extra-clear. If you expect 204 No Content, carry on. " \
54
+ "That's what you'll get from an XHR or API request. Give it a shot."
55
+
56
+ raise ActionController::UnknownFormat, message
57
+ else
58
+ logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
59
+ super
60
+ end
11
61
  end
12
62
 
13
63
  def method_for_action(action_name)
@@ -15,5 +65,10 @@ module ActionController
15
65
  "default_render"
16
66
  end
17
67
  end
68
+
69
+ private
70
+ def interactive_browser_request?
71
+ request.get? && request.format == Mime[:html] && !request.xhr?
72
+ end
18
73
  end
19
74
  end
@@ -11,7 +11,6 @@ module ActionController
11
11
  extend ActiveSupport::Concern
12
12
 
13
13
  include AbstractController::Logger
14
- include ActionController::RackDelegation
15
14
 
16
15
  attr_internal :view_runtime
17
16
 
@@ -20,9 +19,10 @@ module ActionController
20
19
  :controller => self.class.name,
21
20
  :action => self.action_name,
22
21
  :params => request.filtered_parameters,
23
- :format => request.format.try(:ref),
22
+ :headers => request.headers,
23
+ :format => request.format.ref,
24
24
  :method => request.request_method,
25
- :path => (request.fullpath rescue "unknown")
25
+ :path => request.fullpath
26
26
  }
27
27
 
28
28
  ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
@@ -75,8 +75,8 @@ module ActionController
75
75
  ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
76
76
  end
77
77
 
78
- # A hook which allows you to clean up any time taken into account in
79
- # views wrongly, like database querying time.
78
+ # A hook which allows you to clean up any time, wrongly taken into account in
79
+ # views, like database querying time.
80
80
  #
81
81
  # def cleanup_view_runtime
82
82
  # super - time_taken_in_something_expensive