actionpack 2.2.3 → 2.3.2

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

Potentially problematic release.


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

Files changed (264) hide show
  1. data/CHANGELOG +433 -375
  2. data/MIT-LICENSE +1 -1
  3. data/README +21 -75
  4. data/Rakefile +1 -1
  5. data/lib/action_controller.rb +80 -43
  6. data/lib/action_controller/assertions/model_assertions.rb +1 -0
  7. data/lib/action_controller/assertions/response_assertions.rb +43 -16
  8. data/lib/action_controller/assertions/routing_assertions.rb +1 -1
  9. data/lib/action_controller/assertions/selector_assertions.rb +17 -12
  10. data/lib/action_controller/assertions/tag_assertions.rb +1 -4
  11. data/lib/action_controller/base.rb +153 -82
  12. data/lib/action_controller/benchmarking.rb +9 -9
  13. data/lib/action_controller/caching.rb +9 -11
  14. data/lib/action_controller/caching/actions.rb +11 -18
  15. data/lib/action_controller/caching/fragments.rb +28 -20
  16. data/lib/action_controller/caching/pages.rb +13 -15
  17. data/lib/action_controller/caching/sweeping.rb +2 -2
  18. data/lib/action_controller/cgi_ext.rb +0 -1
  19. data/lib/action_controller/cgi_ext/cookie.rb +2 -0
  20. data/lib/action_controller/cgi_process.rb +54 -162
  21. data/lib/action_controller/cookies.rb +13 -25
  22. data/lib/action_controller/dispatcher.rb +43 -122
  23. data/lib/action_controller/failsafe.rb +52 -0
  24. data/lib/action_controller/flash.rb +38 -47
  25. data/lib/action_controller/helpers.rb +13 -9
  26. data/lib/action_controller/http_authentication.rb +203 -23
  27. data/lib/action_controller/integration.rb +126 -70
  28. data/lib/action_controller/layout.rb +36 -39
  29. data/lib/action_controller/middleware_stack.rb +119 -0
  30. data/lib/action_controller/middlewares.rb +13 -0
  31. data/lib/action_controller/mime_responds.rb +19 -4
  32. data/lib/action_controller/mime_type.rb +8 -0
  33. data/lib/action_controller/params_parser.rb +71 -0
  34. data/lib/action_controller/performance_test.rb +0 -1
  35. data/lib/action_controller/polymorphic_routes.rb +36 -30
  36. data/lib/action_controller/reloader.rb +14 -0
  37. data/lib/action_controller/request.rb +107 -499
  38. data/lib/action_controller/request_forgery_protection.rb +7 -39
  39. data/lib/action_controller/rescue.rb +55 -35
  40. data/lib/action_controller/resources.rb +34 -31
  41. data/lib/action_controller/response.rb +99 -57
  42. data/lib/action_controller/rewindable_input.rb +28 -0
  43. data/lib/action_controller/routing.rb +7 -7
  44. data/lib/action_controller/routing/builder.rb +4 -1
  45. data/lib/action_controller/routing/optimisations.rb +1 -1
  46. data/lib/action_controller/routing/recognition_optimisation.rb +1 -2
  47. data/lib/action_controller/routing/route.rb +15 -5
  48. data/lib/action_controller/routing/route_set.rb +82 -35
  49. data/lib/action_controller/routing/segments.rb +35 -0
  50. data/lib/action_controller/session/abstract_store.rb +181 -0
  51. data/lib/action_controller/session/cookie_store.rb +197 -175
  52. data/lib/action_controller/session/mem_cache_store.rb +36 -83
  53. data/lib/action_controller/session_management.rb +26 -134
  54. data/lib/action_controller/streaming.rb +24 -7
  55. data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
  56. data/lib/action_controller/templates/rescues/template_error.erb +2 -2
  57. data/lib/action_controller/test_case.rb +87 -30
  58. data/lib/action_controller/test_process.rb +145 -104
  59. data/lib/action_controller/uploaded_file.rb +44 -0
  60. data/lib/action_controller/url_rewriter.rb +3 -6
  61. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  62. data/lib/action_controller/vendor/html-scanner/html/selector.rb +1 -1
  63. data/lib/action_controller/vendor/rack-1.0/rack.rb +89 -0
  64. data/lib/action_controller/vendor/rack-1.0/rack/adapter/camping.rb +22 -0
  65. data/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb +37 -0
  66. data/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/request.rb +37 -0
  67. data/lib/action_controller/vendor/rack-1.0/rack/auth/basic.rb +58 -0
  68. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb +124 -0
  69. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/nonce.rb +51 -0
  70. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/params.rb +55 -0
  71. data/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb +40 -0
  72. data/lib/action_controller/vendor/rack-1.0/rack/auth/openid.rb +480 -0
  73. data/lib/action_controller/vendor/rack-1.0/rack/builder.rb +63 -0
  74. data/lib/action_controller/vendor/rack-1.0/rack/cascade.rb +36 -0
  75. data/lib/action_controller/vendor/rack-1.0/rack/chunked.rb +49 -0
  76. data/lib/action_controller/vendor/rack-1.0/rack/commonlogger.rb +61 -0
  77. data/lib/action_controller/vendor/rack-1.0/rack/conditionalget.rb +45 -0
  78. data/lib/action_controller/vendor/rack-1.0/rack/content_length.rb +29 -0
  79. data/lib/action_controller/vendor/rack-1.0/rack/content_type.rb +23 -0
  80. data/lib/action_controller/vendor/rack-1.0/rack/deflater.rb +85 -0
  81. data/lib/action_controller/vendor/rack-1.0/rack/directory.rb +153 -0
  82. data/lib/action_controller/vendor/rack-1.0/rack/file.rb +88 -0
  83. data/lib/action_controller/vendor/rack-1.0/rack/handler.rb +48 -0
  84. data/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb +61 -0
  85. data/lib/action_controller/vendor/rack-1.0/rack/handler/evented_mongrel.rb +8 -0
  86. data/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb +89 -0
  87. data/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb +55 -0
  88. data/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb +84 -0
  89. data/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb +59 -0
  90. data/lib/action_controller/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb +8 -0
  91. data/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb +18 -0
  92. data/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb +67 -0
  93. data/lib/action_controller/vendor/rack-1.0/rack/head.rb +19 -0
  94. data/lib/action_controller/vendor/rack-1.0/rack/lint.rb +462 -0
  95. data/lib/action_controller/vendor/rack-1.0/rack/lobster.rb +65 -0
  96. data/lib/action_controller/vendor/rack-1.0/rack/lock.rb +16 -0
  97. data/lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb +27 -0
  98. data/lib/action_controller/vendor/rack-1.0/rack/mime.rb +204 -0
  99. data/lib/action_controller/vendor/rack-1.0/rack/mock.rb +160 -0
  100. data/lib/action_controller/vendor/rack-1.0/rack/recursive.rb +57 -0
  101. data/lib/action_controller/vendor/rack-1.0/rack/reloader.rb +64 -0
  102. data/lib/action_controller/vendor/rack-1.0/rack/request.rb +241 -0
  103. data/lib/action_controller/vendor/rack-1.0/rack/response.rb +179 -0
  104. data/lib/action_controller/vendor/rack-1.0/rack/session/abstract/id.rb +142 -0
  105. data/lib/action_controller/vendor/rack-1.0/rack/session/cookie.rb +91 -0
  106. data/lib/action_controller/vendor/rack-1.0/rack/session/memcache.rb +109 -0
  107. data/lib/action_controller/vendor/rack-1.0/rack/session/pool.rb +100 -0
  108. data/lib/action_controller/vendor/rack-1.0/rack/showexceptions.rb +349 -0
  109. data/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb +106 -0
  110. data/lib/action_controller/vendor/rack-1.0/rack/static.rb +38 -0
  111. data/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb +55 -0
  112. data/lib/action_controller/vendor/rack-1.0/rack/utils.rb +392 -0
  113. data/lib/action_controller/verification.rb +1 -1
  114. data/lib/action_pack.rb +1 -1
  115. data/lib/action_pack/version.rb +2 -2
  116. data/lib/action_view.rb +22 -17
  117. data/lib/action_view/base.rb +53 -79
  118. data/lib/action_view/erb/util.rb +38 -0
  119. data/lib/action_view/helpers.rb +24 -5
  120. data/lib/action_view/helpers/active_record_helper.rb +2 -2
  121. data/lib/action_view/helpers/asset_tag_helper.rb +81 -50
  122. data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
  123. data/lib/action_view/helpers/benchmark_helper.rb +26 -5
  124. data/lib/action_view/helpers/date_helper.rb +82 -7
  125. data/lib/action_view/helpers/form_helper.rb +295 -64
  126. data/lib/action_view/helpers/form_options_helper.rb +160 -18
  127. data/lib/action_view/helpers/form_tag_helper.rb +2 -2
  128. data/lib/action_view/helpers/number_helper.rb +31 -18
  129. data/lib/action_view/helpers/prototype_helper.rb +2 -12
  130. data/lib/action_view/helpers/sanitize_helper.rb +0 -10
  131. data/lib/action_view/helpers/scriptaculous_helper.rb +1 -0
  132. data/lib/action_view/helpers/tag_helper.rb +3 -4
  133. data/lib/action_view/helpers/text_helper.rb +99 -122
  134. data/lib/action_view/helpers/translation_helper.rb +19 -1
  135. data/lib/action_view/helpers/url_helper.rb +25 -2
  136. data/lib/action_view/inline_template.rb +1 -1
  137. data/lib/action_view/locale/en.yml +19 -1
  138. data/lib/action_view/partials.rb +46 -9
  139. data/lib/action_view/paths.rb +28 -84
  140. data/lib/action_view/reloadable_template.rb +117 -0
  141. data/lib/action_view/renderable.rb +28 -35
  142. data/lib/action_view/renderable_partial.rb +3 -4
  143. data/lib/action_view/template.rb +172 -31
  144. data/lib/action_view/template_error.rb +8 -9
  145. data/lib/action_view/template_handler.rb +1 -1
  146. data/lib/action_view/template_handlers.rb +9 -6
  147. data/lib/action_view/template_handlers/erb.rb +2 -39
  148. data/lib/action_view/template_handlers/rjs.rb +1 -0
  149. data/lib/action_view/test_case.rb +27 -1
  150. data/test/abstract_unit.rb +23 -17
  151. data/test/active_record_unit.rb +5 -4
  152. data/test/activerecord/active_record_store_test.rb +139 -106
  153. data/test/activerecord/render_partial_with_record_identification_test.rb +5 -21
  154. data/test/controller/action_pack_assertions_test.rb +25 -23
  155. data/test/controller/addresses_render_test.rb +3 -6
  156. data/test/controller/assert_select_test.rb +83 -70
  157. data/test/controller/base_test.rb +11 -13
  158. data/test/controller/benchmark_test.rb +3 -3
  159. data/test/controller/caching_test.rb +34 -24
  160. data/test/controller/capture_test.rb +3 -6
  161. data/test/controller/content_type_test.rb +3 -6
  162. data/test/controller/cookie_test.rb +31 -66
  163. data/test/controller/deprecation/deprecated_base_methods_test.rb +9 -11
  164. data/test/controller/dispatcher_test.rb +23 -28
  165. data/test/controller/fake_models.rb +8 -0
  166. data/test/controller/filters_test.rb +6 -2
  167. data/test/controller/flash_test.rb +2 -6
  168. data/test/controller/helper_test.rb +15 -1
  169. data/test/controller/html-scanner/document_test.rb +1 -1
  170. data/test/controller/html-scanner/sanitizer_test.rb +1 -1
  171. data/test/controller/http_basic_authentication_test.rb +88 -0
  172. data/test/controller/http_digest_authentication_test.rb +178 -0
  173. data/test/controller/integration_test.rb +56 -52
  174. data/test/controller/layout_test.rb +46 -44
  175. data/test/controller/middleware_stack_test.rb +90 -0
  176. data/test/controller/mime_responds_test.rb +7 -11
  177. data/test/controller/mime_type_test.rb +9 -0
  178. data/test/controller/polymorphic_routes_test.rb +235 -151
  179. data/test/controller/rack_test.rb +52 -81
  180. data/test/controller/redirect_test.rb +6 -14
  181. data/test/controller/render_test.rb +273 -60
  182. data/test/controller/request/json_params_parsing_test.rb +45 -0
  183. data/test/controller/request/multipart_params_parsing_test.rb +223 -0
  184. data/test/controller/request/query_string_parsing_test.rb +120 -0
  185. data/test/controller/request/url_encoded_params_parsing_test.rb +184 -0
  186. data/test/controller/request/xml_params_parsing_test.rb +88 -0
  187. data/test/controller/request_forgery_protection_test.rb +17 -98
  188. data/test/controller/request_test.rb +45 -530
  189. data/test/controller/rescue_test.rb +45 -22
  190. data/test/controller/resources_test.rb +112 -37
  191. data/test/controller/routing_test.rb +1442 -1384
  192. data/test/controller/selector_test.rb +3 -3
  193. data/test/controller/send_file_test.rb +30 -3
  194. data/test/controller/session/cookie_store_test.rb +169 -240
  195. data/test/controller/session/mem_cache_store_test.rb +94 -148
  196. data/test/controller/session/test_session_test.rb +58 -0
  197. data/test/controller/test_test.rb +32 -13
  198. data/test/controller/url_rewriter_test.rb +54 -4
  199. data/test/controller/verification_test.rb +1 -1
  200. data/test/controller/view_paths_test.rb +15 -15
  201. data/test/controller/webservice_test.rb +178 -147
  202. data/test/fixtures/alternate_helpers/foo_helper.rb +3 -0
  203. data/test/fixtures/layout_tests/alt/layouts/alt.rhtml +0 -0
  204. data/test/fixtures/layouts/default_html.html.erb +1 -0
  205. data/test/fixtures/layouts/xhr.html.erb +2 -0
  206. data/test/fixtures/multipart/empty +10 -0
  207. data/test/fixtures/multipart/hello.txt +1 -0
  208. data/test/fixtures/multipart/none +9 -0
  209. data/test/fixtures/public/500.da.html +1 -0
  210. data/test/fixtures/quiz/questions/_question.html.erb +1 -0
  211. data/test/fixtures/replies.yml +1 -1
  212. data/test/fixtures/test/_one.html.erb +1 -0
  213. data/test/fixtures/test/_two.html.erb +1 -0
  214. data/test/fixtures/test/dont_pick_me +1 -0
  215. data/test/fixtures/test/hello.builder +1 -1
  216. data/test/fixtures/test/hello_world.da.html.erb +1 -0
  217. data/test/fixtures/test/hello_world.erb~ +1 -0
  218. data/test/fixtures/test/hello_world.pt-BR.html.erb +1 -0
  219. data/test/fixtures/test/malformed/malformed.en.html.erb~ +1 -0
  220. data/test/fixtures/test/malformed/malformed.erb~ +1 -0
  221. data/test/fixtures/test/malformed/malformed.html.erb~ +1 -0
  222. data/test/fixtures/test/render_explicit_html_template.js.rjs +1 -0
  223. data/test/fixtures/test/render_implicit_html_template.js.rjs +1 -0
  224. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +1 -0
  225. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +1 -0
  226. data/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +1 -0
  227. data/test/fixtures/test/utf8.html.erb +2 -0
  228. data/test/template/active_record_helper_i18n_test.rb +31 -33
  229. data/test/template/active_record_helper_test.rb +34 -0
  230. data/test/template/asset_tag_helper_test.rb +52 -14
  231. data/test/template/atom_feed_helper_test.rb +3 -5
  232. data/test/template/benchmark_helper_test.rb +50 -24
  233. data/test/template/compiled_templates_test.rb +177 -33
  234. data/test/template/date_helper_i18n_test.rb +88 -81
  235. data/test/template/date_helper_test.rb +427 -43
  236. data/test/template/form_helper_test.rb +243 -44
  237. data/test/template/form_options_helper_test.rb +631 -565
  238. data/test/template/form_tag_helper_test.rb +9 -2
  239. data/test/template/javascript_helper_test.rb +0 -5
  240. data/test/template/number_helper_i18n_test.rb +60 -48
  241. data/test/template/number_helper_test.rb +1 -0
  242. data/test/template/render_test.rb +117 -35
  243. data/test/template/test_test.rb +4 -6
  244. data/test/template/text_helper_test.rb +129 -50
  245. data/test/template/translation_helper_test.rb +23 -19
  246. data/test/template/url_helper_test.rb +35 -2
  247. data/test/view/test_case_test.rb +8 -0
  248. metadata +197 -23
  249. data/lib/action_controller/assertions.rb +0 -69
  250. data/lib/action_controller/caching/sql_cache.rb +0 -18
  251. data/lib/action_controller/cgi_ext/session.rb +0 -53
  252. data/lib/action_controller/components.rb +0 -169
  253. data/lib/action_controller/rack_process.rb +0 -297
  254. data/lib/action_controller/request_profiler.rb +0 -169
  255. data/lib/action_controller/session/active_record_store.rb +0 -340
  256. data/lib/action_controller/session/drb_server.rb +0 -32
  257. data/lib/action_controller/session/drb_store.rb +0 -35
  258. data/test/controller/cgi_test.rb +0 -269
  259. data/test/controller/components_test.rb +0 -156
  260. data/test/controller/http_authentication_test.rb +0 -54
  261. data/test/controller/integration_upload_test.rb +0 -43
  262. data/test/controller/session_fixation_test.rb +0 -89
  263. data/test/controller/session_management_test.rb +0 -178
  264. data/test/fixtures/test/hello_world.js +0 -1
