roda 3.28.0 → 3.29.0

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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +10 -0
  3. data/README.rdoc +10 -0
  4. data/doc/release_notes/3.29.0.txt +15 -0
  5. data/lib/roda.rb +1 -0
  6. data/lib/roda/plugins/caching.rb +2 -0
  7. data/lib/roda/plugins/common_logger.rb +1 -1
  8. data/lib/roda/plugins/exception_page.rb +7 -1
  9. data/lib/roda/plugins/indifferent_params.rb +2 -0
  10. data/lib/roda/version.rb +1 -1
  11. metadata +4 -214
  12. data/Rakefile +0 -108
  13. data/doc/release_notes/1.0.0.txt +0 -329
  14. data/doc/release_notes/1.1.0.txt +0 -226
  15. data/doc/release_notes/1.2.0.txt +0 -406
  16. data/doc/release_notes/1.3.0.txt +0 -109
  17. data/doc/release_notes/2.0.0.txt +0 -75
  18. data/doc/release_notes/2.1.0.txt +0 -124
  19. data/doc/release_notes/2.10.0.txt +0 -27
  20. data/doc/release_notes/2.11.0.txt +0 -70
  21. data/doc/release_notes/2.12.0.txt +0 -40
  22. data/doc/release_notes/2.13.0.txt +0 -10
  23. data/doc/release_notes/2.14.0.txt +0 -44
  24. data/doc/release_notes/2.15.0.txt +0 -53
  25. data/doc/release_notes/2.16.0.txt +0 -48
  26. data/doc/release_notes/2.17.0.txt +0 -62
  27. data/doc/release_notes/2.18.0.txt +0 -69
  28. data/doc/release_notes/2.19.0.txt +0 -30
  29. data/doc/release_notes/2.2.0.txt +0 -97
  30. data/doc/release_notes/2.20.0.txt +0 -5
  31. data/doc/release_notes/2.21.0.txt +0 -17
  32. data/doc/release_notes/2.22.0.txt +0 -41
  33. data/doc/release_notes/2.23.0.txt +0 -29
  34. data/doc/release_notes/2.24.0.txt +0 -65
  35. data/doc/release_notes/2.25.0.txt +0 -14
  36. data/doc/release_notes/2.26.0.txt +0 -13
  37. data/doc/release_notes/2.27.0.txt +0 -56
  38. data/doc/release_notes/2.28.0.txt +0 -17
  39. data/doc/release_notes/2.29.0.txt +0 -156
  40. data/doc/release_notes/2.3.0.txt +0 -109
  41. data/doc/release_notes/2.4.0.txt +0 -55
  42. data/doc/release_notes/2.5.0.txt +0 -23
  43. data/doc/release_notes/2.5.1.txt +0 -4
  44. data/doc/release_notes/2.6.0.txt +0 -21
  45. data/doc/release_notes/2.7.0.txt +0 -75
  46. data/doc/release_notes/2.8.0.txt +0 -44
  47. data/doc/release_notes/2.9.0.txt +0 -6
  48. data/spec/all.rb +0 -1
  49. data/spec/assets/css/app.scss +0 -1
  50. data/spec/assets/css/no_access.css +0 -1
  51. data/spec/assets/css/raw.css +0 -1
  52. data/spec/assets/js/head/app.js +0 -1
  53. data/spec/composition_spec.rb +0 -31
  54. data/spec/define_roda_method_spec.rb +0 -274
  55. data/spec/env_spec.rb +0 -11
  56. data/spec/freeze_spec.rb +0 -37
  57. data/spec/integration_spec.rb +0 -209
  58. data/spec/matchers_spec.rb +0 -832
  59. data/spec/opts_spec.rb +0 -42
  60. data/spec/plugin/_after_hook_spec.rb +0 -19
  61. data/spec/plugin/all_verbs_spec.rb +0 -29
  62. data/spec/plugin/assets_preloading_spec.rb +0 -98
  63. data/spec/plugin/assets_spec.rb +0 -745
  64. data/spec/plugin/backtracking_array_spec.rb +0 -42
  65. data/spec/plugin/branch_locals_spec.rb +0 -106
  66. data/spec/plugin/caching_spec.rb +0 -337
  67. data/spec/plugin/chunked_spec.rb +0 -201
  68. data/spec/plugin/class_level_routing_spec.rb +0 -164
  69. data/spec/plugin/class_matchers_spec.rb +0 -40
  70. data/spec/plugin/common_logger_spec.rb +0 -85
  71. data/spec/plugin/content_for_spec.rb +0 -162
  72. data/spec/plugin/content_security_policy_spec.rb +0 -175
  73. data/spec/plugin/cookies_spec.rb +0 -51
  74. data/spec/plugin/csrf_spec.rb +0 -111
  75. data/spec/plugin/default_headers_spec.rb +0 -82
  76. data/spec/plugin/default_status_spec.rb +0 -95
  77. data/spec/plugin/delay_build_spec.rb +0 -23
  78. data/spec/plugin/delegate_spec.rb +0 -23
  79. data/spec/plugin/delete_empty_headers_spec.rb +0 -27
  80. data/spec/plugin/direct_call_spec.rb +0 -28
  81. data/spec/plugin/disallow_file_uploads_spec.rb +0 -25
  82. data/spec/plugin/drop_body_spec.rb +0 -24
  83. data/spec/plugin/early_hints_spec.rb +0 -19
  84. data/spec/plugin/empty_root_spec.rb +0 -14
  85. data/spec/plugin/environments_spec.rb +0 -42
  86. data/spec/plugin/error_email_spec.rb +0 -97
  87. data/spec/plugin/error_handler_spec.rb +0 -216
  88. data/spec/plugin/error_mail_spec.rb +0 -93
  89. data/spec/plugin/exception_page_spec.rb +0 -168
  90. data/spec/plugin/flash_spec.rb +0 -121
  91. data/spec/plugin/h_spec.rb +0 -11
  92. data/spec/plugin/halt_spec.rb +0 -119
  93. data/spec/plugin/hash_matcher_spec.rb +0 -27
  94. data/spec/plugin/hash_routes_spec.rb +0 -535
  95. data/spec/plugin/head_spec.rb +0 -52
  96. data/spec/plugin/header_matchers_spec.rb +0 -98
  97. data/spec/plugin/heartbeat_spec.rb +0 -74
  98. data/spec/plugin/hooks_spec.rb +0 -152
  99. data/spec/plugin/indifferent_params_spec.rb +0 -14
  100. data/spec/plugin/json_parser_spec.rb +0 -141
  101. data/spec/plugin/json_spec.rb +0 -83
  102. data/spec/plugin/mail_processor_spec.rb +0 -451
  103. data/spec/plugin/mailer_spec.rb +0 -282
  104. data/spec/plugin/match_affix_spec.rb +0 -43
  105. data/spec/plugin/match_hook_spec.rb +0 -79
  106. data/spec/plugin/middleware_spec.rb +0 -237
  107. data/spec/plugin/middleware_stack_spec.rb +0 -81
  108. data/spec/plugin/module_include_spec.rb +0 -48
  109. data/spec/plugin/multi_route_spec.rb +0 -268
  110. data/spec/plugin/multi_run_spec.rb +0 -87
  111. data/spec/plugin/multi_view_spec.rb +0 -50
  112. data/spec/plugin/multibyte_string_matcher_spec.rb +0 -44
  113. data/spec/plugin/named_templates_spec.rb +0 -96
  114. data/spec/plugin/not_allowed_spec.rb +0 -69
  115. data/spec/plugin/not_found_spec.rb +0 -128
  116. data/spec/plugin/optimized_string_matchers_spec.rb +0 -43
  117. data/spec/plugin/padrino_render_spec.rb +0 -34
  118. data/spec/plugin/param_matchers_spec.rb +0 -69
  119. data/spec/plugin/params_capturing_spec.rb +0 -33
  120. data/spec/plugin/partials_spec.rb +0 -43
  121. data/spec/plugin/pass_spec.rb +0 -29
  122. data/spec/plugin/path_matchers_spec.rb +0 -42
  123. data/spec/plugin/path_rewriter_spec.rb +0 -45
  124. data/spec/plugin/path_spec.rb +0 -222
  125. data/spec/plugin/placeholder_string_matchers_spec.rb +0 -126
  126. data/spec/plugin/precompile_templates_spec.rb +0 -61
  127. data/spec/plugin/public_spec.rb +0 -85
  128. data/spec/plugin/render_each_spec.rb +0 -82
  129. data/spec/plugin/render_locals_spec.rb +0 -114
  130. data/spec/plugin/render_spec.rb +0 -912
  131. data/spec/plugin/request_aref_spec.rb +0 -51
  132. data/spec/plugin/request_headers_spec.rb +0 -39
  133. data/spec/plugin/response_request_spec.rb +0 -43
  134. data/spec/plugin/route_block_args_spec.rb +0 -86
  135. data/spec/plugin/route_csrf_spec.rb +0 -305
  136. data/spec/plugin/run_append_slash_spec.rb +0 -77
  137. data/spec/plugin/run_handler_spec.rb +0 -53
  138. data/spec/plugin/sessions_spec.rb +0 -452
  139. data/spec/plugin/shared_vars_spec.rb +0 -45
  140. data/spec/plugin/sinatra_helpers_spec.rb +0 -537
  141. data/spec/plugin/slash_path_empty_spec.rb +0 -22
  142. data/spec/plugin/static_routing_spec.rb +0 -192
  143. data/spec/plugin/static_spec.rb +0 -30
  144. data/spec/plugin/status_303_spec.rb +0 -28
  145. data/spec/plugin/status_handler_spec.rb +0 -158
  146. data/spec/plugin/streaming_spec.rb +0 -246
  147. data/spec/plugin/strip_path_prefix_spec.rb +0 -24
  148. data/spec/plugin/symbol_matchers_spec.rb +0 -51
  149. data/spec/plugin/symbol_status_spec.rb +0 -25
  150. data/spec/plugin/symbol_views_spec.rb +0 -32
  151. data/spec/plugin/timestamp_public_spec.rb +0 -85
  152. data/spec/plugin/type_routing_spec.rb +0 -348
  153. data/spec/plugin/typecast_params_spec.rb +0 -1370
  154. data/spec/plugin/unescape_path_spec.rb +0 -22
  155. data/spec/plugin/view_options_spec.rb +0 -170
  156. data/spec/plugin_spec.rb +0 -71
  157. data/spec/redirect_spec.rb +0 -41
  158. data/spec/request_spec.rb +0 -97
  159. data/spec/response_spec.rb +0 -199
  160. data/spec/route_spec.rb +0 -39
  161. data/spec/session_middleware_spec.rb +0 -129
  162. data/spec/session_spec.rb +0 -37
  163. data/spec/spec_helper.rb +0 -137
  164. data/spec/version_spec.rb +0 -14
  165. data/spec/views/_test.erb +0 -1
  166. data/spec/views/a.erb +0 -1
  167. data/spec/views/a.rdoc +0 -2
  168. data/spec/views/about.erb +0 -1
  169. data/spec/views/about.str +0 -1
  170. data/spec/views/about/_test.css.gz +0 -0
  171. data/spec/views/about/_test.erb +0 -1
  172. data/spec/views/about/_test.erb.gz +0 -0
  173. data/spec/views/about/comp_test.erb +0 -1
  174. data/spec/views/b.erb +0 -1
  175. data/spec/views/c.erb +0 -1
  176. data/spec/views/comp_layout.erb +0 -1
  177. data/spec/views/comp_test.erb +0 -1
  178. data/spec/views/content-yield.erb +0 -1
  179. data/spec/views/each.str +0 -1
  180. data/spec/views/home.erb +0 -2
  181. data/spec/views/home.str +0 -2
  182. data/spec/views/iv.erb +0 -1
  183. data/spec/views/layout-alternative.erb +0 -2
  184. data/spec/views/layout-yield.erb +0 -3
  185. data/spec/views/layout.erb +0 -2
  186. data/spec/views/layout.str +0 -2
  187. data/spec/views/multiple-layout.erb +0 -1
  188. data/spec/views/multiple.erb +0 -1
