actionpack 3.2.22.5 → 5.2.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,22 +1,24 @@
1
- require 'active_support/base64'
2
- require 'active_support/core_ext/object/blank'
3
- require 'active_support/security_utils'
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "active_support/security_utils"
4
5
 
5
6
  module ActionController
7
+ # Makes it dead easy to do HTTP Basic, Digest and Token authentication.
6
8
  module HttpAuthentication
7
- # Makes it dead easy to do HTTP \Basic and \Digest authentication.
9
+ # Makes it dead easy to do HTTP \Basic authentication.
8
10
  #
9
11
  # === Simple \Basic example
10
12
  #
11
13
  # class PostsController < ApplicationController
12
- # http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
14
+ # http_basic_authenticate_with name: "dhh", password: "secret", except: :index
13
15
  #
14
16
  # def index
15
- # render :text => "Everyone can see me!"
17
+ # render plain: "Everyone can see me!"
16
18
  # end
17
19
  #
18
20
  # def edit
19
- # render :text => "I'm only accessible if you know the password"
21
+ # render plain: "I'm only accessible if you know the password"
20
22
  # end
21
23
  # end
22
24
  #
@@ -26,16 +28,16 @@ module ActionController
26
28
  # the regular HTML interface is protected by a session approach:
27
29
  #
28
30
  # class ApplicationController < ActionController::Base
29
- # before_filter :set_account, :authenticate
31
+ # before_action :set_account, :authenticate
30
32
  #
31
- # protected
33
+ # private
32
34
  # def set_account
33
- # @account = Account.find_by_url_name(request.subdomains.first)
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
@@ -54,54 +56,11 @@ module ActionController
54
56
  # In your integration tests, you can do something like this:
55
57
  #
56
58
  # def test_access_granted_from_xml
57
- # get(
58
- # "/notes/1.xml", nil,
59
- # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
60
- # )
59
+ # @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
60
+ # get "/notes/1.xml"
61
61
  #
62
62
  # assert_equal 200, status
63
63
  # end
64
- #
65
- # === Simple \Digest example
66
- #
67
- # require 'digest/md5'
68
- # class PostsController < ApplicationController
69
- # REALM = "SuperSecret"
70
- # USERS = {"dhh" => "secret", #plain text password
71
- # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
72
- #
73
- # before_filter :authenticate, :except => [:index]
74
- #
75
- # def index
76
- # render :text => "Everyone can see me!"
77
- # end
78
- #
79
- # def edit
80
- # render :text => "I'm only accessible if you know the password"
81
- # end
82
- #
83
- # private
84
- # def authenticate
85
- # authenticate_or_request_with_http_digest(REALM) do |username|
86
- # USERS[username]
87
- # end
88
- # end
89
- # end
90
- #
91
- # === Notes
92
- #
93
- # The +authenticate_or_request_with_http_digest+ block must return the user's password
94
- # or the ha1 digest hash so the framework can appropriately hash to check the user's
95
- # credentials. Returning +nil+ will cause authentication to fail.
96
- #
97
- # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
98
- # the password file or database is compromised, the attacker would be able to use the ha1 hash to
99
- # authenticate as the user at this +realm+, but would not have the user's password to try using at
100
- # other sites.
101
- #
102
- # In rare instances, web servers or front proxies strip authorization headers before
103
- # they reach your application. You can debug this situation by logging all environment
104
- # variables, and check for HTTP_AUTHORIZATION, amongst others.
105
64
  module Basic
106
65
  extend self
107
66
 
@@ -110,62 +69,117 @@ module ActionController
110
69
 
111
70
  module ClassMethods
112
71
  def http_basic_authenticate_with(options = {})
113
- before_filter(options.except(:name, :password, :realm)) do
72
+ before_action(options.except(:name, :password, :realm)) do
114
73
  authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
115
74
  # This comparison uses & so that it doesn't short circuit and
116
- # uses `variable_size_secure_compare` so that length information
75
+ # uses `secure_compare` so that length information
117
76
  # isn't leaked.
118
- ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
119
- 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])
120
79
  end
121
80
  end
122
81
  end
123
82
  end
124
83
 
125
- def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
126
- 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)
127
86
  end
128
87
 
129
88
  def authenticate_with_http_basic(&login_procedure)