@@ -1,199 +1,221 @@
1
- require 'cgi'
2
- require 'cgi/session'
3
- require 'openssl' # to generate the HMAC message digest
4
-
5
- # This cookie-based session store is the Rails default. Sessions typically
6
- # contain at most a user_id and flash message; both fit within the 4K cookie
7
- # size limit. Cookie-based sessions are dramatically faster than the
8
- # alternatives.
9
- #
10
- # If you have more than 4K of session data or don't want your data to be
11
- # visible to the user, pick another session store.
12
- #
13
- # CookieOverflow is raised if you attempt to store more than 4K of data.
14
- # TamperedWithCookie is raised if the data integrity check fails.
15
- #
16
- # A message digest is included with the cookie to ensure data integrity:
17
- # a user cannot alter his +user_id+ without knowing the secret key included in
18
- # the hash. New apps are generated with a pregenerated secret in
19
- # config/environment.rb. Set your own for old apps you're upgrading.
20
- #
21
- # Session options:
22
- #
23
- # * <tt>:secret</tt>: An application-wide key string or block returning a string
24
- # called per generated digest. The block is called with the CGI::Session
25
- # instance as an argument. It's important that the secret is not vulnerable to
26
- # a dictionary attack. Therefore, you should choose a secret consisting of
27
- # random numbers and letters and more than 30 characters. Examples:
28
- #
29
- # :secret => '449fe2e7daee471bffae2fd8dc02313d'
30
- # :secret => Proc.new { User.current_user.secret_key }
31
- #
32
- # * <tt>:digest</tt>: The message digest algorithm used to verify session
33
- # integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
34
- # such as 'MD5', 'RIPEMD160', 'SHA256', etc.
35
- #
36
- # To generate a secret key for an existing application, run
37
- # "rake secret" and set the key in config/environment.rb.
38
- #
39
- # Note that changing digest or secret invalidates all existing sessions!
40
- class CGI::Session::CookieStore
41
- # Cookies can typically store 4096 bytes.
42
- MAX = 4096
43
- SECRET_MIN_LENGTH = 30 # characters
44
-
45
- # Raised when storing more than 4K of session data.
46
- class CookieOverflow < StandardError; end
47
-
48
- # Raised when the cookie fails its integrity check.
49
- class TamperedWithCookie < StandardError; end
50
-
51
- # Called from CGI::Session only.
52
- def initialize(session, options = {})
53
- # The session_key option is required.
54
- if options['session_key'].blank?
55
- raise ArgumentError, 'A session_key is required to write a cookie containing the session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase" } in config/environment.rb'
56
- end
1
+ module ActionController
2
+ module Session
3
+ # This cookie-based session store is the Rails default. Sessions typically
4
+ # contain at most a user_id and flash message; both fit within the 4K cookie
5
+ # size limit. Cookie-based sessions are dramatically faster than the
6
+ # alternatives.
7
+ #
8
+ # If you have more than 4K of session data or don't want your data to be
9
+ # visible to the user, pick another session store.
10
+ #
11
+ # CookieOverflow is raised if you attempt to store more than 4K of data.
12
+ #
13
+ # A message digest is included with the cookie to ensure data integrity:
14
+ # a user cannot alter his +user_id+ without knowing the secret key
15
+ # included in the hash. New apps are generated with a pregenerated secret
16
+ # in config/environment.rb. Set your own for old apps you're upgrading.
17
+ #
18
+ # Session options:
19
+ #
20
+ # * <tt>:secret</tt>: An application-wide key string or block returning a
21
+ # string called per generated digest. The block is called with the
22
+ # CGI::Session instance as an argument. It's important that the secret
23
+ # is not vulnerable to a dictionary attack. Therefore, you should choose
24
+ # a secret consisting of random numbers and letters and more than 30
25
+ # characters. Examples:
26
+ #
27
+ # :secret => '449fe2e7daee471bffae2fd8dc02313d'
28
+ # :secret => Proc.new { User.current_user.secret_key }
29
+ #
30
+ # * <tt>:digest</tt>: The message digest algorithm used to verify session
31
+ # integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
32
+ # such as 'MD5', 'RIPEMD160', 'SHA256', etc.
33
+ #
34
+ # To generate a secret key for an existing application, run
35
+ # "rake secret" and set the key in config/environment.rb.
36
+ #
37
+ # Note that changing digest or secret invalidates all existing sessions!
38
+ class CookieStore
39
+ # Cookies can typically store 4096 bytes.
40
+ MAX = 4096
41
+ SECRET_MIN_LENGTH = 30 # characters
42
+
43
+ DEFAULT_OPTIONS = {
44
+ :key => '_session_id',
45
+ :domain => nil,
46
+ :path => "/",
47
+ :expire_after => nil,
48
+ :httponly => true
49
+ }.freeze
50
+
51
+ ENV_SESSION_KEY = "rack.session".freeze
52
+ ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
53
+ HTTP_SET_COOKIE = "Set-Cookie".freeze
54
+
55
+ # Raised when storing more than 4K of session data.
56
+ class CookieOverflow < StandardError; end
57
+
58
+ def initialize(app, options = {})
59
+ # Process legacy CGI options
60
+ options = options.symbolize_keys
61
+ if options.has_key?(:session_path)
62
+ options[:path] = options.delete(:session_path)
63
+ end
64
+ if options.has_key?(:session_key)
65
+ options[:key] = options.delete(:session_key)
66
+ end
67
+ if options.has_key?(:session_http_only)
68
+ options[:httponly] = options.delete(:session_http_only)
69
+ end
57
70
 
