PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/rails/actionpack/test/controller/session/cookie_store_test.rb

https://bitbucket.org/javierbuilder/uncontabilidad
Ruby | 310 lines | 256 code | 51 blank | 3 comment | 4 complexity | 32fcfbddb0993cdbd109a500223826b5 MD5 | raw file
  1. require 'abstract_unit'
  2. require 'action_controller/cgi_process'
  3. require 'action_controller/cgi_ext'
  4. require 'stringio'
  5. class CGI::Session::CookieStore
  6. def ensure_secret_secure_with_test_hax(secret)
  7. if secret == CookieStoreTest.default_session_options['secret']
  8. return true
  9. else
  10. ensure_secret_secure_without_test_hax(secret)
  11. end
  12. end
  13. alias_method_chain :ensure_secret_secure, :test_hax
  14. end
  15. # Expose for tests.
  16. class CGI
  17. attr_reader :output_cookies, :output_hidden
  18. class Session
  19. attr_reader :dbman
  20. class CookieStore
  21. attr_reader :data, :original, :cookie_options
  22. end
  23. end
  24. end
  25. class CookieStoreTest < Test::Unit::TestCase
  26. def self.default_session_options
  27. { 'database_manager' => CGI::Session::CookieStore,
  28. 'session_key' => '_myapp_session',
  29. 'secret' => 'Keep it secret; keep it safe.',
  30. 'no_cookies' => true,
  31. 'no_hidden' => true,
  32. 'session_http_only' => true
  33. }
  34. end
  35. def self.cookies
  36. { :empty => ['BAgw--0686dcaccc01040f4bd4f35fe160afe9bc04c330', {}],
  37. :a_one => ['BAh7BiIGYWkG--5689059497d7f122a7119f171aef81dcfd807fec', { 'a' => 1 }],
  38. :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--9d20154623b9eeea05c62ab819be0e2483238759', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
  39. :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--bf9785a666d3c4ac09f7fe3353496b437546cfbf', { 'user_id' => 123, 'flash' => {} }],
  40. :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--bf9785a666d3c4ac09f7fe3353496b437546cfbf'), { 'user_id' => 123, 'flash' => {} }] }
  41. end
  42. def setup
  43. ENV.delete('HTTP_COOKIE')
  44. end
  45. def test_raises_argument_error_if_missing_session_key
  46. [nil, ''].each do |blank|
  47. assert_raise(ArgumentError, blank.inspect) { new_session 'session_key' => blank }
  48. end
  49. end
  50. def test_raises_argument_error_if_missing_secret
  51. [nil, ''].each do |blank|
  52. assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
  53. end
  54. end
  55. def test_raises_argument_error_if_secret_is_probably_insecure
  56. ["password", "secret", "12345678901234567890123456789"].each do |blank|
  57. assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
  58. end
  59. end
  60. def test_reconfigures_session_to_omit_id_cookie_and_hidden_field
  61. new_session do |session|
  62. assert_equal true, @options['no_hidden']
  63. assert_equal true, @options['no_cookies']
  64. end
  65. end
  66. def test_restore_unmarshals_missing_cookie_as_empty_hash
  67. new_session do |session|
  68. assert_nil session.dbman.data
  69. assert_nil session['test']
  70. assert_equal Hash.new, session.dbman.data
  71. end
  72. end
  73. def test_restore_unmarshals_good_cookies
  74. cookies(:empty, :a_one, :typical).each do |value, expected|
  75. set_cookie! value
  76. new_session do |session|
  77. assert_nil session['lazy loads the data hash']
  78. assert_equal expected, session.dbman.data
  79. end
  80. end
  81. end
  82. def test_restore_deletes_tampered_cookies
  83. set_cookie! 'a--b'
  84. new_session do |session|
  85. assert_raise(CGI::Session::CookieStore::TamperedWithCookie) { session['fail'] }
  86. assert_cookie_deleted session
  87. end
  88. end
  89. def test_restores_double_encoded_cookies
  90. set_cookie! cookie_value(:double_escaped)
  91. new_session do |session|
  92. session.dbman.restore
  93. assert_equal session["user_id"], 123
  94. assert_equal session["flash"], {}
  95. end
  96. end
  97. def test_close_doesnt_write_cookie_if_data_is_blank
  98. new_session do |session|
  99. assert_no_cookies session
  100. session.close
  101. assert_no_cookies session
  102. end
  103. end
  104. def test_close_doesnt_write_cookie_if_data_is_unchanged
  105. set_cookie! cookie_value(:typical)
  106. new_session do |session|
  107. assert_no_cookies session
  108. session['user_id'] = session['user_id']
  109. session.close
  110. assert_no_cookies session
  111. end
  112. end
  113. def test_close_raises_when_data_overflows
  114. set_cookie! cookie_value(:empty)
  115. new_session do |session|
  116. session['overflow'] = 'bye!' * 1024
  117. assert_raise(CGI::Session::CookieStore::CookieOverflow) { session.close }
  118. assert_no_cookies session
  119. end
  120. end
  121. def test_close_marshals_and_writes_cookie
  122. set_cookie! cookie_value(:typical)
  123. new_session do |session|
  124. assert_no_cookies session
  125. session['flash'] = {}
  126. assert_no_cookies session
  127. session.close
  128. assert_equal 1, session.cgi.output_cookies.size
  129. cookie = session.cgi.output_cookies.first
  130. assert_cookie cookie, cookie_value(:flashed)
  131. assert_http_only_cookie cookie
  132. assert_secure_cookie cookie, false
  133. end
  134. end
  135. def test_writes_non_secure_cookie_by_default
  136. set_cookie! cookie_value(:typical)
  137. new_session do |session|
  138. session['flash'] = {}
  139. session.close
  140. cookie = session.cgi.output_cookies.first
  141. assert_secure_cookie cookie,false
  142. end
  143. end
  144. def test_writes_secure_cookie
  145. set_cookie! cookie_value(:typical)
  146. new_session('session_secure'=>true) do |session|
  147. session['flash'] = {}
  148. session.close
  149. cookie = session.cgi.output_cookies.first
  150. assert_secure_cookie cookie
  151. end
  152. end
  153. def test_http_only_cookie_by_default
  154. set_cookie! cookie_value(:typical)
  155. new_session do |session|
  156. session['flash'] = {}
  157. session.close
  158. cookie = session.cgi.output_cookies.first
  159. assert_http_only_cookie cookie
  160. end
  161. end
  162. def test_overides_http_only_cookie
  163. set_cookie! cookie_value(:typical)
  164. new_session('session_http_only'=>false) do |session|
  165. session['flash'] = {}
  166. session.close
  167. cookie = session.cgi.output_cookies.first
  168. assert_http_only_cookie cookie, false
  169. end
  170. end
  171. def test_delete_writes_expired_empty_cookie_and_sets_data_to_nil
  172. set_cookie! cookie_value(:typical)
  173. new_session do |session|
  174. assert_no_cookies session
  175. session.delete
  176. assert_cookie_deleted session
  177. # @data is set to nil so #close doesn't send another cookie.
  178. session.close
  179. assert_cookie_deleted session
  180. end
  181. end
  182. def test_new_session_doesnt_reuse_deleted_cookie_data
  183. set_cookie! cookie_value(:typical)
  184. new_session do |session|
  185. assert_not_nil session['user_id']
  186. session.delete
  187. # Start a new session using the same CGI instance.
  188. post_delete_session = CGI::Session.new(session.cgi, self.class.default_session_options)
  189. assert_nil post_delete_session['user_id']
  190. end
  191. end
  192. private
  193. def assert_no_cookies(session)
  194. assert_nil session.cgi.output_cookies, session.cgi.output_cookies.inspect
  195. end
  196. def assert_cookie_deleted(session, message = 'Expected session deletion cookie to be set')
  197. assert_equal 1, session.cgi.output_cookies.size
  198. cookie = session.cgi.output_cookies.first
  199. assert_cookie cookie, nil, 1.year.ago.to_date, "#{message}: #{cookie.name} => #{cookie.value}"
  200. end
  201. def assert_cookie(cookie, value = nil, expires = nil, message = nil)
  202. assert_equal '_myapp_session', cookie.name, message
  203. assert_equal [value].compact, cookie.value, message
  204. assert_equal expires, cookie.expires ? cookie.expires.to_date : cookie.expires, message
  205. end
  206. def assert_secure_cookie(cookie,value=true)
  207. assert cookie.secure==value
  208. end
  209. def assert_http_only_cookie(cookie,value=true)
  210. assert cookie.http_only==value
  211. end
  212. def cookies(*which)
  213. self.class.cookies.values_at(*which)
  214. end
  215. def cookie_value(which)
  216. self.class.cookies[which].first
  217. end
  218. def set_cookie!(value)
  219. ENV['HTTP_COOKIE'] = "_myapp_session=#{value}"
  220. end
  221. def new_session(options = {})
  222. with_cgi do |cgi|
  223. assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
  224. assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
  225. @options = self.class.default_session_options.merge(options)
  226. session = CGI::Session.new(cgi, @options)
  227. ObjectSpace.undefine_finalizer(session)
  228. assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
  229. assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
  230. yield session if block_given?
  231. session
  232. end
  233. end
  234. def with_cgi
  235. ENV['REQUEST_METHOD'] = 'GET'
  236. ENV['HTTP_HOST'] = 'example.com'
  237. ENV['QUERY_STRING'] = ''
  238. cgi = CGI.new('query', StringIO.new(''))
  239. yield cgi if block_given?
  240. cgi
  241. end
  242. end
  243. class CookieStoreWithBlockAsSecretTest < CookieStoreTest
  244. def self.default_session_options
  245. CookieStoreTest.default_session_options.merge 'secret' => Proc.new { 'Keep it secret; keep it safe.' }
  246. end
  247. end
  248. class CookieStoreWithMD5DigestTest < CookieStoreTest
  249. def self.default_session_options
  250. CookieStoreTest.default_session_options.merge 'digest' => 'MD5'
  251. end
  252. def self.cookies
  253. { :empty => ['BAgw--0415cc0be9579b14afc22ee2d341aa21', {}],
  254. :a_one => ['BAh7BiIGYWkG--5a0ed962089cc6600ff44168a5d59bc8', { 'a' => 1 }],
  255. :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--f426763f6ef435b3738b493600db8d64', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
  256. :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--0af9156650dab044a53a91a4ddec2c51', { 'user_id' => 123, 'flash' => {} }],
  257. :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--0af9156650dab044a53a91a4ddec2c51'), { 'user_id' => 123, 'flash' => {} }] }
  258. end
  259. end