130
89
  HttpAuthentication::Basic.authenticate(request, &login_procedure)
131
90
  end
132
91
 
133
- def request_http_basic_authentication(realm = "Application")
134
- HttpAuthentication::Basic.authentication_request(self, realm)
92
+ def request_http_basic_authentication(realm = "Application", message = nil)
93
+ HttpAuthentication::Basic.authentication_request(self, realm, message)
135
94
  end
136
95
  end
137
96
 
138
97
  def authenticate(request, &login_procedure)
139
- unless request.authorization.blank?
98
+ if has_basic_credentials?(request)
140
99
  login_procedure.call(*user_name_and_password(request))
141
100
  end
142
101
  end
143
102
 
103
+ def has_basic_credentials?(request)
104
+ request.authorization.present? && (auth_scheme(request).downcase == "basic")
105
+ end
106
+
144
107
  def user_name_and_password(request)
145
- decode_credentials(request).split(/:/, 2)
108
+ decode_credentials(request).split(":", 2)
146
109
  end
147
110
 
148
111
  def decode_credentials(request)
149
- ::Base64.decode64(request.authorization.split(' ', 2).last || '')
112
+ ::Base64.decode64(auth_param(request) || "")
113
+ end
114
+
115
+ def auth_scheme(request)
116
+ request.authorization.to_s.split(" ", 2).first
117
+ end
118
+
119
+ def auth_param(request)
120
+ request.authorization.to_s.split(" ", 2).second
150
121
  end
151
122
 
152
123
  def encode_credentials(user_name, password)
153
124
  "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
154
125
  end
155
126
 
156
- def authentication_request(controller, realm)
157
- controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
158
- controller.response_body = "HTTP Basic: Access denied.\n"
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)}")
159
130
  controller.status = 401
131
+ controller.response_body = message
160
132
  end
161
133
  end
162
134
 
135
+ # Makes it dead easy to do HTTP \Digest authentication.
136
+ #
137
+ # === Simple \Digest example
138
+ #
139
+ # require 'digest/md5'
140
+ # class PostsController < ApplicationController
141
+ # REALM = "SuperSecret"
142
+ # USERS = {"dhh" => "secret", #plain text password
143
+ # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
144
+ #
145
+ # before_action :authenticate, except: [:index]
146
+ #
147
+ # def index
148
+ # render plain: "Everyone can see me!"
149
+ # end
150
+ #
151
+ # def edit
152
+ # render plain: "I'm only accessible if you know the password"
153
+ # end
154
+ #
155
+ # private
156
+ # def authenticate
157
+ # authenticate_or_request_with_http_digest(REALM) do |username|
158
+ # USERS[username]
159
+ # end
160
+ # end
161
+ # end
162
+ #
163
+ # === Notes
164
+ #
165
+ # The +authenticate_or_request_with_http_digest+ block must return the user's password
166
+ # or the ha1 digest hash so the framework can appropriately hash to check the user's
167
+ # credentials. Returning +nil+ will cause authentication to fail.
168
+ #
169
+ # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
170
+ # the password file or database is compromised, the attacker would be able to use the ha1 hash to
171
+ # authenticate as the user at this +realm+, but would not have the user's password to try using at
172
+ # other sites.
173
+ #
174
+ # In rare instances, web servers or front proxies strip authorization headers before
175
+ # they reach your application. You can debug this situation by logging all environment
176
+ # variables, and check for HTTP_AUTHORIZATION, amongst others.
163
177
  module Digest
164
178
  extend self
165
179
 
166
180
  module ControllerMethods
167
- def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
168
- 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)
169
183
  end
170
184
 
171
185
  # Authenticate with HTTP Digest, returns true or false
@@ -196,8 +210,8 @@ module ActionController
196
210
  password = password_procedure.call(credentials[:username])
197
211
  return false unless password
198
212
 
199
- method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
200
- uri = credentials[:uri][0,1] == '/' ? request.original_fullpath : request.original_url
213
+ method = request.get_header("rack.methodoverride.original_method") || request.get_header("REQUEST_METHOD")
214
+ uri = credentials[:uri]
201
215
 
202
216
  [true, false].any? do |trailing_question_mark|
203
217
  [true, false].any? do |password_is_ha1|
@@ -212,19 +226,19 @@ module ActionController
212
226
  # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