58
- # The secret option is required.
59
- ensure_secret_secure(options['secret'])
60
-
61
- # Keep the session and its secret on hand so we can read and write cookies.
62
- @session, @secret = session, options['secret']
63
-
64
- # Message digest defaults to SHA1.
65
- @digest = options['digest'] || 'SHA1'
66
-
67
- # Default cookie options derived from session settings.
68
- @cookie_options = {
69
- 'name' => options['session_key'],
70
- 'path' => options['session_path'],
71
- 'domain' => options['session_domain'],
72
- 'expires' => options['session_expires'],
73
- 'secure' => options['session_secure'],
74
- 'http_only' => options['session_http_only']
75
- }
76
-
77
- # Set no_hidden and no_cookies since the session id is unused and we
78
- # set our own data cookie.
79
- options['no_hidden'] = true
80
- options['no_cookies'] = true
81
- end
71
+ @app = app
82
72
 
83
- # To prevent users from using something insecure like "Password" we make sure that the
84
- # secret they've provided is at least 30 characters in length.
85
- def ensure_secret_secure(secret)
86
- # There's no way we can do this check if they've provided a proc for the
87
- # secret.
88
- return true if secret.is_a?(Proc)
73
+ # The session_key option is required.
74
+ ensure_session_key(options[:key])
75
+ @key = options.delete(:key).freeze
89
76
 
