diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2015-04-21 15:57:41 +0200 |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2015-04-21 15:57:41 +0200 |
commit | bdd1b0d7eec75e3a29564d11ac92b6588ea7eba5 (patch) | |
tree | 6ee1e8ba1d6834f343d960aaa3b3868b594d1d29 | |
parent | 8bd799e33c874f88091b3a16b5307ead22f1ee3a (diff) | |
download | paste-git-bdd1b0d7eec75e3a29564d11ac92b6588ea7eba5.tar.gz |
Port paste.auth to Python 3
* md5() and hmac expects bytes: on Python 3, encode text to utf-8
* Don't compare None with int
* HTTP body must be bytes
-rw-r--r-- | paste/auth/cookie.py | 17 | ||||
-rw-r--r-- | paste/auth/digest.py | 29 | ||||
-rw-r--r-- | tests/test_auth/test_auth_cookie.py | 6 | ||||
-rw-r--r-- | tests/test_auth/test_auth_digest.py | 8 |
4 files changed, 46 insertions, 14 deletions
diff --git a/paste/auth/cookie.py b/paste/auth/cookie.py index 14b1fd8..8f11d1b 100644 --- a/paste/auth/cookie.py +++ b/paste/auth/cookie.py @@ -74,7 +74,10 @@ class CookieTooLarge(RuntimeError): _all_chars = ''.join([chr(x) for x in range(0, 255)]) def new_secret(): """ returns a 64 byte secret """ - return ''.join(random.sample(_all_chars, 64)) + secret = ''.join(random.sample(_all_chars, 64)) + if six.PY3: + secret = secret.encode('utf8') + return secret class AuthCookieSigner(object): """ @@ -137,12 +140,16 @@ class AuthCookieSigner(object): need to be escaped and quoted). The expiration of this cookie is handled server-side in the auth() function. """ + timestamp = make_time(time.time() + 60*self.timeout) + if six.PY3: + content = content.encode('utf8') + timestamp = timestamp.encode('utf8') cookie = base64.encodestring( hmac.new(self.secret, content, sha1).digest() + - make_time(time.time() + 60*self.timeout) + + timestamp + content) - cookie = cookie.replace("/", "_").replace("=", "~") - cookie = cookie.replace('\n', '').replace('\r', '') + cookie = cookie.replace(b"/", b"_").replace(b"=", b"~") + cookie = cookie.replace(b'\n', b'').replace(b'\r', b'') if len(cookie) > self.maxlen: raise CookieTooLarge(content, cookie) return cookie @@ -298,6 +305,8 @@ class AuthCookieHandler(object): if content: content = ";".join(content) content = self.signer.sign(content) + if six.PY3: + content = content.decode('utf8') cookie = '%s=%s; Path=/;' % (self.cookie_name, content) if 'https' == environ['wsgi.url_scheme']: cookie += ' secure;' diff --git a/paste/auth/digest.py b/paste/auth/digest.py index 798f447..85e0362 100644 --- a/paste/auth/digest.py +++ b/paste/auth/digest.py @@ -37,6 +37,7 @@ except ImportError: from md5 import md5 import time, random from six.moves.urllib.parse import quote as url_quote +import six def _split_auth_string(auth_string): """ split a digest auth string into individual key=value strings """ @@ -68,7 +69,10 @@ def _auth_to_kv_pairs(auth_string): def digest_password(realm, username, password): """ construct the appropriate hashcode needed for HTTP digest """ - return md5("%s:%s:%s" % (username, realm, password)).hexdigest() + content = "%s:%s:%s" % (username, realm, password) + if six.PY3: + content = content.encode('utf8') + return md5(content).hexdigest() class AuthDigestAuthenticator(object): """ implementation of RFC 2617 - HTTP Digest Authentication """ @@ -79,10 +83,16 @@ class AuthDigestAuthenticator(object): def build_authentication(self, stale = ''): """ builds the authentication error """ - nonce = md5( - "%s:%s" % (time.time(), random.random())).hexdigest() - opaque = md5( - "%s:%s" % (time.time(), random.random())).hexdigest() + content = "%s:%s" % (time.time(), random.random()) + if six.PY3: + content = content.encode('utf-8') + nonce = md5(content).hexdigest() + + content = "%s:%s" % (time.time(), random.random()) + if six.PY3: + content = content.encode('utf-8') + opaque = md5(content).hexdigest() + self.nonce[nonce] = None parts = {'realm': self.realm, 'qop': 'auth', 'nonce': nonce, 'opaque': opaque } @@ -97,17 +107,22 @@ class AuthDigestAuthenticator(object): """ computes the authentication, raises error if unsuccessful """ if not ha1: return self.build_authentication() - ha2 = md5('%s:%s' % (method, path)).hexdigest() + content = '%s:%s' % (method, path) + if six.PY3: + content = content.encode('utf8') + ha2 = md5(content).hexdigest() if qop: chk = "%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2) else: chk = "%s:%s:%s" % (ha1, nonce, ha2) + if six.PY3: + chk = chk.encode('utf8') if response != md5(chk).hexdigest(): if nonce in self.nonce: del self.nonce[nonce] return self.build_authentication() pnc = self.nonce.get(nonce,'00000000') - if nc <= pnc: + if pnc is not None and nc <= pnc: if nonce in self.nonce: del self.nonce[nonce] return self.build_authentication(stale = True) diff --git a/tests/test_auth/test_auth_cookie.py b/tests/test_auth/test_auth_cookie.py index 3bff2d8..67465f4 100644 --- a/tests/test_auth/test_auth_cookie.py +++ b/tests/test_auth/test_auth_cookie.py @@ -10,6 +10,7 @@ try: except ImportError: # Python 2 from Cookie import SimpleCookie +import six from paste.auth import cookie from paste.wsgilib import raw_interactive, dump_environ @@ -41,7 +42,10 @@ def test_basic(key='key', val='bingles'): cookie = value.split(";")[0] (status,headers,content,errors) = \ raw_interactive(app,{'HTTP_COOKIE': cookie}) - assert ("%s: %s" % (key,val.replace("\n","\n "))) in content + expected = ("%s: %s" % (key,val.replace("\n","\n "))) + if six.PY3: + expected = expected.encode('utf8') + assert expected in content def test_roundtrip(): roundtrip = str('').join(map(chr, xrange(256))) diff --git a/tests/test_auth/test_auth_digest.py b/tests/test_auth/test_auth_digest.py index a821720..5657782 100644 --- a/tests/test_auth/test_auth_digest.py +++ b/tests/test_auth/test_auth_digest.py @@ -8,12 +8,16 @@ from paste.response import header_value from paste.httpexceptions import * from paste.httpheaders import AUTHORIZATION, WWW_AUTHENTICATE, REMOTE_USER import os +import six def application(environ, start_response): content = REMOTE_USER(environ) start_response("200 OK",(('Content-Type', 'text/plain'), ('Content-Length', len(content)))) - return content + + if six.PY3: + content = content.encode('utf8') + return [content] realm = "tag:clarkevans.com,2005:testing" @@ -46,7 +50,7 @@ def check(username, password, path="/"): assert False, "Unexpected Status: %s" % status def test_digest(): - assert 'bing' == check("bing","gnib") + assert b'bing' == check("bing","gnib") assert check("bing","bad") is None # |