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.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/README.rdoc +10 -0
- data/doc/release_notes/3.29.0.txt +15 -0
- data/lib/roda.rb +1 -0
- data/lib/roda/plugins/caching.rb +2 -0
- data/lib/roda/plugins/common_logger.rb +1 -1
- data/lib/roda/plugins/exception_page.rb +7 -1
- data/lib/roda/plugins/indifferent_params.rb +2 -0
- data/lib/roda/version.rb +1 -1
- metadata +4 -214
- data/Rakefile +0 -108
- data/doc/release_notes/1.0.0.txt +0 -329
- data/doc/release_notes/1.1.0.txt +0 -226
- data/doc/release_notes/1.2.0.txt +0 -406
- data/doc/release_notes/1.3.0.txt +0 -109
- data/doc/release_notes/2.0.0.txt +0 -75
- data/doc/release_notes/2.1.0.txt +0 -124
- data/doc/release_notes/2.10.0.txt +0 -27
- data/doc/release_notes/2.11.0.txt +0 -70
- data/doc/release_notes/2.12.0.txt +0 -40
- data/doc/release_notes/2.13.0.txt +0 -10
- data/doc/release_notes/2.14.0.txt +0 -44
- data/doc/release_notes/2.15.0.txt +0 -53
- data/doc/release_notes/2.16.0.txt +0 -48
- data/doc/release_notes/2.17.0.txt +0 -62
- data/doc/release_notes/2.18.0.txt +0 -69
- data/doc/release_notes/2.19.0.txt +0 -30
- data/doc/release_notes/2.2.0.txt +0 -97
- data/doc/release_notes/2.20.0.txt +0 -5
- data/doc/release_notes/2.21.0.txt +0 -17
- data/doc/release_notes/2.22.0.txt +0 -41
- data/doc/release_notes/2.23.0.txt +0 -29
- data/doc/release_notes/2.24.0.txt +0 -65
- data/doc/release_notes/2.25.0.txt +0 -14
- data/doc/release_notes/2.26.0.txt +0 -13
- data/doc/release_notes/2.27.0.txt +0 -56
- data/doc/release_notes/2.28.0.txt +0 -17
- data/doc/release_notes/2.29.0.txt +0 -156
- data/doc/release_notes/2.3.0.txt +0 -109
- data/doc/release_notes/2.4.0.txt +0 -55
- data/doc/release_notes/2.5.0.txt +0 -23
- data/doc/release_notes/2.5.1.txt +0 -4
- data/doc/release_notes/2.6.0.txt +0 -21
- data/doc/release_notes/2.7.0.txt +0 -75
- data/doc/release_notes/2.8.0.txt +0 -44
- data/doc/release_notes/2.9.0.txt +0 -6
- data/spec/all.rb +0 -1
- data/spec/assets/css/app.scss +0 -1
- data/spec/assets/css/no_access.css +0 -1
- data/spec/assets/css/raw.css +0 -1
- data/spec/assets/js/head/app.js +0 -1
- data/spec/composition_spec.rb +0 -31
- data/spec/define_roda_method_spec.rb +0 -274
- data/spec/env_spec.rb +0 -11
- data/spec/freeze_spec.rb +0 -37
- data/spec/integration_spec.rb +0 -209
- data/spec/matchers_spec.rb +0 -832
- data/spec/opts_spec.rb +0 -42
- data/spec/plugin/_after_hook_spec.rb +0 -19
- data/spec/plugin/all_verbs_spec.rb +0 -29
- data/spec/plugin/assets_preloading_spec.rb +0 -98
- data/spec/plugin/assets_spec.rb +0 -745
- data/spec/plugin/backtracking_array_spec.rb +0 -42
- data/spec/plugin/branch_locals_spec.rb +0 -106
- data/spec/plugin/caching_spec.rb +0 -337
- data/spec/plugin/chunked_spec.rb +0 -201
- data/spec/plugin/class_level_routing_spec.rb +0 -164
- data/spec/plugin/class_matchers_spec.rb +0 -40
- data/spec/plugin/common_logger_spec.rb +0 -85
- data/spec/plugin/content_for_spec.rb +0 -162
- data/spec/plugin/content_security_policy_spec.rb +0 -175
- data/spec/plugin/cookies_spec.rb +0 -51
- data/spec/plugin/csrf_spec.rb +0 -111
- data/spec/plugin/default_headers_spec.rb +0 -82
- data/spec/plugin/default_status_spec.rb +0 -95
- data/spec/plugin/delay_build_spec.rb +0 -23
- data/spec/plugin/delegate_spec.rb +0 -23
- data/spec/plugin/delete_empty_headers_spec.rb +0 -27
- data/spec/plugin/direct_call_spec.rb +0 -28
- data/spec/plugin/disallow_file_uploads_spec.rb +0 -25
- data/spec/plugin/drop_body_spec.rb +0 -24
- data/spec/plugin/early_hints_spec.rb +0 -19
- data/spec/plugin/empty_root_spec.rb +0 -14
- data/spec/plugin/environments_spec.rb +0 -42
- data/spec/plugin/error_email_spec.rb +0 -97
- data/spec/plugin/error_handler_spec.rb +0 -216
- data/spec/plugin/error_mail_spec.rb +0 -93
- data/spec/plugin/exception_page_spec.rb +0 -168
- data/spec/plugin/flash_spec.rb +0 -121
- data/spec/plugin/h_spec.rb +0 -11
- data/spec/plugin/halt_spec.rb +0 -119
- data/spec/plugin/hash_matcher_spec.rb +0 -27
- data/spec/plugin/hash_routes_spec.rb +0 -535
- data/spec/plugin/head_spec.rb +0 -52
- data/spec/plugin/header_matchers_spec.rb +0 -98
- data/spec/plugin/heartbeat_spec.rb +0 -74
- data/spec/plugin/hooks_spec.rb +0 -152
- data/spec/plugin/indifferent_params_spec.rb +0 -14
- data/spec/plugin/json_parser_spec.rb +0 -141
- data/spec/plugin/json_spec.rb +0 -83
- data/spec/plugin/mail_processor_spec.rb +0 -451
- data/spec/plugin/mailer_spec.rb +0 -282
- data/spec/plugin/match_affix_spec.rb +0 -43
- data/spec/plugin/match_hook_spec.rb +0 -79
- data/spec/plugin/middleware_spec.rb +0 -237
- data/spec/plugin/middleware_stack_spec.rb +0 -81
- data/spec/plugin/module_include_spec.rb +0 -48
- data/spec/plugin/multi_route_spec.rb +0 -268
- data/spec/plugin/multi_run_spec.rb +0 -87
- data/spec/plugin/multi_view_spec.rb +0 -50
- data/spec/plugin/multibyte_string_matcher_spec.rb +0 -44
- data/spec/plugin/named_templates_spec.rb +0 -96
- data/spec/plugin/not_allowed_spec.rb +0 -69
- data/spec/plugin/not_found_spec.rb +0 -128
- data/spec/plugin/optimized_string_matchers_spec.rb +0 -43
- data/spec/plugin/padrino_render_spec.rb +0 -34
- data/spec/plugin/param_matchers_spec.rb +0 -69
- data/spec/plugin/params_capturing_spec.rb +0 -33
- data/spec/plugin/partials_spec.rb +0 -43
- data/spec/plugin/pass_spec.rb +0 -29
- data/spec/plugin/path_matchers_spec.rb +0 -42
- data/spec/plugin/path_rewriter_spec.rb +0 -45
- data/spec/plugin/path_spec.rb +0 -222
- data/spec/plugin/placeholder_string_matchers_spec.rb +0 -126
- data/spec/plugin/precompile_templates_spec.rb +0 -61
- data/spec/plugin/public_spec.rb +0 -85
- data/spec/plugin/render_each_spec.rb +0 -82
- data/spec/plugin/render_locals_spec.rb +0 -114
- data/spec/plugin/render_spec.rb +0 -912
- data/spec/plugin/request_aref_spec.rb +0 -51
- data/spec/plugin/request_headers_spec.rb +0 -39
- data/spec/plugin/response_request_spec.rb +0 -43
- data/spec/plugin/route_block_args_spec.rb +0 -86
- data/spec/plugin/route_csrf_spec.rb +0 -305
- data/spec/plugin/run_append_slash_spec.rb +0 -77
- data/spec/plugin/run_handler_spec.rb +0 -53
- data/spec/plugin/sessions_spec.rb +0 -452
- data/spec/plugin/shared_vars_spec.rb +0 -45
- data/spec/plugin/sinatra_helpers_spec.rb +0 -537
- data/spec/plugin/slash_path_empty_spec.rb +0 -22
- data/spec/plugin/static_routing_spec.rb +0 -192
- data/spec/plugin/static_spec.rb +0 -30
- data/spec/plugin/status_303_spec.rb +0 -28
- data/spec/plugin/status_handler_spec.rb +0 -158
- data/spec/plugin/streaming_spec.rb +0 -246
- data/spec/plugin/strip_path_prefix_spec.rb +0 -24
- data/spec/plugin/symbol_matchers_spec.rb +0 -51
- data/spec/plugin/symbol_status_spec.rb +0 -25
- data/spec/plugin/symbol_views_spec.rb +0 -32
- data/spec/plugin/timestamp_public_spec.rb +0 -85
- data/spec/plugin/type_routing_spec.rb +0 -348
- data/spec/plugin/typecast_params_spec.rb +0 -1370
- data/spec/plugin/unescape_path_spec.rb +0 -22
- data/spec/plugin/view_options_spec.rb +0 -170
- data/spec/plugin_spec.rb +0 -71
- data/spec/redirect_spec.rb +0 -41
- data/spec/request_spec.rb +0 -97
- data/spec/response_spec.rb +0 -199
- data/spec/route_spec.rb +0 -39
- data/spec/session_middleware_spec.rb +0 -129
- data/spec/session_spec.rb +0 -37
- data/spec/spec_helper.rb +0 -137
- data/spec/version_spec.rb +0 -14
- data/spec/views/_test.erb +0 -1
- data/spec/views/a.erb +0 -1
- data/spec/views/a.rdoc +0 -2
- data/spec/views/about.erb +0 -1
- data/spec/views/about.str +0 -1
- data/spec/views/about/_test.css.gz +0 -0
- data/spec/views/about/_test.erb +0 -1
- data/spec/views/about/_test.erb.gz +0 -0
- data/spec/views/about/comp_test.erb +0 -1
- data/spec/views/b.erb +0 -1
- data/spec/views/c.erb +0 -1
- data/spec/views/comp_layout.erb +0 -1
- data/spec/views/comp_test.erb +0 -1
- data/spec/views/content-yield.erb +0 -1
- data/spec/views/each.str +0 -1
- data/spec/views/home.erb +0 -2
- data/spec/views/home.str +0 -2
- data/spec/views/iv.erb +0 -1
- data/spec/views/layout-alternative.erb +0 -2
- data/spec/views/layout-yield.erb +0 -3
- data/spec/views/layout.erb +0 -2
- data/spec/views/layout.str +0 -2
- data/spec/views/multiple-layout.erb +0 -1
- data/spec/views/multiple.erb +0 -1
@@ -1,77 +0,0 @@
|
|
1
|
-
require_relative "../spec_helper"
|
2
|
-
|
3
|
-
describe "run_append_slash plugin" do
|
4
|
-
before do
|
5
|
-
sub2 = app do |r|
|
6
|
-
r.root do
|
7
|
-
'sub-bar-root'
|
8
|
-
end
|
9
|
-
|
10
|
-
r.get 'baz' do
|
11
|
-
'sub-bar-baz'
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
sub1 = app(:run_append_slash) do |r|
|
16
|
-
r.root do
|
17
|
-
'sub-root'
|
18
|
-
end
|
19
|
-
|
20
|
-
r.get 'foo' do
|
21
|
-
'sub-foo'
|
22
|
-
end
|
23
|
-
|
24
|
-
r.on 'bar' do
|
25
|
-
r.run sub2
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
app(:bare) do
|
30
|
-
route do |r|
|
31
|
-
r.root do
|
32
|
-
'root'
|
33
|
-
end
|
34
|
-
|
35
|
-
r.on 'sub' do
|
36
|
-
r.run sub1
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
it "internally appends a missing trailing slash to #run sub apps" do
|
43
|
-
# Without append slash
|
44
|
-
body.must_equal 'root'
|
45
|
-
status('/sub').must_equal 404
|
46
|
-
body('/sub/').must_equal 'sub-root'
|
47
|
-
body('/sub/foo').must_equal 'sub-foo'
|
48
|
-
status('/sub/foo/').must_equal 404
|
49
|
-
body('/sub/bar/').must_equal 'sub-bar-root'
|
50
|
-
body('/sub/bar/baz').must_equal 'sub-bar-baz'
|
51
|
-
status('/sub/bar/baz/').must_equal 404
|
52
|
-
|
53
|
-
# With append slash
|
54
|
-
app.plugin :run_append_slash
|
55
|
-
body('/sub').must_equal 'sub-root'
|
56
|
-
body('/sub/').must_equal 'sub-root'
|
57
|
-
body('/sub/foo').must_equal 'sub-foo'
|
58
|
-
status('/sub/foo/').must_equal 404
|
59
|
-
body('/sub/bar').must_equal 'sub-bar-root'
|
60
|
-
body('/sub/bar/').must_equal 'sub-bar-root'
|
61
|
-
body('/sub/bar/baz').must_equal 'sub-bar-baz'
|
62
|
-
status('/sub/bar/baz/').must_equal 404
|
63
|
-
end
|
64
|
-
|
65
|
-
it "redirects #run sub apps when trailing slash is missing" do
|
66
|
-
app.plugin :run_append_slash, :use_redirects => true
|
67
|
-
status('/sub').must_equal 302
|
68
|
-
header('Location', '/sub').must_equal '/sub/'
|
69
|
-
body('/sub/').must_equal 'sub-root'
|
70
|
-
body('/sub/foo').must_equal 'sub-foo'
|
71
|
-
status('/sub/foo/').must_equal 404
|
72
|
-
body('/sub/bar').must_equal 'sub-bar-root'
|
73
|
-
body('/sub/bar/').must_equal 'sub-bar-root'
|
74
|
-
body('/sub/bar/baz').must_equal 'sub-bar-baz'
|
75
|
-
status('/sub/bar/baz/').must_equal 404
|
76
|
-
end
|
77
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
require_relative "../spec_helper"
|
2
|
-
|
3
|
-
describe "run_handler plugin" do
|
4
|
-
it "makes r.run :not_found=>:pass keep going on 404" do
|
5
|
-
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
|
6
|
-
app(:run_handler) do |r|
|
7
|
-
r.run pr, :not_found=>:pass
|
8
|
-
'a'
|
9
|
-
end
|
10
|
-
|
11
|
-
status.must_equal 201
|
12
|
-
body.must_equal 'b'
|
13
|
-
status('/a').must_equal 200
|
14
|
-
body('/a').must_equal 'a'
|
15
|
-
end
|
16
|
-
|
17
|
-
it "makes r.run with a block yield rack app to block, and have it be thrown afterward" do
|
18
|
-
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
|
19
|
-
app(:run_handler) do |r|
|
20
|
-
r.run(pr){|a| a[0] *= 2}
|
21
|
-
'a'
|
22
|
-
end
|
23
|
-
|
24
|
-
status.must_equal 402
|
25
|
-
status('/a').must_equal 808
|
26
|
-
end
|
27
|
-
|
28
|
-
it "works when both :not_found=>:pass and block are given" do
|
29
|
-
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 202 : 201), {}, ['b']]}
|
30
|
-
app(:run_handler) do |r|
|
31
|
-
r.run(pr, :not_found=>:pass){|a| a[0] *= 2}
|
32
|
-
'a'
|
33
|
-
end
|
34
|
-
|
35
|
-
status.must_equal 402
|
36
|
-
body.must_equal 'b'
|
37
|
-
status('/a').must_equal 200
|
38
|
-
body('/a').must_equal 'a'
|
39
|
-
end
|
40
|
-
|
41
|
-
it "makes r.run work normally if not given an option or block" do
|
42
|
-
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
|
43
|
-
app(:run_handler) do |r|
|
44
|
-
r.run pr
|
45
|
-
'a'
|
46
|
-
end
|
47
|
-
|
48
|
-
status.must_equal 201
|
49
|
-
body.must_equal 'b'
|
50
|
-
status('/a').must_equal 404
|
51
|
-
body('/a').must_equal 'b'
|
52
|
-
end
|
53
|
-
end
|
@@ -1,452 +0,0 @@
|
|
1
|
-
require_relative "../spec_helper"
|
2
|
-
|
3
|
-
if RUBY_VERSION >= '2'
|
4
|
-
[true, false].each do |per_cookie_cipher_secret|
|
5
|
-
describe "sessions plugin with per_cookie_cipher_secret: #{per_cookie_cipher_secret}" do
|
6
|
-
include CookieJar
|
7
|
-
|
8
|
-
def req(path, opts={})
|
9
|
-
@errors ||= (errors = []; def errors.puts(s) self << s; end; errors)
|
10
|
-
super(path, opts.merge('rack.errors'=>@errors))
|
11
|
-
end
|
12
|
-
|
13
|
-
def errors
|
14
|
-
e = @errors.dup
|
15
|
-
@errors.clear
|
16
|
-
e
|
17
|
-
end
|
18
|
-
|
19
|
-
before do
|
20
|
-
app(:bare) do
|
21
|
-
plugin :sessions, :secret=>'1'*64, :per_cookie_cipher_secret=>per_cookie_cipher_secret
|
22
|
-
route do |r|
|
23
|
-
if r.GET['sut']
|
24
|
-
session
|
25
|
-
env['roda.session.updated_at'] -= r.GET['sut'].to_i if r.GET['sut']
|
26
|
-
end
|
27
|
-
r.get('s', String, String){|k, v| session[k] = v}
|
28
|
-
r.get('g', String){|k| session[k].to_s}
|
29
|
-
r.get('sct'){|i| session; env['roda.session.created_at'].to_s}
|
30
|
-
r.get('ssct', Integer){|i| session; (env['roda.session.created_at'] -= i).to_s}
|
31
|
-
r.get('ssct2', Integer, String, String){|i, k, v| session[k] = v; (env['roda.session.created_at'] -= i).to_s}
|
32
|
-
r.get('sc'){session.clear; 'c'}
|
33
|
-
r.get('cs', String, String){|k, v| clear_session; session[k] = v}
|
34
|
-
r.get('cat'){t = r.session_created_at; t.strftime("%F") if t}
|
35
|
-
r.get('uat'){t = r.session_updated_at; t.strftime("%F") if t}
|
36
|
-
''
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
it "requires appropriate :secret option" do
|
42
|
-
proc{app(:bare){plugin :sessions}}.must_raise Roda::RodaError
|
43
|
-
proc{app(:bare){plugin :sessions, :secret=>Object.new}}.must_raise Roda::RodaError
|
44
|
-
proc{app(:bare){plugin :sessions, :secret=>'1'*63}}.must_raise Roda::RodaError
|
45
|
-
end
|
46
|
-
|
47
|
-
it "has session store data between requests" do
|
48
|
-
req('/').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"0"}, [""]]
|
49
|
-
body('/s/foo/bar').must_equal 'bar'
|
50
|
-
body('/g/foo').must_equal 'bar'
|
51
|
-
|
52
|
-
body('/s/foo/baz').must_equal 'baz'
|
53
|
-
body('/g/foo').must_equal 'baz'
|
54
|
-
|
55
|
-
body("/s/foo/\u1234").must_equal "\u1234"
|
56
|
-
body("/g/foo").must_equal "\u1234"
|
57
|
-
|
58
|
-
errors.must_equal []
|
59
|
-
end
|
60
|
-
|
61
|
-
it "supports loading sessions created when per_cookie_cipher_secret: #{!per_cookie_cipher_secret} " do
|
62
|
-
req('/').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"0"}, [""]]
|
63
|
-
body('/s/foo/bar').must_equal 'bar'
|
64
|
-
body('/g/foo').must_equal 'bar'
|
65
|
-
|
66
|
-
app.plugin :sessions, :per_cookie_cipher_secret=>!per_cookie_cipher_secret
|
67
|
-
|
68
|
-
body('/s/foo/baz').must_equal 'baz'
|
69
|
-
body('/g/foo').must_equal 'baz'
|
70
|
-
|
71
|
-
errors.must_equal []
|
72
|
-
end
|
73
|
-
|
74
|
-
it "allows accessing session creation and last update times" do
|
75
|
-
status('/cat').must_equal 404
|
76
|
-
status('/uat').must_equal 404
|
77
|
-
status('/s/foo/bar').must_equal 200
|
78
|
-
body('/cat').must_equal Date.today.strftime("%F")
|
79
|
-
body('/uat').must_equal Date.today.strftime("%F")
|
80
|
-
status('/ssct2/172800/bar/baz').must_equal 200
|
81
|
-
body('/cat').must_equal((Date.today - 2).strftime("%F"))
|
82
|
-
end
|
83
|
-
|
84
|
-
it "does not add Set-Cookie header if session does not change, unless outside :skip_within seconds" do
|
85
|
-
req('/').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"0"}, [""]]
|
86
|
-
_, h, b = req('/s/foo/bar')
|
87
|
-
h['Set-Cookie'].must_match(/\Aroda.session/)
|
88
|
-
b.must_equal ["bar"]
|
89
|
-
req('/g/foo').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["bar"]]
|
90
|
-
req('/s/foo/bar').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["bar"]]
|
91
|
-
|
92
|
-
_, h, b = req('/s/foo/baz')
|
93
|
-
h['Set-Cookie'].must_match(/\Aroda.session/)
|
94
|
-
b.must_equal ["baz"]
|
95
|
-
req('/g/foo').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["baz"]]
|
96
|
-
|
97
|
-
req('/g/foo', 'QUERY_STRING'=>'sut=3500').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["baz"]]
|
98
|
-
_, h, b = req('/g/foo', 'QUERY_STRING'=>'sut=3700')
|
99
|
-
h['Set-Cookie'].must_match(/\Aroda.session/)
|
100
|
-
b.must_equal ["baz"]
|
101
|
-
|
102
|
-
@app.plugin(:sessions, :skip_within=>3800)
|
103
|
-
req('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal [200, {"Content-Type"=>"text/html", "Content-Length"=>"3"}, ["baz"]]
|
104
|
-
_, h, b = req('/g/foo', 'QUERY_STRING'=>'sut=3900')
|
105
|
-
h['Set-Cookie'].must_match(/\Aroda.session/)
|
106
|
-
b.must_equal ["baz"]
|
107
|
-
|
108
|
-
errors.must_equal []
|
109
|
-
end
|
110
|
-
|
111
|
-
it "removes session cookie when session is submitted but empty after request" do
|
112
|
-
body('/s/foo/bar').must_equal 'bar'
|
113
|
-
body('/sct').to_i
|
114
|
-
body('/g/foo').must_equal 'bar'
|
115
|
-
|
116
|
-
_, h, b = req('/sc')
|
117
|
-
|
118
|
-
# Parameters can come in any order, and only the final parameter may omit the ;
|
119
|
-
['roda.session=', 'max-age=0', 'path=/'].each do |param|
|
120
|
-
h['Set-Cookie'].must_match(/#{Regexp.escape(param)}(;|\z)/)
|
121
|
-
end
|
122
|
-
h['Set-Cookie'].must_match(/expires=Thu, 01 Jan 1970 00:00:00 (-0000|GMT)(;|\z)/)
|
123
|
-
|
124
|
-
b.must_equal ['c']
|
125
|
-
|
126
|
-
errors.must_equal []
|
127
|
-
end
|
128
|
-
|
129
|
-
it "removes session cookie even when max-age and expires are in cookie options" do
|
130
|
-
app.plugin :sessions, :cookie_options=>{:max_age=>'1000', :expires=>Time.now+1000}
|
131
|
-
body('/s/foo/bar').must_equal 'bar'
|
132
|
-
body('/sct').to_i
|
133
|
-
body('/g/foo').must_equal 'bar'
|
134
|
-
|
135
|
-
_, h, b = req('/sc')
|
136
|
-
|
137
|
-
# Parameters can come in any order, and only the final parameter may omit the ;
|
138
|
-
['roda.session=', 'max-age=0', 'path=/'].each do |param|
|
139
|
-
h['Set-Cookie'].must_match(/#{Regexp.escape(param)}(;|\z)/)
|
140
|
-
end
|
141
|
-
h['Set-Cookie'].must_match(/expires=Thu, 01 Jan 1970 00:00:00 (-0000|GMT)(;|\z)/)
|
142
|
-
|
143
|
-
b.must_equal ['c']
|
144
|
-
|
145
|
-
errors.must_equal []
|
146
|
-
end
|
147
|
-
|
148
|
-
it "sets new session create time when clear_session is called even when session is not empty when serializing" do
|
149
|
-
body('/s/foo/bar').must_equal 'bar'
|
150
|
-
sct = body('/sct').to_i
|
151
|
-
body('/g/foo').must_equal 'bar'
|
152
|
-
body('/sct').to_i.must_equal sct
|
153
|
-
body('/ssct/10').to_i.must_equal(sct - 10)
|
154
|
-
|
155
|
-
body('/cs/foo/baz').must_equal 'baz'
|
156
|
-
body('/sct').to_i.must_be :>=, sct
|
157
|
-
|
158
|
-
errors.must_equal []
|
159
|
-
end
|
160
|
-
|
161
|
-
it "should include HttpOnly and secure cookie options appropriately" do
|
162
|
-
h = header('Set-Cookie', '/s/foo/bar')
|
163
|
-
h.must_include('; HttpOnly')
|
164
|
-
h.wont_include('; secure')
|
165
|
-
|
166
|
-
h = header('Set-Cookie', '/s/foo/baz', 'HTTPS'=>'on')
|
167
|
-
h.must_include('; HttpOnly')
|
168
|
-
h.must_include('; secure')
|
169
|
-
|
170
|
-
@app.plugin(:sessions, :cookie_options=>{})
|
171
|
-
h = header('Set-Cookie', '/s/foo/bar')
|
172
|
-
h.must_include('; HttpOnly')
|
173
|
-
h.wont_include('; secure')
|
174
|
-
end
|
175
|
-
|
176
|
-
it "should merge :cookie_options options into the default cookie options" do
|
177
|
-
@app.plugin(:sessions, :cookie_options=>{:secure=>true})
|
178
|
-
h = header('Set-Cookie', '/s/foo/bar')
|
179
|
-
h.must_include('; HttpOnly')
|
180
|
-
h.must_include('; path=/')
|
181
|
-
h.must_include('; secure')
|
182
|
-
end
|
183
|
-
|
184
|
-
it "handles secret rotation using :old_secret option" do
|
185
|
-
body('/s/foo/bar').must_equal 'bar'
|
186
|
-
body('/g/foo').must_equal 'bar'
|
187
|
-
|
188
|
-
old_cookie = @cookie
|
189
|
-
@app.plugin(:sessions, :secret=>'2'*64, :old_secret=>'1'*64)
|
190
|
-
body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
|
191
|
-
|
192
|
-
@app.plugin(:sessions, :secret=>'2'*64, :old_secret=>nil)
|
193
|
-
body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
|
194
|
-
|
195
|
-
@cookie = old_cookie
|
196
|
-
body('/g/foo').must_equal ''
|
197
|
-
errors.must_equal ["Not decoding session: HMAC invalid"]
|
198
|
-
|
199
|
-
proc{app(:bare){plugin :sessions, :old_secret=>'1'*63}}.must_raise Roda::RodaError
|
200
|
-
proc{app(:bare){plugin :sessions, :old_secret=>Object.new}}.must_raise Roda::RodaError
|
201
|
-
end
|
202
|
-
|
203
|
-
it "handles secret rotation using :old_secret option when also changing :per_cookie_cipher_secret option" do
|
204
|
-
body('/s/foo/bar').must_equal 'bar'
|
205
|
-
body('/g/foo').must_equal 'bar'
|
206
|
-
|
207
|
-
old_cookie = @cookie
|
208
|
-
@app.plugin(:sessions, :secret=>'2'*64, :old_secret=>'1'*64, :per_cookie_cipher_secret=>!per_cookie_cipher_secret)
|
209
|
-
|
210
|
-
body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
|
211
|
-
|
212
|
-
@app.plugin(:sessions, :secret=>'2'*64, :old_secret=>nil)
|
213
|
-
body('/g/foo', 'QUERY_STRING'=>'sut=3700').must_equal 'bar'
|
214
|
-
|
215
|
-
@cookie = old_cookie
|
216
|
-
body('/g/foo').must_equal ''
|
217
|
-
errors.must_equal ["Not decoding session: HMAC invalid"]
|
218
|
-
|
219
|
-
proc{app(:bare){plugin :sessions, :old_secret=>'1'*63}}.must_raise Roda::RodaError
|
220
|
-
proc{app(:bare){plugin :sessions, :old_secret=>Object.new}}.must_raise Roda::RodaError
|
221
|
-
end
|
222
|
-
|
223
|
-
it "pads data by default to make it more difficult to guess session contents based on size" do
|
224
|
-
long = "bar"*35
|
225
|
-
|
226
|
-
_, h1, b = req('/s/foo/bar')
|
227
|
-
b.must_equal ['bar']
|
228
|
-
_, h2, b = req('/s/foo/bar', 'QUERY_STRING'=>'sut=3700')
|
229
|
-
b.must_equal ['bar']
|
230
|
-
_, h3, b = req('/s/foo/bar2')
|
231
|
-
b.must_equal ['bar2']
|
232
|
-
_, h4, b = req("/s/foo/#{long}")
|
233
|
-
b.must_equal [long]
|
234
|
-
h1['Set-Cookie'].length.must_equal h2['Set-Cookie'].length
|
235
|
-
h1['Set-Cookie'].wont_equal h2['Set-Cookie']
|
236
|
-
h1['Set-Cookie'].length.must_equal h3['Set-Cookie'].length
|
237
|
-
h1['Set-Cookie'].wont_equal h3['Set-Cookie']
|
238
|
-
h1['Set-Cookie'].length.wont_equal h4['Set-Cookie'].length
|
239
|
-
|
240
|
-
@app.plugin(:sessions, :pad_size=>256)
|
241
|
-
|
242
|
-
_, h1, b = req('/s/foo/bar')
|
243
|
-
b.must_equal ['bar']
|
244
|
-
_, h2, b = req('/s/foo/bar', 'QUERY_STRING'=>'sut=3700')
|
245
|
-
b.must_equal ['bar']
|
246
|
-
_, h3, b = req('/s/foo/bar2')
|
247
|
-
b.must_equal ['bar2']
|
248
|
-
_, h4, b = req("/s/foo/#{long}")
|
249
|
-
b.must_equal [long]
|
250
|
-
h1['Set-Cookie'].length.must_equal h2['Set-Cookie'].length
|
251
|
-
h1['Set-Cookie'].wont_equal h2['Set-Cookie']
|
252
|
-
h1['Set-Cookie'].length.must_equal h3['Set-Cookie'].length
|
253
|
-
h1['Set-Cookie'].wont_equal h3['Set-Cookie']
|
254
|
-
h1['Set-Cookie'].length.must_equal h4['Set-Cookie'].length
|
255
|
-
h1['Set-Cookie'].wont_equal h3['Set-Cookie']
|
256
|
-
|
257
|
-
@app.plugin(:sessions, :pad_size=>nil)
|
258
|
-
|
259
|
-
_, h1, b = req('/s/foo/bar')
|
260
|
-
b.must_equal ['bar']
|
261
|
-
_, h2, b = req('/s/foo/bar', 'QUERY_STRING'=>'sut=3700')
|
262
|
-
b.must_equal ['bar']
|
263
|
-
_, h3, b = req('/s/foo/bar2')
|
264
|
-
b.must_equal ['bar2']
|
265
|
-
h1['Set-Cookie'].length.must_equal h2['Set-Cookie'].length
|
266
|
-
h1['Set-Cookie'].wont_equal h2['Set-Cookie']
|
267
|
-
if !defined?(JRUBY_VERSION) || JRUBY_VERSION >= '9.2'
|
268
|
-
h1['Set-Cookie'].length.wont_equal h3['Set-Cookie'].length
|
269
|
-
end
|
270
|
-
|
271
|
-
proc{@app.plugin(:sessions, :pad_size=>0)}.must_raise Roda::RodaError
|
272
|
-
proc{@app.plugin(:sessions, :pad_size=>1)}.must_raise Roda::RodaError
|
273
|
-
proc{@app.plugin(:sessions, :pad_size=>Object.new)}.must_raise Roda::RodaError
|
274
|
-
|
275
|
-
errors.must_equal []
|
276
|
-
end
|
277
|
-
|
278
|
-
it "compresses data over a certain size by default" do
|
279
|
-
long = 'b'*8192
|
280
|
-
proc{body("/s/foo/#{long}")}.must_raise Roda::RodaPlugins::Sessions::CookieTooLarge
|
281
|
-
|
282
|
-
@app.plugin(:sessions, :gzip_over=>8000)
|
283
|
-
body("/s/foo/#{long}").must_equal long
|
284
|
-
body("/g/foo", 'QUERY_STRING'=>'sut=3700').must_equal long
|
285
|
-
|
286
|
-
@app.plugin(:sessions, :gzip_over=>15000)
|
287
|
-
proc{body("/g/foo", 'QUERY_STRING'=>'sut=3700')}.must_raise Roda::RodaPlugins::Sessions::CookieTooLarge
|
288
|
-
|
289
|
-
errors.must_equal []
|
290
|
-
end
|
291
|
-
|
292
|
-
it "raises CookieTooLarge if cookie is too large" do
|
293
|
-
proc{req('/s/foo/'+Base64.urlsafe_encode64(SecureRandom.random_bytes(8192)))}.must_raise Roda::RodaPlugins::Sessions::CookieTooLarge
|
294
|
-
end
|
295
|
-
|
296
|
-
it "ignores session cookies if session exceeds max time since create" do
|
297
|
-
body("/s/foo/bar").must_equal 'bar'
|
298
|
-
body("/g/foo").must_equal 'bar'
|
299
|
-
|
300
|
-
@app.plugin(:sessions, :max_seconds=>-1)
|
301
|
-
body("/g/foo").must_equal ''
|
302
|
-
errors.must_equal ["Not returning session: maximum session time expired"]
|
303
|
-
|
304
|
-
@app.plugin(:sessions, :max_seconds=>10)
|
305
|
-
body("/s/foo/bar").must_equal 'bar'
|
306
|
-
body("/g/foo").must_equal 'bar'
|
307
|
-
|
308
|
-
errors.must_equal []
|
309
|
-
end
|
310
|
-
|
311
|
-
it "ignores session cookies if session exceeds max idle time since update" do
|
312
|
-
body("/s/foo/bar").must_equal 'bar'
|
313
|
-
body("/g/foo").must_equal 'bar'
|
314
|
-
|
315
|
-
@app.plugin(:sessions, :max_idle_seconds=>-1)
|
316
|
-
body("/g/foo").must_equal ''
|
317
|
-
errors.must_equal ["Not returning session: maximum session idle time expired"]
|
318
|
-
|
319
|
-
@app.plugin(:sessions, :max_idle_seconds=>10)
|
320
|
-
body("/s/foo/bar").must_equal 'bar'
|
321
|
-
body("/g/foo").must_equal 'bar'
|
322
|
-
|
323
|
-
errors.must_equal []
|
324
|
-
end
|
325
|
-
|
326
|
-
it "supports :serializer and :parser options to override serializer/deserializer" do
|
327
|
-
body('/s/foo/bar').must_equal 'bar'
|
328
|
-
|
329
|
-
@app.plugin(:sessions, :parser=>proc{|s| JSON.parse("{#{s[1...-1].reverse}}")})
|
330
|
-
body('/g/rab').must_equal 'oof'
|
331
|
-
|
332
|
-
@app.plugin(:sessions, :serializer=>proc{|s| s.to_json.upcase})
|
333
|
-
|
334
|
-
body('/s/foo/baz').must_equal 'baz'
|
335
|
-
body('/g/ZAB').must_equal 'OOF'
|
336
|
-
|
337
|
-
errors.must_equal []
|
338
|
-
end
|
339
|
-
|
340
|
-
it "logs session decoding errors to rack.errors" do
|
341
|
-
body('/s/foo/bar').must_equal 'bar'
|
342
|
-
c = @cookie.dup
|
343
|
-
k = c.split('=', 2)[0] + '='
|
344
|
-
|
345
|
-
@cookie[20] = '!'
|
346
|
-
body('/g/foo').must_equal ''
|
347
|
-
errors.must_equal ["Unable to decode session: invalid base64"]
|
348
|
-
|
349
|
-
@cookie = k+Base64.urlsafe_encode64('')
|
350
|
-
body('/g/foo').must_equal ''
|
351
|
-
errors.must_equal ["Unable to decode session: no data"]
|
352
|
-
|
353
|
-
@cookie = k+Base64.urlsafe_encode64("\0" * 60)
|
354
|
-
body('/g/foo').must_equal ''
|
355
|
-
errors.must_equal ["Unable to decode session: data too short"]
|
356
|
-
|
357
|
-
@cookie = k+Base64.urlsafe_encode64("\1" * 92)
|
358
|
-
body('/g/foo').must_equal ''
|
359
|
-
errors.must_equal ["Unable to decode session: data too short"]
|
360
|
-
|
361
|
-
@cookie = k+Base64.urlsafe_encode64('1'*75)
|
362
|
-
body('/g/foo').must_equal ''
|
363
|
-
errors.must_equal ["Unable to decode session: version marker unsupported"]
|
364
|
-
|
365
|
-
@cookie = k+Base64.urlsafe_encode64("\0"*75)
|
366
|
-
body('/g/foo').must_equal ''
|
367
|
-
errors.must_equal ["Not decoding session: HMAC invalid"]
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
describe "sessions plugin" do
|
372
|
-
include CookieJar
|
373
|
-
|
374
|
-
def req(path, opts={})
|
375
|
-
@errors ||= (errors = []; def errors.puts(s) self << s; end; errors)
|
376
|
-
super(path, opts.merge('rack.errors'=>@errors))
|
377
|
-
end
|
378
|
-
|
379
|
-
def errors
|
380
|
-
e = @errors.dup
|
381
|
-
@errors.clear
|
382
|
-
e
|
383
|
-
end
|
384
|
-
|
385
|
-
it "supports transparent upgrade from Rack::Session::Cookie with default HMAC and coder" do
|
386
|
-
app(:bare) do
|
387
|
-
use Rack::Session::Cookie, :secret=>'1'
|
388
|
-
plugin :middleware_stack
|
389
|
-
route do |r|
|
390
|
-
r.get('s', String, String){|k, v| session[k] = {:a=>v}; v}
|
391
|
-
r.get('g', String){|k| session[k].inspect}
|
392
|
-
''
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
_, h, b = req('/s/foo/bar')
|
397
|
-
(h['Set-Cookie'] =~ /\A(rack\.session=.*); path=\/; HttpOnly\z/).must_equal 0
|
398
|
-
c = $1
|
399
|
-
b.must_equal ['bar']
|
400
|
-
_, h, b = req('/g/foo')
|
401
|
-
h['Set-Cookie'].must_be_nil
|
402
|
-
b.must_equal ['{:a=>"bar"}']
|
403
|
-
|
404
|
-
@app.plugin :sessions, :secret=>'1'*64,
|
405
|
-
:upgrade_from_rack_session_cookie_secret=>'1'
|
406
|
-
@app.middleware_stack.remove{|m, *| m == Rack::Session::Cookie}
|
407
|
-
|
408
|
-
@cookie = c.dup
|
409
|
-
@cookie.slice!(15)
|
410
|
-
body('/g/foo').must_equal 'nil'
|
411
|
-
errors.must_equal ["Not decoding Rack::Session::Cookie session: HMAC invalid"]
|
412
|
-
|
413
|
-
@cookie = c.split('--', 2)[0]
|
414
|
-
body('/g/foo').must_equal 'nil'
|
415
|
-
errors.must_equal ["Not decoding Rack::Session::Cookie session: invalid format"]
|
416
|
-
|
417
|
-
@cookie = c.split('--', 2)[0][13..-1]
|
418
|
-
@cookie = Rack::Utils.unescape(@cookie).unpack('m')[0]
|
419
|
-
@cookie[2] = "^"
|
420
|
-
@cookie = [@cookie].pack('m')
|
421
|
-
cookie = String.new
|
422
|
-
cookie << 'rack.session=' << @cookie << '--' << OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, '1', @cookie)
|
423
|
-
@cookie = cookie
|
424
|
-
body('/g/foo').must_equal 'nil'
|
425
|
-
errors.must_equal ["Error decoding Rack::Session::Cookie session: not base64 encoded marshal dump"]
|
426
|
-
|
427
|
-
@cookie = c
|
428
|
-
_, h, b = req('/g/foo')
|
429
|
-
h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/; HttpOnly(; SameSite=Lax)?\nrack\.session=; path=\/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
|
430
|
-
b.must_equal ['{"a"=>"bar"}']
|
431
|
-
|
432
|
-
@app.plugin :sessions, :cookie_options=>{:path=>'/foo'}, :upgrade_from_rack_session_cookie_options=>{}
|
433
|
-
@cookie = c
|
434
|
-
_, h, b = req('/g/foo')
|
435
|
-
h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/foo; HttpOnly(; SameSite=Lax)?\nrack\.session=; path=\/foo; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
|
436
|
-
b.must_equal ['{"a"=>"bar"}']
|
437
|
-
|
438
|
-
@app.plugin :sessions, :upgrade_from_rack_session_cookie_options=>{:path=>'/baz'}
|
439
|
-
@cookie = c
|
440
|
-
_, h, b = req('/g/foo')
|
441
|
-
h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/foo; HttpOnly(; SameSite=Lax)?\nrack\.session=; path=\/baz; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
|
442
|
-
b.must_equal ['{"a"=>"bar"}']
|
443
|
-
|
444
|
-
@app.plugin :sessions, :upgrade_from_rack_session_cookie_key=>'quux.session'
|
445
|
-
@cookie = c.sub(/\Arack/, 'quux')
|
446
|
-
_, h, b = req('/g/foo')
|
447
|
-
h['Set-Cookie'].must_match(/\Aroda\.session=(.*); path=\/foo; HttpOnly(; SameSite=Lax)?\nquux\.session=; path=\/baz; max-age=0; expires=Thu, 01 Jan 1970 00:00:00/m)
|
448
|
-
b.must_equal ['{"a"=>"bar"}']
|
449
|
-
end
|
450
|
-
end
|
451
|
-
end
|
452
|
-
end
|