213
227
  # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
214
228
  # of a plain-text password.
215
- 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)
216
230
  ha1 = password_is_ha1 ? password : ha1(credentials, password)
217
- ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
218
- ::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(":"))
219
233
  end
220
234
 
221
235
  def ha1(credentials, password)
222
- ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
236
+ ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
223
237
  end
224
238
 
225
239
  def encode_credentials(http_method, credentials, password, password_is_ha1)
226
240
  credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
227
- "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(", ")
228
242
  end
229
243
 
230
244
  def decode_credentials_header(request)
@@ -232,9 +246,9 @@ module ActionController
232
246
  end
233
247
 
234
248
  def decode_credentials(header)
235
- HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
236
- key, value = pair.split('=', 2)
237
- [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("'")]
238
252
  end]
239
253
  end
240
254
 
@@ -248,14 +262,14 @@ module ActionController
248
262
  def authentication_request(controller, realm, message = nil)
249
263
  message ||= "HTTP Digest: Access denied.\n"
250
264
  authentication_header(controller, realm)
251
- controller.response_body = message
252
265
  controller.status = 401
266
+ controller.response_body = message
253
267
  end
254
268
 
255
269
  def secret_token(request)
256
- secret = request.env["action_dispatch.secret_token"]
257
- raise "You must set config.secret_token in your app's config" if secret.blank?
258
- secret
270
+ key_generator = request.key_generator
271
+ http_auth_salt = request.http_auth_salt
272
+ key_generator.generate_key(http_auth_salt)
259
273
  end
260
274
 
261
275
  # Uses an MD5 digest based on time to generate a value to be used only once.
@@ -268,7 +282,7 @@ module ActionController
268
282
  # The quality of the implementation depends on a good choice.
269
283
  # A nonce might, for example, be constructed as the base 64 encoding of
270
284
  #
271
- # => time-stamp H(time-stamp ":" ETag ":" private-key)
285
+ # time-stamp H(time-stamp ":" ETag ":" private-key)
272
286
  #
273
287
  # where time-stamp is a server-generated time or other non-repeating value,
274
288
  # ETag is the value of the HTTP ETag header associated with the requested entity,
@@ -284,7 +298,7 @@ module ActionController
284
298
  #
285
299
  # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
286
300
  # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
287
- # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
301
+ # POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
288
302
  # of this document.
289
303
  #
290
304
  # The nonce is opaque to the client. Composed of Time, and hash of Time with secret
@@ -294,24 +308,24 @@ module ActionController
294
308
  t = time.to_i
295
309
  hashed = [t, secret_key]
296
310
  digest = ::Digest::MD5.hexdigest(hashed.join(":"))
297
- ::Base64.encode64("#{t}:#{digest}").gsub("\n", '')
311
+ ::Base64.strict_encode64("#{t}:#{digest}")
298
312
  end
299
313
 
300
314
  # Might want a shorter timeout depending on whether the request
301
- # is a 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.
302
316
  # Can be much shorter if the Stale directive is implemented. This would
303
- # 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
304
318
  # username and password.
305
- 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)
320
+ return false if value.nil?
306
321
  t = ::Base64.decode64(value).split(":").first.to_i
307
322
  nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
308
323
  end
309
324
 
310
- # Opaque based on random generation - but changing each request?
325
+ # Opaque based on digest of secret key
311
326
  def opaque(secret_key)
312
327
  ::Digest::MD5.hexdigest(secret_key)
313
328
  end
314
-
315
329
  end
316
330
 
317
331
  # Makes it dead easy to do HTTP Token authentication.
@@ -321,20 +335,22 @@ module ActionController
321
335
  # class PostsController < ApplicationController
322
336
  # TOKEN = "secret"
323
337
  #
324
- # before_filter :authenticate, :except => [ :index ]
338
+ # before_action :authenticate, except: [ :index ]
325
339
  #
326
340
  # def index
327
- # render :text => "Everyone can see me!"
341
+ # render plain: "Everyone can see me!"
328
342
  # end
329
343
  #
330
344
  # def edit
331
- # render :text => "I'm only accessible if you know the password"
345
+ # render plain: "I'm only accessible if you know the password"
332
346
  # end
333
347
  #
334
348
  # private
335
349
  # def authenticate
336
350
  # authenticate_or_request_with_http_token do |token, options|