@@ -1,51 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- describe "request_aref plugin" do
4
- def request_aref_app(value)
5
- warning = @warning = String.new('')
6
- app(:bare) do
7
- plugin :request_aref, value
8
- self::RodaRequest.send(:define_method, :warn){|s| warning.replace(s)}
9
- route do |r|
10
- r.get('set'){r['b'] = 'c'; r.params['b']}
11
- r['a']
12
- end
13
- end
14
- end
15
-
16
- def aref_body
17
- body("QUERY_STRING" => 'a=d', 'rack.input'=>StringIO.new)
18
- end
19
-
20
- def aset_body
21
- body('/set', "QUERY_STRING" => 'a=d', 'rack.input'=>StringIO.new)
22
- end
23
-
24
- it "allows if given the :allow option" do
25
- request_aref_app(:allow)
26
- aref_body.must_equal 'd'
27
- @warning.must_equal ''
28
- aset_body.must_equal 'c'
29
- @warning.must_equal ''
30
- end
31
-
32
- it "warns if given the :warn option" do
33
- request_aref_app(:warn)
34
- aref_body.must_equal 'd'
35
- @warning.must_include('#[] is deprecated, use #params.[] instead')
36
- aset_body.must_equal 'c'
37
- @warning.must_include('#[]= is deprecated, use #params.[]= instead')
38
- end
39
-
40
- it "raises if given the :raise option" do
41
- request_aref_app(:raise)
42
- proc{aref_body}.must_raise Roda::RodaPlugins::RequestAref::Error
43
- @warning.must_equal ''
44
- proc{aset_body}.must_raise Roda::RodaPlugins::RequestAref::Error
45
- @warning.must_equal ''
46
- end
47
-
48
- it "raises when loading plugin if given other option" do
49
- proc{request_aref_app(:r)}.must_raise Roda::RodaError
50
- end
51
- end
@@ -1,39 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- describe "request_headers plugin" do
4
- def header_app(header_name)
5
- app(:bare) do
6
- plugin :request_headers
7
- route do |r|
8
- r.on do
9
- # return the value of the request header in the response body,
10
- # or the static string 'not found' if it hasn't been supplied.
11
- r.headers[header_name] || 'not found'
12
- end
13
- end
14
- end
15
- end
16
-
17
- it "must add HTTP_ prefix when appropriate" do
18
- header_app('Foo')
19
- body('/', {'HTTP_FOO' => 'a'}).must_equal 'a'
20
- end
21
-
22
- it "must ignore HTTP_ prefix when appropriate" do
23
- header_app('Content-Type')
24
- body('/', {'CONTENT_TYPE' => 'a'}).must_equal 'a'
25
- end
26
-
27
- it "must return nil for non-existant headers" do
28
- header_app('X-Non-Existant')
29
- body('/').must_equal 'not found'
30
- end
31
-
32
- it "must be case-insensitive" do
33
- header_app('X-My-Header')
34
- body('/', {'HTTP_X_MY_HEADER' => 'a'}).must_equal 'a'
35
-
36
- header_app('x-my-header')
37
- body('/', {'HTTP_X_MY_HEADER' => 'a'}).must_equal 'a'
38
- end
39
- end
@@ -1,43 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- describe "response_request plugin" do
4
- it "gives the response access to the request" do
5
- app(:response_request) do
6
- response.request.post? ? "b" : "a"
7
- end
8
-
9
- body.must_equal "a"
10
- body('REQUEST_METHOD'=>'POST').must_equal "b"
11
- end
12
-
13
- it "should work with error_handler plugin" do
14
- app(:bare) do
15
- plugin :response_request
16
-
17
- plugin :error_handler do |_|
18
- response.request.post? ? "b" : "a"
19
- end
20
-
21
- route{raise}
22
- end
23
-
24
- body.must_equal "a"
25
- body('REQUEST_METHOD'=>'POST').must_equal "b"
26
- end
27
-
28
- it "should work with class_level_routing plugin" do
29
- app(:bare) do
30
- plugin :response_request
31
- plugin :class_level_routing
32
-
33
- is '' do |_|
34
- response.request.post? ? "b" : "a"
35
- end
36
-
37
- route{}
38
- end
39
-
40
- body.must_equal "a"
41
- body('REQUEST_METHOD'=>'POST').must_equal "b"
42
- end
43
- end
@@ -1,86 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- describe "route_block_args plugin" do
4
- it "works with hooks when loaded last" do
5
- a = []
6
- app(:bare) do
7
- plugin :hooks
8
- before { a << 1 }
9
- after { a << 2 }
10
- plugin :route_block_args do
11
- [request, response]
12
- end
13
- route do |req, res|
14
- response.status = 401
15
- a << req.path << res.status
16
- "1"
17
- end
18
- end
19
- body.must_equal "1"
20
- a.must_equal [1, '/', 401, 2]
21
- end
22
-
23
- it "works with hooks when loaded first" do
24
- a = []
25
- app(:bare) do
26
- plugin :route_block_args do
27
- [request, response]
28
- end
29
- plugin :hooks
30
- before { a << 1 }
31
- after { a << 2 }
32
- route do |req, res|
33
- response.status = 401
34
- a << req.path << res.status
35
- "1"
36
- end
37
- end
38
- body.must_equal "1"
39
- a.must_equal [1, '/', 401, 2]
40
- end
41
-
42
- it "still supports a single route block argument" do
43
- app(:bare) do
44
- plugin :route_block_args do
45
- request
46
- end
47
- route { |r| "OK" }
48
- end
49
-
50
- status('/').must_equal(200)
51
- end
52
-
53
- it "supports many route block arguments" do
54
- app(:bare) do
55
- plugin :route_block_args do
56
- [request.params, request.env, response.headers, response.body]
57
- end
58
- route do |p, e, h, b|
59
- h['Foo'] = 'Bar'
60
- b << "#{p['a']}-#{e['B']}"
61
- "x"
62
- end
63
- end
64
-
65
- header('Foo', 'rack.input'=>StringIO.new).must_equal('Bar')
66
- body('rack.input'=>StringIO.new).must_equal('-')
67
- body('QUERY_STRING'=>'a=c', 'B'=>'D', 'rack.input'=>StringIO.new).must_equal('c-D')
68
- end
69
-
70
- it "works if given after the route block" do
71
- app(:bare) do
72
- route do |p, e, h, b|
73
- h['Foo'] = 'Bar'
74
- b << "#{p['a']}-#{e['B']}"
75
- "x"
76
- end
77
- plugin :route_block_args do
78
- [request.params, request.env, response.headers, response.body]
79
- end
80
- end
81
-
82
- header('Foo', 'rack.input'=>StringIO.new).must_equal('Bar')
83
- body('rack.input'=>StringIO.new).must_equal('-')
84
- body('QUERY_STRING'=>'a=c', 'B'=>'D', 'rack.input'=>StringIO.new).must_equal('c-D')
85
- end
86
- end
@@ -1,305 +0,0 @@
1
- require_relative "../spec_helper"
2
-
3
- describe "route_csrf plugin" do
4
- include CookieJar
5
-
6
- def route_csrf_app(opts={}, &block)
7
- app(:bare) do
8
- send(*DEFAULT_SESSION_ARGS) unless opts[:no_sessions_plugin]
9
- plugin(:route_csrf, opts, &opts[:block])
10
- route do |r|
11
- check_csrf! unless env['SKIP']
12
- r.post('foo'){'f'}
13
- r.post('bar'){'b'}
14
- r.get "token", String do |s|
15
- csrf_token("/#{s}")
16
- end
17
- instance_exec(r, &block) if block
18
- end
19
- end
20
- end
21
-
22
- it "allows all GET requests and allows POST requests only if they have a correct token for the path" do
23
- route_csrf_app
24
- token = body("/token/foo")
25
- token.length.must_equal 84
26
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
27
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
28
- proc{body("/foo", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
29
-
30
- token = body("/token/bar")
31
- token.length.must_equal 84
32
- body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'b'
33
- proc{body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
34
- proc{body("/bar", "REQUEST_METHOD"=>'DELETE', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
35
-
36
- # Additional failure cases
37
-
38
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new)}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
39
-
40
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}a"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
41
-
42
- t2 = token.dup
43
- t2.setbyte(1, t2.getbyte(1) ^ 1)
44
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(t2)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
45
-
46
- t2 = token.dup
47
- t2.setbyte(61, t2.getbyte(61) ^ 1)
48
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(t2)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
49
-
50
- t2 = token.dup
51
- t2[1] = '|'
52
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(t2)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
53
- end
54
-
55
- it "supports :require_request_specific_tokens => false option to allow non-request-specific tokens" do
56
- route_csrf_app(:require_request_specific_tokens=>false){csrf_token}
57
- token = body("/token/foo")
58
- token.length.must_equal 84
59
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
60
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
61
-
62
- token = body
63
- token.length.must_equal 84
64
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
65
- body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'b'
66
- end
67
-
68
- it "allows tokens submitted in both parameter and HTTP header if :check_header option is true" do
69
- route_csrf_app(:check_header=>true)
70
- token = body("/token/foo")
71
- token.length.must_equal 84
72
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
73
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new, 'HTTP_X_CSRF_TOKEN'=>token).must_equal 'f'
74
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
75
- proc{body("/foo", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'HTTP_X_CSRF_TOKEN'=>token)}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
76
- end
77
-
78
- it "allows tokens submitted in only HTTP header if :check_header option is :only" do
79
- route_csrf_app(:check_header=>:only)
80
- token = body("/token/foo")
81
- token.length.must_equal 84
82
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new, 'HTTP_X_CSRF_TOKEN'=>token).must_equal 'f'
83
- proc{body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
84
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
85
- proc{body("/foo", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'HTTP_X_CSRF_TOKEN'=>token)}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
86
- end
87
-
88
- it "allows configuring CSRF failure action with :csrf_failure => :empty_403 option" do
89
- route_csrf_app(:csrf_failure=>:empty_403)
90
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/foo"))}")).must_equal 'f'
91
- req("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new).must_equal [403, {'Content-Type'=>'text/html', 'Content-Length'=>'0'}, []]
92
- end
93
-
94
- it "allows configuring CSRF failure action with :csrf_failure => :empty_403 option" do
95
- route_csrf_app(:csrf_failure=>:clear_session){session.inspect}
96
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/foo"))}")).must_equal 'f'
97
- body("/b", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/token/a'))}")).must_equal '{}'
98
- end
99
-
100
- it "allows configuring CSRF failure action with :csrf_failure => proc option" do
101
- route_csrf_app(:csrf_failure=>proc{|r| r.path + '2'})
102
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/foo"))}")).must_equal 'f'
103
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new).must_equal '/foo2'
104
- end
105
-
106
- it "allows configuring CSRF failure action via a plugin block" do
107
- route_csrf_app(:block=>proc{|r| r.path + '2'})
108
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/foo"))}")).must_equal 'f'
109
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new).must_equal '/foo2'
110
- end
111
-
112
- it "allows plugin block to integrate with route_block_args plugin" do
113
- app(:bare) do
114
- send(*DEFAULT_SESSION_ARGS)
115
- plugin :route_block_args do
116
- [request, request.path, response]
117
- end
118
- plugin(:route_csrf){|r, path, res| res.write(path); res.write('2')}
119
- route do |r|
120
- check_csrf!
121
- r.post('foo'){'f'}
122
- r.get "token", String do |s|
123
- csrf_token("/#{s}")
124
- end
125
- end
126
- end
127
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/foo"))}")).must_equal 'f'
128
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new).must_equal '/foo2'
129
- end
130
-
131
- it "raises Error if configuring plugin with invalid :csrf_failure option" do
132
- route_csrf_app(:csrf_failure=>:foo)
133
- proc{body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new)}.must_raise Roda::RodaError
134
- end
135
-
136
- it "raises Error if configuring plugin with block and :csrf_failure option" do
137
- proc{route_csrf_app(:block=>proc{|r| r.path + '2'}, :csrf_failure=>:raise)}.must_raise Roda::RodaError
138
- end
139
-
140
- deprecated "supports check_csrf! :csrf_failure option as a Proc" do
141
- pr = proc{env['BAD'] == '1' ? 't' : 'f'}
142
- route_csrf_app{check_csrf!(:csrf_failure=>pr); ''}
143
- token = body("/token/foo")
144
- body("SKIP"=>"1", "BAD"=>'1', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 't'
145
- body("SKIP"=>"1", "BAD"=>'0', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
146
- end
147
-
148
- it "supports valid_csrf? method" do
149
- route_csrf_app{valid_csrf?.to_s}
150
- body("/a", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/a"))}")).must_equal 'true'
151
- body("/a", "REQUEST_METHOD"=>'POST', 'SKIP'=>true, 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/b"))}")).must_equal 'false'
152
- end
153
-
154
- it "supports valid_csrf? method" do
155
- route_csrf_app do
156
- check_csrf!{'nope'}
157
- 'yep'
158
- end
159
- body("/a", "REQUEST_METHOD"=>'POST', 'SKIP'=>true, 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/a"))}")).must_equal 'yep'
160
- body("/a", "REQUEST_METHOD"=>'POST', 'SKIP'=>true, 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body("/token/b"))}")).must_equal 'nope'
161
- end
162
-
163
- it "supports use_request_specific_csrf_tokens? method" do
164
- route_csrf_app{use_request_specific_csrf_tokens?.to_s}
165
- body.must_equal 'true'
166
- route_csrf_app(:require_request_specific_tokens=>false){use_request_specific_csrf_tokens?.to_s}
167
- body.must_equal 'false'
168
- end
169
-
170
- it "supports csrf_field method" do
171
- route_csrf_app{csrf_field}
172
- body.must_equal '_csrf'
173
- route_csrf_app(:field=>'foo'){csrf_field}
174
- body.must_equal 'foo'
175
- end
176
-
177
- it "supports csrf_header method" do
178
- route_csrf_app{csrf_header}
179
- body.must_equal 'X-CSRF-Token'
180
- route_csrf_app(:header=>'Foo'){csrf_header}
181
- body.must_equal 'Foo'
182
- end
183
-
184
- it "supports csrf_metatag method" do
185
- route_csrf_app(:require_request_specific_tokens=>false){csrf_metatag}
186
- body =~ /\A<meta name="_csrf" content="([+\/0-9A-Za-z]{84})" \/>\z/
187
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape($1)}")).must_equal 'f'
188
-
189
- route_csrf_app(:require_request_specific_tokens=>false, :field=>'foo'){csrf_metatag}
190
- body =~ /\A<meta name="foo" content="([+\/0-9A-Za-z]{84})" \/>\z/
191
- body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("foo=#{Rack::Utils.escape($1)}")).must_equal 'b'
192
- end
193
-
194
- it "supports csrf_tag method" do
195
- route_csrf_app(:require_request_specific_tokens=>false){csrf_tag}
196
- body =~ /\A<input type="hidden" name="_csrf" value="([+\/0-9A-Za-z]{84})" \/>\z/
197
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape($1)}")).must_equal 'f'
198
-
199
- route_csrf_app(:require_request_specific_tokens=>false, :field=>'foo'){csrf_tag}
200
- body =~ /\A<input type="hidden" name="foo" value="([+\/0-9A-Za-z]{84})" \/>\z/
201
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("foo=#{Rack::Utils.escape($1)}")).must_equal 'f'
202
-
203
- route_csrf_app{csrf_tag('/foo')}
204
- body =~ /\A<input type="hidden" name="_csrf" value="([+\/0-9A-Za-z]{84})" \/>\z/
205
- token = $1
206
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
207
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
208
- proc{body("/foo", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'QUERY_STRING'=>"_csrf=#{Rack::Utils.escape(token)}")}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
209
-
210
- route_csrf_app do |r|
211
- r.is 'foo', :method=>'PUT' do
212
- 'f2'
213
- end
214
- csrf_tag('/foo', 'PUT')
215
- end
216
- body =~ /\A<input type="hidden" name="_csrf" value="([+\/0-9A-Za-z]{84})" \/>\z/
217
- token = $1
218
- body("/foo", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'QUERY_STRING'=>"_csrf=#{Rack::Utils.escape(token)}").must_equal 'f2'
219
- proc{body("/bar", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'QUERY_STRING'=>"_csrf=#{Rack::Utils.escape(token)}")}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
220
- proc{body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
221
- end
222
-
223
- it "supports csrf_tag method" do
224
- route_csrf_app(:require_request_specific_tokens=>false){csrf_token}
225
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body)}")).must_equal 'f'
226
-
227
- route_csrf_app(:require_request_specific_tokens=>false, :field=>'foo'){csrf_token}
228
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("foo=#{Rack::Utils.escape(body)}")).must_equal 'f'
229
-
230
- route_csrf_app{csrf_token('/foo')}
231
- token = body
232
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
233
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
234
- proc{body("/foo", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'QUERY_STRING'=>"_csrf=#{Rack::Utils.escape(token)}")}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
235
-
236
- route_csrf_app do |r|
237
- r.is 'foo', :method=>'PUT' do
238
- 'f2'
239
- end
240
- csrf_token('/foo', 'PUT')
241
- end
242
- token = body
243
- body("/foo", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'QUERY_STRING'=>"_csrf=#{Rack::Utils.escape(token)}").must_equal 'f2'
244
- proc{body("/bar", "REQUEST_METHOD"=>'PUT', 'rack.input'=>StringIO.new, 'QUERY_STRING'=>"_csrf=#{Rack::Utils.escape(token)}")}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
245
- proc{body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
246
- end
247
-
248
- it "supports csrf_path method" do
249
- route_csrf_app do |r|
250
- r.post{r.path + '2'}
251
- csrf_token(csrf_path(env['CP']))
252
- end
253
-
254
- body("REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>nil))}")).must_equal '/2'
255
- body("REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>''))}")).must_equal '/2'
256
- body("REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'#foo'))}")).must_equal '/2'
257
- body("REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'?foo'))}")).must_equal '/2'
258
-
259
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/a', 'CP'=>nil))}")).must_equal '/a2'
260
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/a', 'CP'=>''))}")).must_equal '/a2'
261
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/a', 'CP'=>'?foo'))}")).must_equal '/a2'
262
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/a', 'CP'=>'#foo'))}")).must_equal '/a2'
263
-
264
- body("REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'http://foo/'))}")).must_equal '/2'
265
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'https://foo/a'))}")).must_equal '/a2'
266
- body('/a/b', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'http://foo/a/b'))}")).must_equal '/a/b2'
267
-
268
- body("REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'/'))}")).must_equal '/2'
269
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'/a'))}")).must_equal '/a2'
270
- body('/a/b', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('CP'=>'/a/b'))}")).must_equal '/a/b2'
271
-
272
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('HTTPS'=>'on', 'HTTP_HOST'=>'foo.com', 'CP'=>'a'))}")).must_equal '/a2'
273
- body('/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/b', 'HTTPS'=>'on', 'HTTP_HOST'=>'foo.com', 'CP'=>'a'))}")).must_equal '/a2'
274
- body('/b/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/b/', 'HTTPS'=>'on', 'HTTP_HOST'=>'foo.com', 'CP'=>'a'))}")).must_equal '/b/a2'
275
- body('/b/a', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/b/b', 'HTTPS'=>'on', 'HTTP_HOST'=>'foo.com', 'CP'=>'a'))}")).must_equal '/b/a2'
276
- body('/a/b', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/b', 'HTTPS'=>'on', 'HTTP_HOST'=>'foo.com', 'CP'=>'a/b'))}")).must_equal '/a/b2'
277
- body('/b/a/b', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/b/', 'HTTPS'=>'on', 'HTTP_HOST'=>'foo.com', 'CP'=>'a/b'))}")).must_equal '/b/a/b2'
278
- body('/b/a/b', "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(body('/b/a', 'HTTPS'=>'on', 'HTTP_HOST'=>'foo.com', 'CP'=>'a/b'))}")).must_equal '/b/a/b2'
279
- end
280
-
281
- begin
282
- require 'rack/csrf'
283
- rescue LoadError
284
- warn "rack_csrf not installed, skipping route_csrf plugin test for rack_csrf upgrade"
285
- else
286
- it "supports upgrades from existing rack_csrf token" do
287
- route_csrf_app(:upgrade_from_rack_csrf_key=>'csrf.token', :no_sessions_plugin=>true) do |r|
288
- r.get 'clear' do
289
- session.clear
290
- ''
291
- end
292
- Rack::Csrf.token(env)
293
- end
294
- app.use(*DEFAULT_SESSION_MIDDLEWARE_ARGS)
295
- app.use Rack::Csrf, :skip=>['POST:/foo', 'POST:/bar'], :raise=>true
296
- token = body
297
- token.length.wont_equal 84
298
- body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'f'
299
- body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}")).must_equal 'b'
300
- body('/clear').must_equal ''
301
- proc{body("/foo", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
302
- proc{body("/bar", "REQUEST_METHOD"=>'POST', 'rack.input'=>StringIO.new("_csrf=#{Rack::Utils.escape(token)}"))}.must_raise Roda::RodaPlugins::RouteCsrf::InvalidToken
303
- end
304
- end
305
- end