90
- if secret.blank?
91
- raise ArgumentError, %Q{A secret is required to generate an integrity hash for cookie session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase of at least #{SECRET_MIN_LENGTH} characters" } in config/environment.rb}
92
- end
77
+ # The secret option is required.
78
+ ensure_secret_secure(options[:secret])
79
+ @secret = options.delete(:secret).freeze
93
80
 
94
- if secret.length < SECRET_MIN_LENGTH
95
- raise ArgumentError, %Q{Secret should be something secure, like "#{CGI::Session.generate_unique_id}". The value you provided, "#{secret}", is shorter than the minimum length of #{SECRET_MIN_LENGTH} characters}
96
- end
97
- end
81
+ @digest = options.delete(:digest) || 'SHA1'
82
+ @verifier = verifier_for(@secret, @digest)
98
83
 
99
- # Restore session data from the cookie.
100
- def restore
101
- @original = read_cookie
102
- @data = unmarshal(@original) || {}
103
- end
84
+ @default_options = DEFAULT_OPTIONS.merge(options).freeze
104
85
 
105
- # Wait until close to write the session data cookie.
106
- def update; end
86
+ freeze
87
+ end
107
88
 
108
- # Write the session data cookie if it was loaded and has changed.
109
- def close
110
- if defined?(@data) && !@data.blank?
111
- updated = marshal(@data)
112
- raise CookieOverflow if updated.size > MAX
113
- write_cookie('value' => updated) unless updated == @original
114
- end
115
- end
89
+ def call(env)
90
+ env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
91
+ env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
116
92
 
117
- # Delete the session data by setting an expired cookie with no data.
118
- def delete
119
- @data = nil
120
- clear_old_cookie_value
121
- write_cookie('value' => nil, 'expires' => 1.year.ago)
122
- end
93
+ status, headers, body = @app.call(env)
123
94
 
124
- # Generate the HMAC keyed message digest. Uses SHA1 by default.
125
- def generate_digest(data)
126
- key = @secret.respond_to?(:call) ? @secret.call(@session) : @secret
127
- OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), key, data)
128
- end
95
+ session_data = env[ENV_SESSION_KEY]
96
+ options = env[ENV_SESSION_OPTIONS_KEY]
129
97
 