337
- # token == TOKEN
351
+ # # Compare the tokens in a time-constant manner, to mitigate
352
+ # # timing attacks.
353
+ # ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
338
354
  # end
339
355
  # end
340
356
  # end
@@ -344,16 +360,16 @@ module ActionController
344
360
  # the regular HTML interface is protected by a session approach:
345
361
  #
346
362
  # class ApplicationController < ActionController::Base
347
- # before_filter :set_account, :authenticate
363
+ # before_action :set_account, :authenticate
348
364
  #
349
- # protected
365
+ # private
350
366
  # def set_account
351
- # @account = Account.find_by_url_name(request.subdomains.first)
367
+ # @account = Account.find_by(url_name: request.subdomains.first)
352
368
  # end
353
369
  #
354
370
  # def authenticate
355
371
  # case request.format
356
- # when Mime::XML, Mime::ATOM
372
+ # when Mime[:xml], Mime[:atom]
357
373
  # if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
358
374
  # @current_user = user
359
375
  # else
@@ -375,7 +391,7 @@ module ActionController
375
391
  # def test_access_granted_from_xml
376
392
  # get(
377
393
  # "/notes/1.xml", nil,
378
- # :authorization => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
394
+ # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
379
395
  # )
380
396
  #
381
397
  # assert_equal 200, status
@@ -388,32 +404,39 @@ module ActionController
388
404
  #
389
405
  # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
390
406
  module Token
407
+ TOKEN_KEY = "token="
408
+ TOKEN_REGEX = /^(Token|Bearer)\s+/
409
+ AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
391
410
  extend self
392
411
 
393
412
  module ControllerMethods
394
- def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
395
- 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)
396
415
  end
397
416
 
398
417
  def authenticate_with_http_token(&login_procedure)
399
418
  Token.authenticate(self, &login_procedure)
400
419
  end
401
420
 
402
- def request_http_token_authentication(realm = "Application")
403
- Token.authentication_request(self, realm)
421
+ def request_http_token_authentication(realm = "Application", message = nil)
422
+ Token.authentication_request(self, realm, message)
404
423
  end
405
424
  end
406
425
 
407
- # If token Authorization header is present, call the login procedure with
408
- # the present token and options.
426
+ # If token Authorization header is present, call the login
427
+ # procedure with the present token and options.
428
+ #
429
+ # [controller]
430
+ # ActionController::Base instance for the current request.
431
+ #
432
+ # [login_procedure]
433
+ # Proc to call if a token is present. The Proc should take two arguments:
409
434
  #
410
- # controller - ActionController::Base instance for the current request.
411
- # login_procedure - Proc to call if a token is present. The Proc should
412
- # take 2 arguments:
413
- # authenticate(controller) { |token, options| ... }
435
+ # authenticate(controller) { |token, options| ... }
414
436
  #
415
- # Returns the return value of `&login_procedure` if a token is found.
416
- # Returns nil if no token is found.
437
+ # Returns the return value of <tt>login_procedure</tt> if a
438
+ # token is found. Returns <tt>nil</tt> if no token is found.
439
+
417
440
  def authenticate(controller, &login_procedure)
418
441
  token, options = token_and_options(controller.request)
419
442
  unless token.blank?
@@ -421,28 +444,52 @@ module ActionController
421
444
  end
422
445
  end
423
446
 
424
- # Parses the token and options out of the token authorization header. If
425
- # 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:
426
450
  # Authorization: Token token="abc", nonce="def"
427
- # 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>
428
453
  #
429
454
  # request - ActionDispatch::Request instance with the current headers.
430
455
  #
431
- # Returns an Array of [String, Hash] if a token is present.
432
- # 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.
433
458
  def token_and_options(request)
