actionpack 3.2.19 → 4.2.11.3

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