130
- private
131
- # Marshal a session hash into safe cookie data. Include an integrity hash.
132
- def marshal(session)
133
- data = ActiveSupport::Base64.encode64s(Marshal.dump(session))
134
- "#{data}--#{generate_digest(data)}"
135
- end
98
+ if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
99
+ session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
100
+ session_data = marshal(session_data.to_hash)
136
101
 
137
- # Unmarshal cookie data to a hash and verify its integrity.
138
- def unmarshal(cookie)
139
- if cookie
140
- data, digest = cookie.split('--')
102
+ raise CookieOverflow if session_data.size > MAX
141
103
 
142
- # Do two checks to transparently support old double-escaped data.
143
- unless secure_compare(digest, generate_digest(data)) || secure_compare(digest, generate_digest(data = CGI.unescape(data)))
144
- delete
145
- raise TamperedWithCookie
104
+ cookie = Hash.new
105
+ cookie[:value] = session_data
106
+ unless options[:expire_after].nil?
107
+ cookie[:expires] = Time.now + options[:expire_after]
108
+ end
109
+
110
+ cookie = build_cookie(@key, cookie.merge(options))
111
+ unless headers[HTTP_SET_COOKIE].blank?
112
+ headers[HTTP_SET_COOKIE] << "\n#{cookie}"
113
+ else
114
+ headers[HTTP_SET_COOKIE] = cookie
115
+ end
146
116
  end
