Skip to content

Commit fcd8c32

Browse files
committed
raise an exception if the URL passed in is not a unicode object nor ascii
We can't submit a correct URL with arbitrary bytes -- we have to submit a utf-8 encoded unicode string. (Otherwise we'll cause either a rejection or a signature mismatch in the server, which is what has happened at SimpleGeo.) If the caller passes in non-ascii things in a str then it would be better for them to change their code to decode it to unicode before passing it in than for us to decode it, since they have a better chance of knowing what encoding it is in -- if we did it we would be guessing.
1 parent 77cb11f commit fcd8c32

File tree

2 files changed

+36
-2
lines changed

2 files changed

+36
-2
lines changed

oauth2/__init__.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,21 @@ def build_xoauth_string(url, consumer, token=None):
8888
return "%s %s %s" % ("GET", url, ','.join(params))
8989

9090

91+
def check_for_bad_encoding(s):
92+
""" Returns None if s is a unicode or an ascii string, else
93+
returns the UnicodeDecodeError that results from attempting to
94+
decode it as ascii. """
95+
try:
96+
s.decode('ascii')
97+
except UnicodeDecodeError, le:
98+
return le
99+
91100
def escape(s):
92101
"""Escape a URL including any /."""
93-
return urllib.quote(s, safe='~')
102+
encerr = check_for_bad_encoding(s)
103+
if encerr:
104+
raise Error('You are required to pass either a unicode object or an ascii string here. You passed a Python string object which contained non-ascii: %r. The UnicodeDecodeError that resulted from attempting to interpret it as ascii was: %s' % (encerr,))
105+
return urllib.quote(s.encode('utf-8'), safe='~')
94106

95107

96108
def generate_timestamp():
@@ -276,8 +288,11 @@ class Request(dict):
276288
version = OAUTH_VERSION
277289

278290
def __init__(self, method=HTTP_METHOD, url=None, parameters=None):
291+
encerr = check_for_bad_encoding(url)
292+
if encerr:
293+
raise ValueError("You are required to pass either a unicode object or an ascii string for `url'. You passed a Python string object which contained non-ascii: %r. The UnicodeDecodeError that resulted from attempting to interpret it as ascii was: %s" % (url, encerr,))
279294
self.method = method
280-
self.url = url
295+
self.url = unicode(url)
281296
if parameters is not None:
282297
self.update(parameters)
283298

@@ -735,6 +750,7 @@ def signing_base(self, request, consumer, token):
735750
def sign(self, request, consumer, token):
736751
"""Builds the base signature string."""
737752
key, raw = self.signing_base(request, consumer, token)
753+
print "raw: type: %s, str: %s, repr: %s, hexbytes: %s" % (type(raw), str(raw), repr(raw), ["0x%x" % ord(c) for c in raw])
738754

739755
hashed = hmac.new(key, raw, sha)
740756

tests/test_oauth.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,24 @@ def test_get_normalized_string_escapes_spaces_properly(self):
558558
expected = urllib.urlencode(sorted(params.items())).replace('+', '%20')
559559
self.assertEqual(expected, res)
560560

561+
def test_request_nonascii_bytes(self):
562+
# If someone has a sequence of bytes which is not ascii, we'll
563+
# raise an exception as early as possible.
564+
url = "http://sp.example.com/\x92"
565+
566+
params = {
567+
'oauth_version': "1.0",
568+
'oauth_nonce': "4572616e48616d6d65724c61686176",
569+
'oauth_timestamp': "137131200"
570+
}
571+
572+
tok = oauth.Token(key="tok-test-key", secret="tok-test-secret")
573+
con = oauth.Consumer(key="con-test-key", secret="con-test-secret")
574+
575+
params['oauth_token'] = tok.key
576+
params['oauth_consumer_key'] = con.key
577+
self.assertRaises(ValueError, oauth.Request, method="GET", url=url, parameters=params)
578+
561579
def test_sign_request(self):
562580
url = "http://sp.example.com/"
563581

0 commit comments

Comments
 (0)