]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Document and test for type differences between get_cookie and get_secure_cookie.
authorBen Darnell <ben@bendarnell.com>
Mon, 7 May 2012 00:29:02 +0000 (17:29 -0700)
committerBen Darnell <ben@bendarnell.com>
Mon, 7 May 2012 00:29:02 +0000 (17:29 -0700)
Closes #501.

tornado/test/web_test.py
tornado/web.py

index 49d67de5aed3a120ce45942134876c4b82af1201..40aafbc1498ba4d5f87ed8490c6970f09ad95d34 100644 (file)
@@ -5,7 +5,7 @@ from tornado.iostream import IOStream
 from tornado.template import DictLoader
 from tornado.testing import LogTrapTestCase, AsyncHTTPTestCase
 from tornado.util import b, bytes_type, ObjectDict
-from tornado.web import RequestHandler, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature
+from tornado.web import RequestHandler, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature, create_signed_value
 
 import binascii
 import logging
@@ -60,6 +60,13 @@ class SecureCookieTest(LogTrapTestCase):
         # it gets rejected
         assert handler.get_secure_cookie('foo') is None
 
+    def test_arbitrary_bytes(self):
+        # Secure cookies accept arbitrary data (which is base64 encoded).
+        # Note that normal cookies accept only a subset of ascii.
+        handler = CookieTestRequestHandler()
+        handler.set_secure_cookie('foo', b('\xe9'))
+        self.assertEqual(handler.get_secure_cookie('foo'), b('\xe9'))
+
 
 class CookieTest(AsyncHTTPTestCase, LogTrapTestCase):
     def get_app(self):
@@ -294,6 +301,12 @@ class TypeCheckHandler(RequestHandler):
         self.check_type('cookie_key', self.cookies.keys()[0], str)
         self.check_type('cookie_value', self.cookies.values()[0].value, str)
 
+        # Secure cookies return bytes because they can contain arbitrary
+        # data, but regular cookies are native strings.
+        assert self.cookies.keys() == ['asdf']
+        self.check_type('get_secure_cookie', self.get_secure_cookie('asdf'), bytes_type)
+        self.check_type('get_cookie', self.get_cookie('asdf'), str)
+
         self.check_type('xsrf_token', self.xsrf_token, bytes_type)
         self.check_type('xsrf_form_html', self.xsrf_form_html(), str)
 
@@ -413,6 +426,7 @@ class HeaderInjectionHandler(RequestHandler):
 
 
 class WebTest(AsyncHTTPTestCase, LogTrapTestCase):
+    COOKIE_SECRET = "WebTest.COOKIE_SECRET"
     def get_app(self):
         loader = DictLoader({
                 "linkify.html": "{% module linkify(message) %}",
@@ -441,7 +455,8 @@ class WebTest(AsyncHTTPTestCase, LogTrapTestCase):
             ]
         return Application(urls,
                            template_loader=loader,
-                           autoescape="xhtml_escape")
+                           autoescape="xhtml_escape",
+                           cookie_secret=self.COOKIE_SECRET)
 
     def fetch_json(self, *args, **kwargs):
         response = self.fetch(*args, **kwargs)
@@ -449,13 +464,15 @@ class WebTest(AsyncHTTPTestCase, LogTrapTestCase):
         return json_decode(response.body)
 
     def test_types(self):
+        cookie_value = to_unicode(create_signed_value(self.COOKIE_SECRET,
+                                                      "asdf", "qwer"))
         response = self.fetch("/typecheck/asdf?foo=bar",
-                              headers={"Cookie": "cook=ie"})
+                              headers={"Cookie": "asdf=" + cookie_value})
         data = json_decode(response.body)
         self.assertEqual(data, {})
 
         response = self.fetch("/typecheck/asdf?foo=bar", method="POST",
-                              headers={"Cookie": "cook=ie"},
+                              headers={"Cookie": "asdf=" + cookie_value},
                               body="foo=bar")
 
     def test_decode_argument(self):
index cb8a68bb10b707c557d645df682d18713f2d2e7e..8978d2d0b14db38e07521f19c19c700571489884 100644 (file)
@@ -408,6 +408,9 @@ class RequestHandler(object):
         Note that the ``expires_days`` parameter sets the lifetime of the
         cookie in the browser, but is independent of the ``max_age_days``
         parameter to `get_secure_cookie`.
+
+        Secure cookies may contain arbitrary byte values, not just unicode
+        strings (unlike regular cookies)
         """
         self.set_cookie(name, self.create_signed_value(name, value),
                         expires_days=expires_days, **kwargs)
@@ -424,7 +427,11 @@ class RequestHandler(object):
                                    name, value)
 
     def get_secure_cookie(self, name, value=None, max_age_days=31):
-        """Returns the given signed cookie if it validates, or None."""
+        """Returns the given signed cookie if it validates, or None.
+
+        The decoded cookie value is returned as a byte string (unlike
+        `get_cookie`).
+        """
         self.require_setting("cookie_secret", "secure cookies")
         if value is None:
             value = self.get_cookie(name)