147
117
 
148
- Marshal.load(ActiveSupport::Base64.decode64(data))
118
+ [status, headers, body]
149
119
  end
150
- end
151
120
 
152
- # Read the session data cookie.
153
- def read_cookie
154
- @session.cgi.cookies[@cookie_options['name']].first
155
- end
121
+ private
122
+ # Should be in Rack::Utils soon
123
+ def build_cookie(key, value)
124
+ case value
125
+ when Hash
126
+ domain = "; domain=" + value[:domain] if value[:domain]
127
+ path = "; path=" + value[:path] if value[:path]
128
+ # According to RFC 2109, we need dashes here.
129
+ # N.B.: cgi.rb uses spaces...
130
+ expires = "; expires=" + value[:expires].clone.gmtime.
131
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
132
+ secure = "; secure" if value[:secure]
133
+ httponly = "; HttpOnly" if value[:httponly]
134
+ value = value[:value]
135
+ end
136
+ value = [value] unless Array === value
137
+ cookie = Rack::Utils.escape(key) + "=" +
138
+ value.map { |v| Rack::Utils.escape(v) }.join("&") +
139
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
140
+ end
156
141
 
157
- # CGI likes to make you hack.
158
- def write_cookie(options)
159
- cookie = CGI::Cookie.new(@cookie_options.merge(options))
160
- @session.cgi.send :instance_variable_set, '@output_cookies', [cookie]
161
- end
142
+ def load_session(env)
143
+ request = Rack::Request.new(env)
144
+ session_data = request.cookies[@key]
145
+ data = unmarshal(session_data) || persistent_session_id!({})
146
+ [data[:session_id], data]
147
+ end
162
148
 