434
- if request.authorization.to_s[/^Token (.*)/]
435
- values = Hash[$1.split(',').map do |value|
436
- value.strip! # remove any spaces between commas and values
437
- key, value = value.split(/\=\"?/) # split key=value pairs
438
- value.chomp!('"') # chomp trailing " in value
439
- value.gsub!(/\\\"/, '"') # unescape remaining quotes
440
- [key, value]
441
- end]
442
- [values.delete("token"), values.with_indifferent_access]
459
+ authorization_request = request.authorization.to_s
460
+ if authorization_request[TOKEN_REGEX]
461
+ params = token_params_from authorization_request
462
+ [params.shift[1], Hash[params].with_indifferent_access]
443
463
  end
444
464
  end
445
465
 
466
+ def token_params_from(auth)
467
+ rewrite_param_values params_array_from raw_params auth
468
+ end
469
+
470
+ # Takes raw_params and turns it into an array of parameters
471
+ def params_array_from(raw_params)
472
+ raw_params.map { |param| param.split %r/=(.+)?/ }
473
+ end
474
+
475
+ # This removes the <tt>"</tt> characters wrapping the value.
476
+ def rewrite_param_values(array_params)
477
+ array_params.each { |param| (param[1] || "".dup).gsub! %r/^"|"$/, "" }
478
+ end
479
+
480
+ # This method takes an authorization body and splits up the key-value
481
+ # pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
482
+ # delimiters defined in +AUTHN_PAIR_DELIMITERS+.
483
+ def raw_params(auth)
484
+ _raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
485
+
486
+ if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
487
+ _raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
488
+ end
489
+
490
+ _raw_params
491
+ end
492
+
446
493
  # Encodes the given token and options into an Authorization header value.
447
494
  #
448
495
  # token - String token.
@@ -450,21 +497,22 @@ module ActionController
450
497
  #
451
498
  # Returns String.
452
499
  def encode_credentials(token, options = {})
453
- values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
500
+ values = ["#{TOKEN_KEY}#{token.to_s.inspect}"] + options.map do |key, value|
454
501
  "#{key}=#{value.to_s.inspect}"
455
502
  end
456
503
  "Token #{values * ", "}"
457
504
  end
458
505
 
459
- # 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.
460
507
  #
461
508
  # controller - ActionController::Base instance for the outgoing response.
462
509
  # realm - String realm to use in the header.
463
510
  #
464
511
  # Returns nothing.
465
- def authentication_request(controller, realm)
466
- controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
467
- 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
468
516
  end
469
517
  end
470
518
  end
@@ -1,19 +1,73 @@
1
+ # frozen_string_literal: true
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 <tt>204 No Content</tt>.
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
+ # <tt>204 No Content</tt>:
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 <tt>204 No Content</tt>.
2
29
  module ImplicitRender
3
- def send_action(method, *args)
4
- ret = super
5
- default_render unless response_body
6
- ret
7
- end
30
+ # :stopdoc:
31
+ include BasicImplicitRender
8
32
 
9
33
  def default_render(*args)
10
- render(*args)
34
+ if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
35
+ render(*args)
36
+ elsif any_templates?(action_name.to_s, _prefixes)
37
+ message = "#{self.class.name}\##{action_name} is missing a template " \
38
+ "for this request format and variant.\n" \
39
+ "\nrequest.formats: #{request.formats.map(&:to_s).inspect}" \
40
+ "\nrequest.variant: #{request.variant.inspect}"
41
+
42
+ raise ActionController::UnknownFormat, message
43
+ elsif interactive_browser_request?
44
+ message = "#{self.class.name}\##{action_name} is missing a template " \
45
+ "for this request format and variant.\n\n" \
46
+ "request.formats: #{request.formats.map(&:to_s).inspect}\n" \
47
+ "request.variant: #{request.variant.inspect}\n\n" \
48
+ "NOTE! For XHR/Ajax or API requests, this action would normally " \
49
+ "respond with 204 No Content: an empty white screen. Since you're " \
50
+ "loading it in a web browser, we assume that you expected to " \
51
+ "actually render a template, not nothing, so we're showing an " \
52
+ "error to be extra-clear. If you expect 204 No Content, carry on. " \
53
+ "That's what you'll get from an XHR or API request. Give it a shot."
54
+
55
+ raise ActionController::UnknownFormat, message
56
+ else
57
+ logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
58
+ super
59
+ end
11
60
  end
12
61
 
13
62
  def method_for_action(action_name)
14
63
  super || if template_exists?(action_name.to_s, _prefixes)
15
- "default_render"
16
- end
64
+ "default_render"
65
+ end
17
66
  end
67
+
68
+ private
69
+ def interactive_browser_request?
70
+ request.get? && request.format == Mime[:html] && !request.xhr?
71
+ end
18
72
  end
19
73
  end