From: Ben Darnell Date: Sun, 12 Aug 2012 00:51:27 +0000 (-0700) Subject: Fix str/bytes problems in the auth module on python 3. X-Git-Tag: v2.4.0~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ec4981691141b198b5e7eedb8aa89e4992681100;p=thirdparty%2Ftornado.git Fix str/bytes problems in the auth module on python 3. In some cases signatures were being computed on str(byte object), which is incorrect. Enable python -bb flag to catch this class of errors and fix up a few more instances. Fixes #581. --- diff --git a/tornado/auth.py b/tornado/auth.py index 4f9a980b8..78ea9df80 100644 --- a/tornado/auth.py +++ b/tornado/auth.py @@ -282,10 +282,10 @@ class OAuthMixin(object): consumer_token = self._oauth_consumer_token() url = self._OAUTH_REQUEST_TOKEN_URL args = dict( - oauth_consumer_key=consumer_token["key"], + oauth_consumer_key=escape.to_basestring(consumer_token["key"]), oauth_signature_method="HMAC-SHA1", oauth_timestamp=str(int(time.time())), - oauth_nonce=binascii.b2a_hex(uuid.uuid4().bytes), + oauth_nonce=escape.to_basestring(binascii.b2a_hex(uuid.uuid4().bytes)), oauth_version=getattr(self, "_OAUTH_VERSION", "1.0a"), ) if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a": @@ -312,7 +312,7 @@ class OAuthMixin(object): self.set_cookie("_oauth_request_token", data) args = dict(oauth_token=request_token["key"]) if callback_uri == "oob": - self.finish(authorize_url + "?" + urllib.urlencode(args)) + self.finish(authorize_url + "?" + urllib.urlencode(args)) return elif callback_uri: args["oauth_callback"] = urlparse.urljoin( @@ -323,11 +323,11 @@ class OAuthMixin(object): consumer_token = self._oauth_consumer_token() url = self._OAUTH_ACCESS_TOKEN_URL args = dict( - oauth_consumer_key=consumer_token["key"], - oauth_token=request_token["key"], + oauth_consumer_key=escape.to_basestring(consumer_token["key"]), + oauth_token=escape.to_basestring(request_token["key"]), oauth_signature_method="HMAC-SHA1", oauth_timestamp=str(int(time.time())), - oauth_nonce=binascii.b2a_hex(uuid.uuid4().bytes), + oauth_nonce=escape.to_basestring(binascii.b2a_hex(uuid.uuid4().bytes)), oauth_version=getattr(self, "_OAUTH_VERSION", "1.0a"), ) if "verifier" in request_token: @@ -372,11 +372,11 @@ class OAuthMixin(object): """ consumer_token = self._oauth_consumer_token() base_args = dict( - oauth_consumer_key=consumer_token["key"], - oauth_token=access_token["key"], + oauth_consumer_key=escape.to_basestring(consumer_token["key"]), + oauth_token=escape.to_basestring(access_token["key"]), oauth_signature_method="HMAC-SHA1", oauth_timestamp=str(int(time.time())), - oauth_nonce=binascii.b2a_hex(uuid.uuid4().bytes), + oauth_nonce=escape.to_basestring(binascii.b2a_hex(uuid.uuid4().bytes)), oauth_version=getattr(self, "_OAUTH_VERSION", "1.0a"), ) args = {} diff --git a/tornado/test/options_test.py b/tornado/test/options_test.py index e5411d945..f792329df 100644 --- a/tornado/test/options_test.py +++ b/tornado/test/options_test.py @@ -1,14 +1,27 @@ from __future__ import absolute_import, division, with_statement +import contextlib import logging import os import re import tempfile import unittest +import warnings from tornado.escape import utf8 from tornado.options import _Options, _LogFormatter from tornado.util import b, bytes_type +@contextlib.contextmanager +def ignore_bytes_warning(): + if not hasattr(warnings, 'catch_warnings'): + # python 2.5 doesn't have catch_warnings, but it doesn't have + # BytesWarning either so there's nothing to catch. + yield + return + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=BytesWarning) + yield + class OptionsTest(unittest.TestCase): def setUp(self): @@ -56,7 +69,7 @@ class LogFormatterTest(unittest.TestCase): # Base case: default setup without explicit encoding. # In python 2, supports arbitrary byte strings and unicode objects # that contain only ascii. In python 3, supports ascii-only unicode - # strings (but byte strings will be repr'd automatically. + # strings (but byte strings will be repr'd automatically). return logging.FileHandler(filename) def get_output(self): @@ -73,9 +86,10 @@ class LogFormatterTest(unittest.TestCase): self.assertEqual(self.get_output(), b("foo")) def test_bytes_logging(self): - self.logger.error(b("\xe9")) - # This will be "\xe9" on python 2 or "b'\xe9'" on python 3 - self.assertEqual(self.get_output(), utf8(repr(b("\xe9")))) + with ignore_bytes_warning(): + # This will be "\xe9" on python 2 or "b'\xe9'" on python 3 + self.logger.error(b("\xe9")) + self.assertEqual(self.get_output(), utf8(repr(b("\xe9")))) def test_utf8_logging(self): self.logger.error(u"\u00e9".encode("utf8")) diff --git a/tornado/test/runtests.py b/tornado/test/runtests.py index 047d45898..a7a73e1ff 100644 --- a/tornado/test/runtests.py +++ b/tornado/test/runtests.py @@ -1,6 +1,7 @@ #!/usr/bin/env python from __future__ import absolute_import, division, with_statement +import sys import unittest TEST_MODULES = [ @@ -53,4 +54,12 @@ if __name__ == '__main__': module=r"tornado\..*") import tornado.testing - tornado.testing.main() + kwargs = {} + if sys.version_info >= (3,2): + # HACK: unittest.main will make its own changes to the warning + # configuration, which may conflict with the settings above + # or command-line flags like -bb. Passing warnings=False + # suppresses this behavior, although this looks like an implementation + # detail. http://bugs.python.org/issue15626 + kwargs['warnings'] = False + tornado.testing.main(**kwargs) diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index ad4173365..3418ec42e 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, division, with_statement from tornado import gen -from tornado.escape import json_decode, utf8, to_unicode, recursive_unicode, native_str +from tornado.escape import json_decode, utf8, to_unicode, recursive_unicode, native_str, to_basestring from tornado.iostream import IOStream from tornado.template import DictLoader from tornado.testing import LogTrapTestCase, AsyncHTTPTestCase @@ -66,7 +66,8 @@ class SecureCookieTest(LogTrapTestCase): 'foo', '1234', b('5678') + timestamp), sig) # tamper with the cookie - handler._cookies['foo'] = utf8('1234|5678%s|%s' % (timestamp, sig)) + handler._cookies['foo'] = utf8('1234|5678%s|%s' % ( + to_basestring(timestamp), to_basestring(sig))) # it gets rejected self.assertTrue(handler.get_secure_cookie('foo') is None) diff --git a/tox.ini b/tox.ini index dd4132a8b..381320424 100644 --- a/tox.ini +++ b/tox.ini @@ -83,6 +83,9 @@ deps = [testenv:py32] basepython = python3.2 setenv = LANG=C +# -b turns on an extra warning when calling str(bytes), and -bb makes +# it an error. +commands = python -bb -m tornado.test.runtests {posargs:} [testenv:py32-utf8] basepython = python3.2