163
- # Clear cookie value so subsequent new_session doesn't reload old data.
164
- def clear_old_cookie_value
165
- @session.cgi.cookies[@cookie_options['name']].clear
166
- end
167
-
168
- if "foo".respond_to?(:force_encoding)
169
- # constant-time comparison algorithm to prevent timing attacks
170
- def secure_compare(a, b)
171
- a = a.dup.force_encoding(Encoding::BINARY)
172
- b = b.dup.force_encoding(Encoding::BINARY)
173
-
174
- if a.length == b.length
175
- result = 0
176
- for i in 0..(a.length - 1)
177
- result |= a[i].ord ^ b[i].ord
149
+ # Marshal a session hash into safe cookie data. Include an integrity hash.
150
+ def marshal(session)
151
+ @verifier.generate(persistent_session_id!(session))
152
+ end
153
+
154
+ # Unmarshal cookie data to a hash and verify its integrity.
155
+ def unmarshal(cookie)
156
+ persistent_session_id!(@verifier.verify(cookie)) if cookie
157
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
158
+ nil
159
+ end
160
+
161
+ def ensure_session_key(key)
162
+ if key.blank?
163
+ raise ArgumentError, 'A key is required to write a ' +
164
+ 'cookie containing the session data. Use ' +
165
+ 'config.action_controller.session = { :key => ' +
166
+ '"_myapp_session", :secret => "some secret phrase" } in ' +
167
+ 'config/environment.rb'
178
168
  end
179
- result == 0
180
- else
181
- false
182
169
  end
183
- end
184
- else
185
- # For 1.8
186
- def secure_compare(a, b)
187
- if a.length == b.length
188
- result = 0
189
- for i in 0..(a.length - 1)
190
- result |= a[i] ^ b[i]
170
+
171
+ # To prevent users from using something insecure like "Password" we make sure that the
172
+ # secret they've provided is at least 30 characters in length.
173
+ def ensure_secret_secure(secret)
174
+ # There's no way we can do this check if they've provided a proc for the
175
+ # secret.
176
+ return true if secret.is_a?(Proc)
177
+
178
+ if secret.blank?
179
+ raise ArgumentError, "A secret is required to generate an " +
180
+ "integrity hash for cookie session data. Use " +
181
+ "config.action_controller.session = { :key => " +
182
+ "\"_myapp_session\", :secret => \"some secret phrase of at " +
183
+ "least #{SECRET_MIN_LENGTH} characters\" } " +
184
+ "in config/environment.rb"
185
+ end
186
+
187
+ if secret.length < SECRET_MIN_LENGTH
188
+ raise ArgumentError, "Secret should be something secure, " +
189
+ "like \"#{ActiveSupport::SecureRandom.hex(16)}\". The value you " +
190
+ "provided, \"#{secret}\", is shorter than the minimum length " +
191
+ "of #{SECRET_MIN_LENGTH} characters"
191
192
  end
192
- result == 0
193
- else
194
- false
195
193
  end
196
- end
197
- end
198
194
 
195
+ def verifier_for(secret, digest)
196
+ key = secret.respond_to?(:call) ? secret.call : secret
197
+ ActiveSupport::MessageVerifier.new(key, digest)
198
+ end
199
+
200
+ def generate_sid
201
+ ActiveSupport::SecureRandom.hex(16)
202
+ end
203
+
204
+ def persistent_session_id!(data)
205
+ (data ||= {}).merge!(inject_persistent_session_id(data))
206
+ end
207
+
208
+ def inject_persistent_session_id(data)
209
+ requires_session_id?(data) ? { :session_id => generate_sid } : {}
210
+ end
211
+
212
+ def requires_session_id?(data)
213
+ if data
214
+ data.respond_to?(:key?) && !data.key?(:session_id)
215
+ else
216
+ true
217
+ end
218
+ end
219
+ end
220
+ end
199
221
  end
