1 require 'abstract_unit'
2 require 'action_controller/cgi_process'
3 require 'action_controller/cgi_ext'
8 class CGI
::Session::CookieStore
9 def ensure_secret_secure_with_test_hax(secret
)
10 if secret
== CookieStoreTest
.default_session_options
['secret']
13 ensure_secret_secure_without_test_hax(secret
)
16 alias_method_chain
:ensure_secret_secure, :test_hax
22 attr_reader
:output_cookies, :output_hidden
28 attr_reader
:data, :original, :cookie_options
33 class CookieStoreTest
< Test
::Unit::TestCase
34 def self.default_session_options
35 { 'database_manager' => CGI
::Session::CookieStore,
36 'session_key' => '_myapp_session',
37 'secret' => 'Keep it secret; keep it safe.',
40 'session_http_only' => true
45 { :empty => ['BAgw--0686dcaccc01040f4bd4f35fe160afe9bc04c330', {}],
46 :a_one => ['BAh7BiIGYWkG--5689059497d7f122a7119f171aef81dcfd807fec', { 'a' => 1 }],
47 :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--9d20154623b9eeea05c62ab819be0e2483238759', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
48 :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--bf9785a666d3c4ac09f7fe3353496b437546cfbf', { 'user_id' => 123, 'flash' => {} }],
49 :double_escaped => [CGI
.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--bf9785a666d3c4ac09f7fe3353496b437546cfbf'), { 'user_id' => 123, 'flash' => {} }] }
54 ENV.delete('HTTP_COOKIE')
57 def test_raises_argument_error_if_missing_session_key
58 [nil, ''].each
do |blank
|
59 assert_raise(ArgumentError
, blank
.inspect
) { new_session
'session_key' => blank
}
63 def test_raises_argument_error_if_missing_secret
64 [nil, ''].each
do |blank
|
65 assert_raise(ArgumentError
, blank
.inspect
) { new_session
'secret' => blank
}
69 def test_raises_argument_error_if_secret_is_probably_insecure
70 ["password", "secret", "12345678901234567890123456789"].each
do |blank
|
71 assert_raise(ArgumentError
, blank
.inspect
) { new_session
'secret' => blank
}
75 def test_reconfigures_session_to_omit_id_cookie_and_hidden_field
76 new_session
do |session
|
77 assert_equal
true, @options['no_hidden']
78 assert_equal
true, @options['no_cookies']
82 def test_restore_unmarshals_missing_cookie_as_empty_hash
83 new_session
do |session
|
84 assert_nil session
.dbman
.data
85 assert_nil session
['test']
86 assert_equal Hash
.new
, session
.dbman
.data
90 def test_restore_unmarshals_good_cookies
91 cookies(:empty, :a_one, :typical).each
do |value
, expected
|
93 new_session
do |session
|
94 assert_nil session
['lazy loads the data hash']
95 assert_equal expected
, session
.dbman
.data
100 def test_restore_deletes_tampered_cookies
102 new_session
do |session
|
103 assert_raise(CGI
::Session::CookieStore::TamperedWithCookie) { session
['fail'] }
104 assert_cookie_deleted session
108 def test_restores_double_encoded_cookies
109 set_cookie
! cookie_value(:double_escaped)
110 new_session
do |session
|
111 session
.dbman
.restore
112 assert_equal session
["user_id"], 123
113 assert_equal session
["flash"], {}
117 def test_close_doesnt_write_cookie_if_data_is_blank
118 new_session
do |session
|
119 assert_no_cookies session
121 assert_no_cookies session
125 def test_close_doesnt_write_cookie_if_data_is_unchanged
126 set_cookie
! cookie_value(:typical)
127 new_session
do |session
|
128 assert_no_cookies session
129 session
['user_id'] = session
['user_id']
131 assert_no_cookies session
135 def test_close_raises_when_data_overflows
136 set_cookie
! cookie_value(:empty)
137 new_session
do |session
|
138 session
['overflow'] = 'bye!' * 1024
139 assert_raise(CGI
::Session::CookieStore::CookieOverflow) { session
.close
}
140 assert_no_cookies session
144 def test_close_marshals_and_writes_cookie
145 set_cookie
! cookie_value(:typical)
146 new_session
do |session
|
147 assert_no_cookies session
148 session
['flash'] = {}
149 assert_no_cookies session
151 assert_equal
1, session
.cgi
.output_cookies
.size
152 cookie
= session
.cgi
.output_cookies
.first
153 assert_cookie cookie
, cookie_value(:flashed)
154 assert_http_only_cookie cookie
155 assert_secure_cookie cookie
, false
159 def test_writes_non_secure_cookie_by_default
160 set_cookie
! cookie_value(:typical)
161 new_session
do |session
|
162 session
['flash'] = {}
164 cookie
= session
.cgi
.output_cookies
.first
165 assert_secure_cookie cookie
,false
169 def test_writes_secure_cookie
170 set_cookie
! cookie_value(:typical)
171 new_session('session_secure'=>true) do |session
|
172 session
['flash'] = {}
174 cookie
= session
.cgi
.output_cookies
.first
175 assert_secure_cookie cookie
179 def test_http_only_cookie_by_default
180 set_cookie
! cookie_value(:typical)
181 new_session
do |session
|
182 session
['flash'] = {}
184 cookie
= session
.cgi
.output_cookies
.first
185 assert_http_only_cookie cookie
189 def test_overides_http_only_cookie
190 set_cookie
! cookie_value(:typical)
191 new_session('session_http_only'=>false) do |session
|
192 session
['flash'] = {}
194 cookie
= session
.cgi
.output_cookies
.first
195 assert_http_only_cookie cookie
, false
199 def test_delete_writes_expired_empty_cookie_and_sets_data_to_nil
200 set_cookie
! cookie_value(:typical)
201 new_session
do |session
|
202 assert_no_cookies session
204 assert_cookie_deleted session
206 # @data is set to nil so #close doesn't send another cookie.
208 assert_cookie_deleted session
212 def test_new_session_doesnt_reuse_deleted_cookie_data
213 set_cookie
! cookie_value(:typical)
215 new_session
do |session
|
216 assert_not_nil session
['user_id']
219 # Start a new session using the same CGI instance.
220 post_delete_session
= CGI
::Session.new(session
.cgi
, self.class.default_session_options
)
221 assert_nil post_delete_session
['user_id']
226 def assert_no_cookies(session
)
227 assert_nil session
.cgi
.output_cookies
, session
.cgi
.output_cookies
.inspect
230 def assert_cookie_deleted(session
, message
= 'Expected session deletion cookie to be set')
231 assert_equal
1, session
.cgi
.output_cookies
.size
232 cookie
= session
.cgi
.output_cookies
.first
233 assert_cookie cookie
, nil, 1.year
.ago
.to_date
, "#{message}: #{cookie.name} => #{cookie.value}"
236 def assert_cookie(cookie
, value
= nil, expires
= nil, message
= nil)
237 assert_equal
'_myapp_session', cookie
.name
, message
238 assert_equal
[value
].compact
, cookie
.value
, message
239 assert_equal expires
, cookie
.expires
? cookie
.expires
.to_date
: cookie
.expires
, message
242 def assert_secure_cookie(cookie
,value
=true)
243 assert cookie
.secure
==value
246 def assert_http_only_cookie(cookie
,value
=true)
247 assert cookie
.http_only
==value
251 self.class.cookies
.values_at(*which
)
254 def cookie_value(which
)
255 self.class.cookies
[which
].first
258 def set_cookie
!(value
)
259 ENV['HTTP_COOKIE'] = "_myapp_session=#{value}"
262 def new_session(options
= {})
264 assert_nil cgi
.output_hidden
, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
265 assert_nil cgi
.output_cookies
, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
267 @options = self.class.default_session_options
.merge(options
)
268 session
= CGI
::Session.new(cgi
, @options)
269 ObjectSpace
.undefine_finalizer(session
)
271 assert_nil cgi
.output_hidden
, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
272 assert_nil cgi
.output_cookies
, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
274 yield session
if block_given
?
280 ENV['REQUEST_METHOD'] = 'GET'
281 ENV['HTTP_HOST'] = 'example.com'
282 ENV['QUERY_STRING'] = ''
284 cgi
= CGI
.new('query', StringIO
.new(''))
285 yield cgi
if block_given
?
291 class CookieStoreWithBlockAsSecretTest
< CookieStoreTest
292 def self.default_session_options
293 CookieStoreTest
.default_session_options
.merge
'secret' => Proc
.new
{ 'Keep it secret; keep it safe.' }
298 class CookieStoreWithMD5DigestTest
< CookieStoreTest
299 def self.default_session_options
300 CookieStoreTest
.default_session_options
.merge
'digest' => 'MD5'
304 { :empty => ['BAgw--0415cc0be9579b14afc22ee2d341aa21', {}],
305 :a_one => ['BAh7BiIGYWkG--5a0ed962089cc6600ff44168a5d59bc8', { 'a' => 1 }],
306 :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--f426763f6ef435b3738b493600db8d64', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
307 :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--0af9156650dab044a53a91a4ddec2c51', { 'user_id' => 123, 'flash' => {} }],
308 :double_escaped => [CGI
.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--0af9156650dab044a53a91a4ddec2c51'), { 'user_id' => 123, 'flash' => {} }] }