Commit 059522df authored by Senthil Kumaran's avatar Senthil Kumaran

- Issue #13642: Unquote before b64encoding user:password during Basic

  Authentication. Patch contributed by Joonas Kuorilehto and Michele Orrù.
parent f51f7987
...@@ -3,13 +3,16 @@ ...@@ -3,13 +3,16 @@
import urllib import urllib
import httplib import httplib
import unittest import unittest
from test import test_support
import os import os
import sys import sys
import mimetools import mimetools
import tempfile import tempfile
import StringIO import StringIO
from test import test_support
from base64 import b64encode
def hexescape(char): def hexescape(char):
"""Escape char as RFC 2396 specifies""" """Escape char as RFC 2396 specifies"""
hex_repr = hex(ord(char))[2:].upper() hex_repr = hex(ord(char))[2:].upper()
...@@ -22,8 +25,9 @@ class FakeHTTPMixin(object): ...@@ -22,8 +25,9 @@ class FakeHTTPMixin(object):
def fakehttp(self, fakedata): def fakehttp(self, fakedata):
class FakeSocket(StringIO.StringIO): class FakeSocket(StringIO.StringIO):
def sendall(self, str): def sendall(self, data):
pass FakeHTTPConnection.buf = data
def makefile(self, *args, **kwds): def makefile(self, *args, **kwds):
return self return self
...@@ -38,9 +42,15 @@ class FakeHTTPMixin(object): ...@@ -38,9 +42,15 @@ class FakeHTTPMixin(object):
return StringIO.StringIO.readline(self, length) return StringIO.StringIO.readline(self, length)
class FakeHTTPConnection(httplib.HTTPConnection): class FakeHTTPConnection(httplib.HTTPConnection):
# buffer to store data for verification in urlopen tests.
buf = ""
def connect(self): def connect(self):
self.sock = FakeSocket(fakedata) self.sock = FakeSocket(fakedata)
assert httplib.HTTP._connection_class == httplib.HTTPConnection assert httplib.HTTP._connection_class == httplib.HTTPConnection
httplib.HTTP._connection_class = FakeHTTPConnection httplib.HTTP._connection_class = FakeHTTPConnection
def unfakehttp(self): def unfakehttp(self):
...@@ -209,6 +219,41 @@ Content-Type: text/html; charset=iso-8859-1 ...@@ -209,6 +219,41 @@ Content-Type: text/html; charset=iso-8859-1
finally: finally:
self.unfakehttp() self.unfakehttp()
def test_userpass_inurl(self):
self.fakehttp('Hello!')
try:
fakehttp_wrapper = httplib.HTTP._connection_class
fp = urllib.urlopen("http://user:pass@python.org/")
authorization = ("Authorization: Basic %s\r\n" %
b64encode('user:pass'))
# The authorization header must be in place
self.assertIn(authorization, fakehttp_wrapper.buf)
self.assertEqual(fp.readline(), "Hello!")
self.assertEqual(fp.readline(), "")
self.assertEqual(fp.geturl(), 'http://user:pass@python.org/')
self.assertEqual(fp.getcode(), 200)
finally:
self.unfakehttp()
def test_userpass_with_spaces_inurl(self):
self.fakehttp('Hello!')
try:
url = "http://a b:c d@python.org/"
fakehttp_wrapper = httplib.HTTP._connection_class
authorization = ("Authorization: Basic %s\r\n" %
b64encode('a b:c d'))
fp = urllib.urlopen(url)
# The authorization header must be in place
self.assertIn(authorization, fakehttp_wrapper.buf)
self.assertEqual(fp.readline(), "Hello!")
self.assertEqual(fp.readline(), "")
# the spaces are quoted in URL so no match
self.assertNotEqual(fp.geturl(), url)
self.assertEqual(fp.getcode(), 200)
finally:
self.unfakehttp()
class urlretrieve_FileTests(unittest.TestCase): class urlretrieve_FileTests(unittest.TestCase):
"""Test urllib.urlretrieve() on local files""" """Test urllib.urlretrieve() on local files"""
...@@ -716,6 +761,9 @@ class Utility_Tests(unittest.TestCase): ...@@ -716,6 +761,9 @@ class Utility_Tests(unittest.TestCase):
self.assertEqual(('user', 'a\fb'),urllib.splitpasswd('user:a\fb')) self.assertEqual(('user', 'a\fb'),urllib.splitpasswd('user:a\fb'))
self.assertEqual(('user', 'a\vb'),urllib.splitpasswd('user:a\vb')) self.assertEqual(('user', 'a\vb'),urllib.splitpasswd('user:a\vb'))
self.assertEqual(('user', 'a:b'),urllib.splitpasswd('user:a:b')) self.assertEqual(('user', 'a:b'),urllib.splitpasswd('user:a:b'))
self.assertEqual(('user', 'a b'),urllib.splitpasswd('user:a b'))
self.assertEqual(('user 2', 'ab'),urllib.splitpasswd('user 2:ab'))
self.assertEqual(('user+1', 'a+b'),urllib.splitpasswd('user+1:a+b'))
class URLopener_Tests(unittest.TestCase): class URLopener_Tests(unittest.TestCase):
......
...@@ -27,6 +27,8 @@ import socket ...@@ -27,6 +27,8 @@ import socket
import os import os
import time import time
import sys import sys
import base64
from urlparse import urljoin as basejoin from urlparse import urljoin as basejoin
__all__ = ["urlopen", "URLopener", "FancyURLopener", "urlretrieve", __all__ = ["urlopen", "URLopener", "FancyURLopener", "urlretrieve",
...@@ -318,13 +320,13 @@ class URLopener: ...@@ -318,13 +320,13 @@ class URLopener:
if not host: raise IOError, ('http error', 'no host given') if not host: raise IOError, ('http error', 'no host given')
if proxy_passwd: if proxy_passwd:
import base64 proxy_passwd = unquote(proxy_passwd)
proxy_auth = base64.b64encode(proxy_passwd).strip() proxy_auth = base64.b64encode(proxy_passwd).strip()
else: else:
proxy_auth = None proxy_auth = None
if user_passwd: if user_passwd:
import base64 user_passwd = unquote(user_passwd)
auth = base64.b64encode(user_passwd).strip() auth = base64.b64encode(user_passwd).strip()
else: else:
auth = None auth = None
...@@ -408,12 +410,12 @@ class URLopener: ...@@ -408,12 +410,12 @@ class URLopener:
#print "proxy via https:", host, selector #print "proxy via https:", host, selector
if not host: raise IOError, ('https error', 'no host given') if not host: raise IOError, ('https error', 'no host given')
if proxy_passwd: if proxy_passwd:
import base64 proxy_passwd = unquote(proxy_passwd)
proxy_auth = base64.b64encode(proxy_passwd).strip() proxy_auth = base64.b64encode(proxy_passwd).strip()
else: else:
proxy_auth = None proxy_auth = None
if user_passwd: if user_passwd:
import base64 user_passwd = unquote(user_passwd)
auth = base64.b64encode(user_passwd).strip() auth = base64.b64encode(user_passwd).strip()
else: else:
auth = None auth = None
...@@ -589,7 +591,6 @@ class URLopener: ...@@ -589,7 +591,6 @@ class URLopener:
time.gmtime(time.time()))) time.gmtime(time.time())))
msg.append('Content-type: %s' % type) msg.append('Content-type: %s' % type)
if encoding == 'base64': if encoding == 'base64':
import base64
data = base64.decodestring(data) data = base64.decodestring(data)
else: else:
data = unquote(data) data = unquote(data)
......
...@@ -89,6 +89,9 @@ Core and Builtins ...@@ -89,6 +89,9 @@ Core and Builtins
Library Library
------- -------
- Issue #13642: Unquote before b64encoding user:password during Basic
Authentication. Patch contributed by Joonas Kuorilehto and Michele Orrù.
- Issue #13636: Weak ciphers are now disabled by default in the ssl module - Issue #13636: Weak ciphers are now disabled by default in the ssl module
(except when SSLv2 is explicitly asked for). (except when SSLv2 is explicitly asked for).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment