eactionpack 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. data/CHANGELOG +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +469 -0
  4. data/RUNNING_UNIT_TESTS +24 -0
  5. data/Rakefile +146 -0
  6. data/install.rb +30 -0
  7. data/lib/action_controller.rb +79 -0
  8. data/lib/action_controller/assertions.rb +69 -0
  9. data/lib/action_controller/assertions/dom_assertions.rb +39 -0
  10. data/lib/action_controller/assertions/model_assertions.rb +20 -0
  11. data/lib/action_controller/assertions/response_assertions.rb +172 -0
  12. data/lib/action_controller/assertions/routing_assertions.rb +146 -0
  13. data/lib/action_controller/assertions/selector_assertions.rb +491 -0
  14. data/lib/action_controller/assertions/tag_assertions.rb +130 -0
  15. data/lib/action_controller/base.rb +1288 -0
  16. data/lib/action_controller/benchmarking.rb +94 -0
  17. data/lib/action_controller/caching.rb +72 -0
  18. data/lib/action_controller/caching/actions.rb +144 -0
  19. data/lib/action_controller/caching/fragments.rb +138 -0
  20. data/lib/action_controller/caching/pages.rb +154 -0
  21. data/lib/action_controller/caching/sql_cache.rb +18 -0
  22. data/lib/action_controller/caching/sweeping.rb +97 -0
  23. data/lib/action_controller/cgi_ext.rb +16 -0
  24. data/lib/action_controller/cgi_ext/cookie.rb +110 -0
  25. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  26. data/lib/action_controller/cgi_ext/session.rb +73 -0
  27. data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
  28. data/lib/action_controller/cgi_process.rb +223 -0
  29. data/lib/action_controller/components.rb +166 -0
  30. data/lib/action_controller/cookies.rb +96 -0
  31. data/lib/action_controller/dispatcher.rb +162 -0
  32. data/lib/action_controller/filters.rb +642 -0
  33. data/lib/action_controller/flash.rb +172 -0
  34. data/lib/action_controller/headers.rb +31 -0
  35. data/lib/action_controller/helpers.rb +221 -0
  36. data/lib/action_controller/http_authentication.rb +124 -0
  37. data/lib/action_controller/integration.rb +634 -0
  38. data/lib/action_controller/layout.rb +309 -0
  39. data/lib/action_controller/mime_responds.rb +173 -0
  40. data/lib/action_controller/mime_type.rb +186 -0
  41. data/lib/action_controller/mime_types.rb +20 -0
  42. data/lib/action_controller/polymorphic_routes.rb +191 -0
  43. data/lib/action_controller/record_identifier.rb +102 -0
  44. data/lib/action_controller/request.rb +764 -0
  45. data/lib/action_controller/request_forgery_protection.rb +140 -0
  46. data/lib/action_controller/request_profiler.rb +169 -0
  47. data/lib/action_controller/rescue.rb +258 -0
  48. data/lib/action_controller/resources.rb +572 -0
  49. data/lib/action_controller/response.rb +76 -0
  50. data/lib/action_controller/routing.rb +387 -0
  51. data/lib/action_controller/routing/builder.rb +203 -0
  52. data/lib/action_controller/routing/optimisations.rb +120 -0
  53. data/lib/action_controller/routing/recognition_optimisation.rb +162 -0
  54. data/lib/action_controller/routing/route.rb +240 -0
  55. data/lib/action_controller/routing/route_set.rb +436 -0
  56. data/lib/action_controller/routing/routing_ext.rb +46 -0
  57. data/lib/action_controller/routing/segments.rb +283 -0
  58. data/lib/action_controller/session/active_record_store.rb +340 -0
  59. data/lib/action_controller/session/cookie_store.rb +166 -0
  60. data/lib/action_controller/session/drb_server.rb +32 -0
  61. data/lib/action_controller/session/drb_store.rb +35 -0
  62. data/lib/action_controller/session/mem_cache_store.rb +98 -0
  63. data/lib/action_controller/session_management.rb +158 -0
  64. data/lib/action_controller/status_codes.rb +88 -0
  65. data/lib/action_controller/streaming.rb +155 -0
  66. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  67. data/lib/action_controller/templates/rescues/_trace.erb +26 -0
  68. data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
  69. data/lib/action_controller/templates/rescues/layout.erb +29 -0
  70. data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
  71. data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
  72. data/lib/action_controller/templates/rescues/template_error.erb +21 -0
  73. data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
  74. data/lib/action_controller/test_case.rb +83 -0
  75. data/lib/action_controller/test_process.rb +526 -0
  76. data/lib/action_controller/url_rewriter.rb +142 -0
  77. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  78. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  79. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  80. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  81. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  82. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  83. data/lib/action_controller/verification.rb +130 -0
  84. data/lib/action_pack.rb +24 -0
  85. data/lib/action_pack/version.rb +9 -0
  86. data/lib/action_view.rb +44 -0
  87. data/lib/action_view/base.rb +335 -0
  88. data/lib/action_view/helpers/active_record_helper.rb +276 -0
  89. data/lib/action_view/helpers/asset_tag_helper.rb +599 -0
  90. data/lib/action_view/helpers/atom_feed_helper.rb +143 -0
  91. data/lib/action_view/helpers/benchmark_helper.rb +33 -0
  92. data/lib/action_view/helpers/cache_helper.rb +40 -0
  93. data/lib/action_view/helpers/capture_helper.rb +161 -0
  94. data/lib/action_view/helpers/date_helper.rb +711 -0
  95. data/lib/action_view/helpers/debug_helper.rb +31 -0
  96. data/lib/action_view/helpers/form_helper.rb +767 -0
  97. data/lib/action_view/helpers/form_options_helper.rb +458 -0
  98. data/lib/action_view/helpers/form_tag_helper.rb +458 -0
  99. data/lib/action_view/helpers/javascript_helper.rb +148 -0
  100. data/lib/action_view/helpers/number_helper.rb +186 -0
  101. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  102. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  103. data/lib/action_view/helpers/sanitize_helper.rb +229 -0
  104. data/lib/action_view/helpers/tag_helper.rb +134 -0
  105. data/lib/action_view/helpers/text_helper.rb +507 -0
  106. data/lib/action_view/helpers/url_helper.rb +573 -0
  107. data/lib/action_view/inline_template.rb +20 -0
  108. data/lib/action_view/partial_template.rb +70 -0
  109. data/lib/action_view/partials.rb +158 -0
  110. data/lib/action_view/template.rb +125 -0
  111. data/lib/action_view/template_error.rb +110 -0
  112. data/lib/action_view/template_finder.rb +176 -0
  113. data/lib/action_view/template_handler.rb +34 -0
  114. data/lib/action_view/template_handlers/builder.rb +27 -0
  115. data/lib/action_view/template_handlers/compilable.rb +128 -0
  116. data/lib/action_view/template_handlers/erb.rb +56 -0
  117. data/lib/action_view/test_case.rb +58 -0
  118. data/lib/actionpack.rb +1 -0
  119. data/test/abstract_unit.rb +36 -0
  120. data/test/active_record_unit.rb +105 -0
  121. data/test/activerecord/active_record_store_test.rb +141 -0
  122. data/test/activerecord/render_partial_with_record_identification_test.rb +191 -0
  123. data/test/adv_attr_test.rb +20 -0
  124. data/test/controller/action_pack_assertions_test.rb +543 -0
  125. data/test/controller/addresses_render_test.rb +43 -0
  126. data/test/controller/assert_select_test.rb +331 -0
  127. data/test/controller/base_test.rb +219 -0
  128. data/test/controller/benchmark_test.rb +32 -0
  129. data/test/controller/caching_test.rb +581 -0
  130. data/test/controller/capture_test.rb +89 -0
  131. data/test/controller/cgi_test.rb +116 -0
  132. data/test/controller/components_test.rb +140 -0
  133. data/test/controller/content_type_test.rb +139 -0
  134. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  135. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  136. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  137. data/test/controller/cookie_test.rb +146 -0
  138. data/test/controller/custom_handler_test.rb +45 -0
  139. data/test/controller/deprecation/deprecated_base_methods_test.rb +37 -0
  140. data/test/controller/dispatcher_test.rb +105 -0
  141. data/test/controller/fake_controllers.rb +33 -0
  142. data/test/controller/fake_models.rb +11 -0
  143. data/test/controller/filter_params_test.rb +49 -0
  144. data/test/controller/filters_test.rb +881 -0
  145. data/test/controller/flash_test.rb +146 -0
  146. data/test/controller/header_test.rb +14 -0
  147. data/test/controller/helper_test.rb +210 -0
  148. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  149. data/test/controller/html-scanner/document_test.rb +148 -0
  150. data/test/controller/html-scanner/node_test.rb +89 -0
  151. data/test/controller/html-scanner/sanitizer_test.rb +269 -0
  152. data/test/controller/html-scanner/tag_node_test.rb +238 -0
  153. data/test/controller/html-scanner/text_node_test.rb +50 -0
  154. data/test/controller/html-scanner/tokenizer_test.rb +131 -0
  155. data/test/controller/http_authentication_test.rb +54 -0
  156. data/test/controller/integration_test.rb +252 -0
  157. data/test/controller/integration_upload_test.rb +43 -0
  158. data/test/controller/layout_test.rb +255 -0
  159. data/test/controller/mime_responds_test.rb +514 -0
  160. data/test/controller/mime_type_test.rb +84 -0
  161. data/test/controller/new_render_test.rb +843 -0
  162. data/test/controller/polymorphic_routes_test.rb +174 -0
  163. data/test/controller/record_identifier_test.rb +139 -0
  164. data/test/controller/redirect_test.rb +289 -0
  165. data/test/controller/render_test.rb +484 -0
  166. data/test/controller/request_forgery_protection_test.rb +305 -0
  167. data/test/controller/request_test.rb +928 -0
  168. data/test/controller/rescue_test.rb +517 -0
  169. data/test/controller/resources_test.rb +873 -0
  170. data/test/controller/routing_test.rb +2464 -0
  171. data/test/controller/selector_test.rb +628 -0
  172. data/test/controller/send_file_test.rb +138 -0
  173. data/test/controller/session/cookie_store_test.rb +258 -0
  174. data/test/controller/session/mem_cache_store_test.rb +181 -0
  175. data/test/controller/session_fixation_test.rb +89 -0
  176. data/test/controller/session_management_test.rb +178 -0
  177. data/test/controller/test_test.rb +695 -0
  178. data/test/controller/url_rewriter_test.rb +310 -0
  179. data/test/controller/verification_test.rb +270 -0
  180. data/test/controller/view_paths_test.rb +140 -0
  181. data/test/controller/webservice_test.rb +229 -0
  182. data/test/fixtures/addresses/list.erb +1 -0
  183. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  184. data/test/fixtures/companies.yml +24 -0
  185. data/test/fixtures/company.rb +10 -0
  186. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  187. data/test/fixtures/content_type/render_default_for_js.js.erb +1 -0
  188. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  189. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  190. data/test/fixtures/customers/_customer.html.erb +1 -0
  191. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  192. data/test/fixtures/developer.rb +9 -0
  193. data/test/fixtures/developers.yml +21 -0
  194. data/test/fixtures/developers_projects.yml +13 -0
  195. data/test/fixtures/fun/games/hello_world.erb +1 -0
  196. data/test/fixtures/functional_caching/_partial.erb +3 -0
  197. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  198. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  199. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  200. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  201. data/test/fixtures/helpers/abc_helper.rb +5 -0
  202. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  203. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  204. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  205. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  206. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  207. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  208. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  209. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  210. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  211. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  212. data/test/fixtures/layouts/builder.builder +3 -0
  213. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  214. data/test/fixtures/layouts/standard.erb +1 -0
  215. data/test/fixtures/layouts/talk_from_action.erb +2 -0
  216. data/test/fixtures/layouts/yield.erb +2 -0
  217. data/test/fixtures/mascot.rb +3 -0
  218. data/test/fixtures/mascots.yml +4 -0
  219. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  220. data/test/fixtures/multipart/binary_file +0 -0
  221. data/test/fixtures/multipart/boundary_problem_file +10 -0
  222. data/test/fixtures/multipart/bracketed_param +5 -0
  223. data/test/fixtures/multipart/large_text_file +10 -0
  224. data/test/fixtures/multipart/mixed_files +0 -0
  225. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  226. data/test/fixtures/multipart/single_parameter +5 -0
  227. data/test/fixtures/multipart/text_file +10 -0
  228. data/test/fixtures/override/test/hello_world.erb +1 -0
  229. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  230. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  231. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  232. data/test/fixtures/post_test/post/index.html.erb +1 -0
  233. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  234. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  235. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  236. data/test/fixtures/project.rb +3 -0
  237. data/test/fixtures/projects.yml +7 -0
  238. data/test/fixtures/public/404.html +1 -0
  239. data/test/fixtures/public/500.html +1 -0
  240. data/test/fixtures/public/images/rails.png +0 -0
  241. data/test/fixtures/public/javascripts/application.js +1 -0
  242. data/test/fixtures/public/javascripts/bank.js +1 -0
  243. data/test/fixtures/public/javascripts/robber.js +1 -0
  244. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  245. data/test/fixtures/public/stylesheets/bank.css +1 -0
  246. data/test/fixtures/public/stylesheets/robber.css +1 -0
  247. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  248. data/test/fixtures/replies.yml +15 -0
  249. data/test/fixtures/reply.rb +7 -0
  250. data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
  251. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  252. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  253. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  254. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  255. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  256. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  257. data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
  258. data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
  259. data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
  260. data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
  261. data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
  262. data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
  263. data/test/fixtures/scope/test/modgreet.erb +1 -0
  264. data/test/fixtures/shared.html.erb +1 -0
  265. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  266. data/test/fixtures/test/_customer.erb +1 -0
  267. data/test/fixtures/test/_customer_counter.erb +1 -0
  268. data/test/fixtures/test/_customer_greeting.erb +1 -0
  269. data/test/fixtures/test/_form.erb +1 -0
  270. data/test/fixtures/test/_hash_greeting.erb +1 -0
  271. data/test/fixtures/test/_hash_object.erb +2 -0
  272. data/test/fixtures/test/_hello.builder +1 -0
  273. data/test/fixtures/test/_labelling_form.erb +1 -0
  274. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  275. data/test/fixtures/test/_partial.erb +1 -0
  276. data/test/fixtures/test/_partial.html.erb +1 -0
  277. data/test/fixtures/test/_partial.js.erb +1 -0
  278. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  279. data/test/fixtures/test/_partial_only.erb +1 -0
  280. data/test/fixtures/test/_person.erb +2 -0
  281. data/test/fixtures/test/_raise.html.erb +1 -0
  282. data/test/fixtures/test/action_talk_to_layout.erb +2 -0
  283. data/test/fixtures/test/block_content_for.erb +2 -0
  284. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  285. data/test/fixtures/test/capturing.erb +4 -0
  286. data/test/fixtures/test/content_for.erb +2 -0
  287. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  288. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  289. data/test/fixtures/test/delete_with_js.rjs +2 -0
  290. data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
  291. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  292. data/test/fixtures/test/erb_content_for.erb +2 -0
  293. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  294. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  295. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  296. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  297. data/test/fixtures/test/greeting.erb +1 -0
  298. data/test/fixtures/test/greeting.js.rjs +1 -0
  299. data/test/fixtures/test/hello.builder +4 -0
  300. data/test/fixtures/test/hello_world.erb +1 -0
  301. data/test/fixtures/test/hello_world_container.builder +3 -0
  302. data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
  303. data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
  304. data/test/fixtures/test/hello_xml_world.builder +11 -0
  305. data/test/fixtures/test/list.erb +1 -0
  306. data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
  307. data/test/fixtures/test/potential_conflicts.erb +4 -0
  308. data/test/fixtures/test/render_file_from_template.html.erb +1 -0
  309. data/test/fixtures/test/render_file_with_ivar.erb +1 -0
  310. data/test/fixtures/test/render_file_with_locals.erb +1 -0
  311. data/test/fixtures/test/render_to_string_test.erb +1 -0
  312. data/test/fixtures/test/update_element_with_capture.erb +9 -0
  313. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  314. data/test/fixtures/topic.rb +3 -0
  315. data/test/fixtures/topics.yml +22 -0
  316. data/test/fixtures/topics/_topic.html.erb +1 -0
  317. data/test/template/active_record_helper_test.rb +268 -0
  318. data/test/template/asset_tag_helper_test.rb +514 -0
  319. data/test/template/atom_feed_helper_test.rb +179 -0
  320. data/test/template/benchmark_helper_test.rb +60 -0
  321. data/test/template/date_helper_test.rb +1791 -0
  322. data/test/template/deprecated_erb_variable_test.rb +9 -0
  323. data/test/template/erb_util_test.rb +24 -0
  324. data/test/template/form_helper_test.rb +885 -0
  325. data/test/template/form_options_helper_test.rb +1333 -0
  326. data/test/template/form_tag_helper_test.rb +272 -0
  327. data/test/template/javascript_helper_test.rb +73 -0
  328. data/test/template/number_helper_test.rb +97 -0
  329. data/test/template/record_tag_helper_test.rb +54 -0
  330. data/test/template/sanitize_helper_test.rb +48 -0
  331. data/test/template/tag_helper_test.rb +77 -0
  332. data/test/template/template_finder_test.rb +73 -0
  333. data/test/template/template_object_test.rb +95 -0
  334. data/test/template/test_test.rb +56 -0
  335. data/test/template/text_helper_test.rb +367 -0
  336. data/test/template/url_helper_test.rb +544 -0
  337. data/test/testing_sandbox.rb +15 -0
  338. metadata +469 -0
@@ -0,0 +1,140 @@
1
+ module ActionController #:nodoc:
2
+ class InvalidAuthenticityToken < ActionControllerError #:nodoc:
3
+ end
4
+
5
+ module RequestForgeryProtection
6
+ def self.included(base)
7
+ base.class_eval do
8
+ class_inheritable_accessor :request_forgery_protection_options
9
+ self.request_forgery_protection_options = {}
10
+ helper_method :form_authenticity_token
11
+ helper_method :protect_against_forgery?
12
+ end
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
17
+ # forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
18
+ # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
19
+ # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
20
+ # scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
21
+ #
22
+ # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
23
+ # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
24
+ # production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
25
+ # applications.
26
+ #
27
+ # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
28
+ # use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
29
+ # set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
30
+ # make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
31
+ #
32
+ # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
33
+ #
34
+ # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
35
+ # config/environments/test.rb:
36
+ #
37
+ # # Disable request forgery protection in test environment
38
+ # config.action_controller.allow_forgery_protection = false
39
+ #
40
+ # == Learn more about CSRF (Cross-Site Request Forgery) attacks
41
+ #
42
+ # Here are some resources:
43
+ # * http://isc.sans.org/diary.html?storyid=1750
44
+ # * http://en.wikipedia.org/wiki/Cross-site_request_forgery
45
+ #
46
+ # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
47
+ # There are a few guidelines you should follow:
48
+ #
49
+ # * Keep your GET requests safe and idempotent. More reading material:
50
+ # * http://www.xml.com/pub/a/2002/04/24/deviant.html
51
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
52
+ # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
53
+ #
54
+ module ClassMethods
55
+ # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
56
+ #
57
+ # Example:
58
+ #
59
+ # class FooController < ApplicationController
60
+ # # uses the cookie session store (then you don't need a separate :secret)
61
+ # protect_from_forgery :except => :index
62
+ #
63
+ # # uses one of the other session stores that uses a session_id value.
64
+ # protect_from_forgery :secret => 'my-little-pony', :except => :index
65
+ #
66
+ # # you can disable csrf protection on controller-by-controller basis:
67
+ # skip_before_filter :verify_authenticity_token
68
+ # end
69
+ #
70
+ # Valid Options:
71
+ #
72
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
73
+ # * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
74
+ # Leave this off if you are using the cookie session store.
75
+ # * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'.
76
+ def protect_from_forgery(options = {})
77
+ self.request_forgery_protection_token ||= :authenticity_token
78
+ before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
79
+ request_forgery_protection_options.update(options)
80
+ end
81
+ end
82
+
83
+ protected
84
+ # The actual before_filter that is used. Modify this to change how you handle unverified requests.
85
+ def verify_authenticity_token
86
+ verified_request? || raise(ActionController::InvalidAuthenticityToken)
87
+ end
88
+
89
+ # Returns true or false if a request is verified. Checks:
90
+ #
91
+ # * is the format restricted? By default, only HTML and AJAX requests are checked.
92
+ # * is it a GET request? Gets should be safe and idempotent
93
+ # * Does the form_authenticity_token match the given _token value from the params?
94
+ def verified_request?
95
+ !protect_against_forgery? ||
96
+ request.method == :get ||
97
+ !verifiable_request_format? ||
98
+ form_authenticity_token == params[request_forgery_protection_token]
99
+ end
100
+
101
+ def verifiable_request_format?
102
+ request.content_type.nil? || request.content_type.verify_request?
103
+ end
104
+
105
+ # Sets the token value for the current session. Pass a <tt>:secret</tt> option
106
+ # in +protect_from_forgery+ to add a custom salt to the hash.
107
+ def form_authenticity_token
108
+ @form_authenticity_token ||= if !session.respond_to?(:session_id)
109
+ raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
110
+ elsif request_forgery_protection_options[:secret]
111
+ authenticity_token_from_session_id
112
+ elsif session.respond_to?(:dbman) && session.dbman.respond_to?(:generate_digest)
113
+ authenticity_token_from_cookie_session
114
+ else
115
+ raise InvalidAuthenticityToken, "No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store)."
116
+ end
117
+ end
118
+
119
+ # Generates a unique digest using the session_id and the CSRF secret.
120
+ def authenticity_token_from_session_id
121
+ key = if request_forgery_protection_options[:secret].respond_to?(:call)
122
+ request_forgery_protection_options[:secret].call(@session)
123
+ else
124
+ request_forgery_protection_options[:secret]
125
+ end
126
+ digest = request_forgery_protection_options[:digest] ||= 'SHA1'
127
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), key.to_s, session.session_id.to_s)
128
+ end
129
+
130
+ # No secret was given, so assume this is a cookie session store.
131
+ def authenticity_token_from_cookie_session
132
+ session[:csrf_id] ||= CGI::Session.generate_unique_id
133
+ session.dbman.generate_digest(session[:csrf_id])
134
+ end
135
+
136
+ def protect_against_forgery?
137
+ allow_forgery_protection && request_forgery_protection_token
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,169 @@
1
+ require 'optparse'
2
+ require 'action_controller/integration'
3
+
4
+ module ActionController
5
+ class RequestProfiler
6
+ # Wrap up the integration session runner.
7
+ class Sandbox
8
+ include Integration::Runner
9
+
10
+ def self.benchmark(n, script)
11
+ new(script).benchmark(n)
12
+ end
13
+
14
+ def initialize(script_path)
15
+ @quiet = false
16
+ define_run_method(script_path)
17
+ reset!
18
+ end
19
+
20
+ def benchmark(n, profiling = false)
21
+ @quiet = true
22
+ print ' '
23
+
24
+ result = Benchmark.realtime do
25
+ n.times do |i|
26
+ run(profiling)
27
+ print_progress(i)
28
+ end
29
+ end
30
+
31
+ puts
32
+ result
33
+ ensure
34
+ @quiet = false
35
+ end
36
+
37
+ def say(message)
38
+ puts " #{message}" unless @quiet
39
+ end
40
+
41
+ private
42
+ def define_run_method(script_path)
43
+ script = File.read(script_path)
44
+
45
+ source = <<-end_source
46
+ def run(profiling = false)
47
+ if profiling
48
+ RubyProf.resume do
49
+ #{script}
50
+ end
51
+ else
52
+ #{script}
53
+ end
54
+
55
+ old_request_count = request_count
56
+ reset!
57
+ self.request_count = old_request_count
58
+ end
59
+ end_source
60
+
61
+ instance_eval source, script_path, 1
62
+ end
63
+
64
+ def print_progress(i)
65
+ print "\n " if i % 60 == 0
66
+ print ' ' if i % 10 == 0
67
+ print '.'
68
+ $stdout.flush
69
+ end
70
+ end
71
+
72
+
73
+ attr_reader :options
74
+
75
+ def initialize(options = {})
76
+ @options = default_options.merge(options)
77
+ end
78
+
79
+
80
+ def self.run(args = nil, options = {})
81
+ profiler = new(options)
82
+ profiler.parse_options(args) if args
83
+ profiler.run
84
+ end
85
+
86
+ def run
87
+ sandbox = Sandbox.new(options[:script])
88
+
89
+ puts 'Warming up once'
90
+
91
+ elapsed = warmup(sandbox)
92
+ puts '%.2f sec, %d requests, %d req/sec' % [elapsed, sandbox.request_count, sandbox.request_count / elapsed]
93
+ puts "\n#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x"
94
+
95
+ options[:benchmark] ? benchmark(sandbox) : profile(sandbox)
96
+ end
97
+
98
+ def profile(sandbox)
99
+ load_ruby_prof
100
+
101
+ benchmark(sandbox, true)
102
+ results = RubyProf.stop
103
+
104
+ show_profile_results results
105
+ results
106
+ end
107
+
108
+ def benchmark(sandbox, profiling = false)
109
+ sandbox.request_count = 0
110
+ elapsed = sandbox.benchmark(options[:n], profiling).to_f
111
+ count = sandbox.request_count.to_i
112
+ puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed]
113
+ end
114
+
115
+ def warmup(sandbox)
116
+ Benchmark.realtime { sandbox.run(false) }
117
+ end
118
+
119
+ def default_options
120
+ { :n => 100, :open => 'open %s &' }
121
+ end
122
+
123
+ # Parse command-line options
124
+ def parse_options(args)
125
+ OptionParser.new do |opt|
126
+ opt.banner = "USAGE: #{$0} [options] [session script path]"
127
+
128
+ opt.on('-n', '--times [100]', 'How many requests to process. Defaults to 100.') { |v| options[:n] = v.to_i if v }
129
+ opt.on('-b', '--benchmark', 'Benchmark instead of profiling') { |v| options[:benchmark] = v }
130
+ opt.on('-m', '--measure [mode]', 'Which ruby-prof measure mode to use: process_time, wall_time, cpu_time, allocations, or memory. Defaults to process_time.') { |v| options[:measure] = v }
131
+ opt.on('--open [CMD]', 'Command to open profile results. Defaults to "open %s &"') { |v| options[:open] = v }
132
+ opt.on('-h', '--help', 'Show this help') { puts opt; exit }
133
+
134
+ opt.parse args
135
+
136
+ if args.empty?
137
+ puts opt
138
+ exit
139
+ end
140
+ options[:script] = args.pop
141
+ end
142
+ end
143
+
144
+ protected
145
+ def load_ruby_prof
146
+ begin
147
+ gem 'ruby-prof', '>= 0.6.1'
148
+ require 'ruby-prof'
149
+ if mode = options[:measure]
150
+ RubyProf.measure_mode = RubyProf.const_get(mode.upcase)
151
+ end
152
+ rescue LoadError
153
+ abort '`gem install ruby-prof` to use the profiler'
154
+ end
155
+ end
156
+
157
+ def show_profile_results(results)
158
+ File.open "#{RAILS_ROOT}/tmp/profile-graph.html", 'w' do |file|
159
+ RubyProf::GraphHtmlPrinter.new(results).print(file)
160
+ `#{options[:open] % file.path}` if options[:open]
161
+ end
162
+
163
+ File.open "#{RAILS_ROOT}/tmp/profile-flat.txt", 'w' do |file|
164
+ RubyProf::FlatPrinter.new(results).print(file)
165
+ `#{options[:open] % file.path}` if options[:open]
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,258 @@
1
+ module ActionController #:nodoc:
2
+ # Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view
3
+ # (with a nice user-friendly explanation) or for the developers view (with tons of debugging information). The developers view
4
+ # is already implemented by the Action Controller, but the public view should be tailored to your specific application.
5
+ #
6
+ # The default behavior for public exceptions is to render a static html file with the name of the error code thrown. If no such
7
+ # file exists, an empty response is sent with the correct status code.
8
+ #
9
+ # You can override what constitutes a local request by overriding the <tt>local_request?</tt> method in your own controller.
10
+ # Custom rescue behavior is achieved by overriding the <tt>rescue_action_in_public</tt> and <tt>rescue_action_locally</tt> methods.
11
+ module Rescue
12
+ LOCALHOST = '127.0.0.1'.freeze
13
+
14
+ DEFAULT_RESCUE_RESPONSE = :internal_server_error
15
+ DEFAULT_RESCUE_RESPONSES = {
16
+ 'ActionController::RoutingError' => :not_found,
17
+ 'ActionController::UnknownAction' => :not_found,
18
+ 'ActiveRecord::RecordNotFound' => :not_found,
19
+ 'ActiveRecord::StaleObjectError' => :conflict,
20
+ 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
21
+ 'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
22
+ 'ActionController::MethodNotAllowed' => :method_not_allowed,
23
+ 'ActionController::NotImplemented' => :not_implemented,
24
+ 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
25
+ }
26
+
27
+ DEFAULT_RESCUE_TEMPLATE = 'diagnostics'
28
+ DEFAULT_RESCUE_TEMPLATES = {
29
+ 'ActionView::MissingTemplate' => 'missing_template',
30
+ 'ActionController::RoutingError' => 'routing_error',
31
+ 'ActionController::UnknownAction' => 'unknown_action',
32
+ 'ActionView::TemplateError' => 'template_error'
33
+ }
34
+
35
+ def self.included(base) #:nodoc:
36
+ base.cattr_accessor :rescue_responses
37
+ base.rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE)
38
+ base.rescue_responses.update DEFAULT_RESCUE_RESPONSES
39
+
40
+ base.cattr_accessor :rescue_templates
41
+ base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
42
+ base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
43
+
44
+ base.class_inheritable_array :rescue_handlers
45
+ base.rescue_handlers = []
46
+
47
+ base.extend(ClassMethods)
48
+ base.class_eval do
49
+ alias_method_chain :perform_action, :rescue
50
+ end
51
+ end
52
+
53
+ module ClassMethods
54
+ def process_with_exception(request, response, exception) #:nodoc:
55
+ new.process(request, response, :rescue_action, exception)
56
+ end
57
+
58
+ # Rescue exceptions raised in controller actions.
59
+ #
60
+ # <tt>rescue_from</tt> receives a series of exception classes or class
61
+ # names, and a trailing <tt>:with</tt> option with the name of a method
62
+ # or a Proc object to be called to handle them. Alternatively a block can
63
+ # be given.
64
+ #
65
+ # Handlers that take one argument will be called with the exception, so
66
+ # that the exception can be inspected when dealing with it.
67
+ #
68
+ # Handlers are inherited. They are searched from right to left, from
69
+ # bottom to top, and up the hierarchy. The handler of the first class for
70
+ # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
71
+ # any.
72
+ #
73
+ # class ApplicationController < ActionController::Base
74
+ # rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
75
+ # rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
76
+ #
77
+ # rescue_from 'MyAppError::Base' do |exception|
78
+ # render :xml => exception, :status => 500
79
+ # end
80
+ #
81
+ # protected
82
+ # def deny_access
83
+ # ...
84
+ # end
85
+ #
86
+ # def show_errors(exception)
87
+ # exception.record.new_record? ? ...
88
+ # end
89
+ # end
90
+ def rescue_from(*klasses, &block)
91
+ options = klasses.extract_options!
92
+ unless options.has_key?(:with)
93
+ block_given? ? options[:with] = block : raise(ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument.")
94
+ end
95
+
96
+ klasses.each do |klass|
97
+ key = if klass.is_a?(Class) && klass <= Exception
98
+ klass.name
99
+ elsif klass.is_a?(String)
100
+ klass
101
+ else
102
+ raise(ArgumentError, "#{klass} is neither an Exception nor a String")
103
+ end
104
+
105
+ # Order is important, we put the pair at the end. When dealing with an
106
+ # exception we will follow the documented order going from right to left.
107
+ rescue_handlers << [key, options[:with]]
108
+ end
109
+ end
110
+ end
111
+
112
+ protected
113
+ # Exception handler called when the performance of an action raises an exception.
114
+ def rescue_action(exception)
115
+ log_error(exception) if logger
116
+ erase_results if performed?
117
+
118
+ # Let the exception alter the response if it wants.
119
+ # For example, MethodNotAllowed sets the Allow header.
120
+ if exception.respond_to?(:handle_response!)
121
+ exception.handle_response!(response)
122
+ end
123
+
124
+ if consider_all_requests_local || local_request?
125
+ rescue_action_locally(exception)
126
+ else
127
+ rescue_action_in_public(exception)
128
+ end
129
+ end
130
+
131
+ # Overwrite to implement custom logging of errors. By default logs as fatal.
132
+ def log_error(exception) #:doc:
133
+ ActiveSupport::Deprecation.silence do
134
+ if ActionView::TemplateError === exception
135
+ logger.fatal(exception.to_s)
136
+ else
137
+ logger.fatal(
138
+ "\n\n#{exception.class} (#{exception.message}):\n " +
139
+ clean_backtrace(exception).join("\n ") +
140
+ "\n\n"
141
+ )
142
+ end
143
+ end
144
+ end
145
+
146
+ # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>). By
147
+ # default will call render_optional_error_file. Override this method to provide more user friendly error messages.s
148
+ def rescue_action_in_public(exception) #:doc:
149
+ render_optional_error_file response_code_for_rescue(exception)
150
+ end
151
+
152
+ # Attempts to render a static error page based on the <tt>status_code</tt> thrown,
153
+ # or just return headers if no such file exists. For example, if a 500 error is
154
+ # being handled Rails will first attempt to render the file at <tt>public/500.html</tt>.
155
+ # If the file doesn't exist, the body of the response will be left empty.
156
+ def render_optional_error_file(status_code)
157
+ status = interpret_status(status_code)
158
+ path = "#{Rails.public_path}/#{status[0,3]}.html"
159
+ if File.exist?(path)
160
+ render :file => path, :status => status
161
+ else
162
+ head status
163
+ end
164
+ end
165
+
166
+ # True if the request came from localhost, 127.0.0.1. Override this
167
+ # method if you wish to redefine the meaning of a local request to
168
+ # include remote IP addresses or other criteria.
169
+ def local_request? #:doc:
170
+ request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
171
+ end
172
+
173
+ # Render detailed diagnostics for unhandled exceptions rescued from
174
+ # a controller action.
175
+ def rescue_action_locally(exception)
176
+ add_variables_to_assigns
177
+ @template.instance_variable_set("@exception", exception)
178
+ @template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
179
+ @template.send!(:assign_variables_from_controller)
180
+
181
+ @template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false))
182
+
183
+ response.content_type = Mime::HTML
184
+ render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
185
+ end
186
+
187
+ # Tries to rescue the exception by looking up and calling a registered handler.
188
+ def rescue_action_with_handler(exception)
189
+ if handler = handler_for_rescue(exception)
190
+ if handler.arity != 0
191
+ handler.call(exception)
192
+ else
193
+ handler.call
194
+ end
195
+ true # don't rely on the return value of the handler
196
+ end
197
+ end
198
+
199
+ private
200
+ def perform_action_with_rescue #:nodoc:
201
+ perform_action_without_rescue
202
+ rescue Exception => exception
203
+ rescue_action_with_handler(exception) || rescue_action(exception)
204
+ end
205
+
206
+ def rescues_path(template_name)
207
+ "#{File.dirname(__FILE__)}/templates/rescues/#{template_name}.erb"
208
+ end
209
+
210
+ def template_path_for_local_rescue(exception)
211
+ rescues_path(rescue_templates[exception.class.name])
212
+ end
213
+
214
+ def response_code_for_rescue(exception)
215
+ rescue_responses[exception.class.name]
216
+ end
217
+
218
+ def handler_for_rescue(exception)
219
+ # We go from right to left because pairs are pushed onto rescue_handlers
220
+ # as rescue_from declarations are found.
221
+ _, handler = *rescue_handlers.reverse.detect do |klass_name, handler|
222
+ # The purpose of allowing strings in rescue_from is to support the
223
+ # declaration of handler associations for exception classes whose
224
+ # definition is yet unknown.
225
+ #
226
+ # Since this loop needs the constants it would be inconsistent to
227
+ # assume they should exist at this point. An early raised exception
228
+ # could trigger some other handler and the array could include
229
+ # precisely a string whose corresponding constant has not yet been
230
+ # seen. This is why we are tolerant to unknown constants.
231
+ #
232
+ # Note that this tolerance only matters if the exception was given as
233
+ # a string, otherwise a NameError will be raised by the interpreter
234
+ # itself when rescue_from CONSTANT is executed.
235
+ klass = self.class.const_get(klass_name) rescue nil
236
+ klass ||= klass_name.constantize rescue nil
237
+ exception.is_a?(klass) if klass
238
+ end
239
+
240
+ case handler
241
+ when Symbol
242
+ method(handler)
243
+ when Proc
244
+ handler.bind(self)
245
+ end
246
+ end
247
+
248
+ def clean_backtrace(exception)
249
+ if backtrace = exception.backtrace
250
+ if defined?(RAILS_ROOT)
251
+ backtrace.map { |line| line.sub RAILS_ROOT, '' }
252
+ else
253
+ backtrace
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end