@@ -1,95 +1,48 @@
1
- # cgi/session/memcached.rb - persistent storage of marshalled session data
2
- #
3
- # == Overview
4
- #
5
- # This file provides the CGI::Session::MemCache class, which builds
6
- # persistence of storage data on top of the MemCache library. See
7
- # cgi/session.rb for more details on session storage managers.
8
- #
9
-
10
1
  begin
11
- require 'cgi/session'
12
2
  require_library_or_gem 'memcache'
13
3
 
14
- class CGI
15
- class Session
16
- # MemCache-based session storage class.
17
- #
18
- # This builds upon the top-level MemCache class provided by the
19
- # library file memcache.rb. Session data is marshalled and stored
20
- # in a memcached cache.
21
- class MemCacheStore
22
- def check_id(id) #:nodoc:#
23
- /[^0-9a-zA-Z]+/ =~ id.to_s ? false : true
24
- end
4
+ module ActionController
5
+ module Session
6
+ class MemCacheStore < AbstractStore
7
+ def initialize(app, options = {})
8
+ # Support old :expires option
9
+ options[:expire_after] ||= options[:expires]
25
10
 
26
- # Create a new CGI::Session::MemCache instance
27
- #
28
- # This constructor is used internally by CGI::Session. The
29
- # user does not generally need to call it directly.
30
- #
31
- # +session+ is the session for which this instance is being
32
- # created. The session id must only contain alphanumeric
33
- # characters; automatically generated session ids observe
34
- # this requirement.
35
- #
36
- # +options+ is a hash of options for the initializer. The
37
- # following options are recognized:
38
- #
39
- # cache:: an instance of a MemCache client to use as the
40
- # session cache.
41
- #
42
- # expires:: an expiry time value to use for session entries in
43
- # the session cache. +expires+ is interpreted in seconds
44
- # relative to the current time if it�s less than 60*60*24*30
45
- # (30 days), or as an absolute Unix time (e.g., Time#to_i) if
46
- # greater. If +expires+ is +0+, or not passed on +options+,
47
- # the entry will never expire.
48
- #
49
- # This session's memcache entry will be created if it does
50
- # not exist, or retrieved if it does.
51
- def initialize(session, options = {})
52
- id = session.session_id
53
- unless check_id(id)
54
- raise ArgumentError, "session_id '%s' is invalid" % id
55
- end
56
- @cache = options['cache'] || MemCache.new('localhost')
57
- @expires = options['expires'] || 0
58
- @session_key = "session:#{id}"
59
- @session_data = {}
60
- # Add this key to the store if haven't done so yet
61
- unless @cache.get(@session_key)
62
- @cache.add(@session_key, @session_data, @expires)
63
- end
64
- end
11
+ super
65
12
 
66
- # Restore session state from the session's memcache entry.
67
- #
68
- # Returns the session state as a hash.
69
- def restore
70
- @session_data = @cache[@session_key] || {}
71
- end
13
+ @default_options = {
14
+ :namespace => 'rack:session',
15
+ :memcache_server => 'localhost:11211'
16
+ }.merge(@default_options)
72
17
 
73
- # Save session state to the session's memcache entry.
74
- def update
75
- @cache.set(@session_key, @session_data, @expires)
76
- end
77
-
78
- # Update and close the session's memcache entry.
79
- def close
80
- update
81
- end
18
+ @pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options)
19
+ unless @pool.servers.any? { |s| s.alive? }
20
+ raise "#{self} unable to find server during initialization."
21
+ end
22
+ @mutex = Mutex.new
82
23
 
83
- # Delete the session's memcache entry.
84
- def delete
85
- @cache.delete(@session_key)
86
- @session_data = {}
87
- end
88
-
89
- def data
90
- @session_data
24
+ super
91
25
  end
92
26
 
27
+ private
28
+ def get_session(env, sid)
29
+ sid ||= generate_sid
30
+ begin
31
+ session = @pool.get(sid) || {}
32
+ rescue MemCache::MemCacheError, Errno::ECONNREFUSED
33
+ session = {}
34
+ end
35
+ [sid, session]
36
+ end
37
+
38
+ def set_session(env, sid, session_data)
39
+ options = env['rack.session.options']
40
+ expiry = options[:expire_after] || 0
41
+ @pool.set(sid, session_data, expiry)
42
+ return true
43
+ rescue MemCache::MemCacheError, Errno::ECONNREFUSED
44
+ return false
45
+ end
93
46
  end
94
47
  end
95
48
  end