Skip to content

Commit a6e5a8b

Browse files
authored
Merge pull request #3288 from OpenTreeMap/jf/fix-tests-2to3
First pass at fixing tests from Py2->3 upgrade
2 parents c1fb9fb + d3b4fb9 commit a6e5a8b

File tree

21 files changed

+151
-98
lines changed

21 files changed

+151
-98
lines changed

opentreemap/api/auth.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import urllib.parse
1010
import urllib.error
1111

12-
from django.http import HttpResponse
12+
from django.http import HttpResponse, RawPostDataException
1313
from django.contrib.auth import authenticate
1414

1515

@@ -37,19 +37,32 @@ def get_signature_for_request(request, secret_key):
3737

3838
sign_string = '\n'.join([httpverb, hostheader, request_uri, paramstr])
3939

40-
# Sometimes reeading from body fails, so try reading as a file-like
40+
# Sometimes reading from body fails, so try reading as a file-like object
4141
try:
42-
body_encoded = base64.b64encode(request.body)
43-
except:
44-
body_encoded = base64.b64encode(request.read())
42+
body_decoded = base64.b64encode(request.body).decode()
43+
except RawPostDataException:
44+
body_decoded = base64.b64encode(request.read()).decode()
4545

46-
if body_encoded:
47-
sign_string += body_encoded
46+
if body_decoded:
47+
sign_string += body_decoded
48+
49+
try:
50+
binary_secret_key = secret_key.encode()
51+
except (AttributeError, UnicodeEncodeError):
52+
binary_secret_key = secret_key
4853

4954
sig = base64.b64encode(
50-
hmac.new(secret_key, sign_string, hashlib.sha256).digest())
55+
hmac.new(
56+
binary_secret_key,
57+
sign_string.encode(),
58+
hashlib.sha256
59+
).digest()
60+
)
61+
62+
if sig is None:
63+
return sig
5164

52-
return sig
65+
return sig.decode()
5366

5467

5568
def create_401unauthorized(body="Unauthorized"):
@@ -67,19 +80,38 @@ def firstmatch(regx, strg):
6780
return m.group(1)
6881

6982

70-
def decodebasicauth(strg):
71-
if strg is None:
83+
def split_basicauth(strg):
84+
"""
85+
Returns username, password from decoded,
86+
stringified, basic auth credentials
87+
"""
88+
if strg is None or len(strg) == 0:
7289
return None
7390
else:
74-
m = re.match(r'([^:]*)\:(.*)', base64.decodestring(strg))
91+
m = re.match(r'([^:]*)\:(.*)', strg)
7592
if m is not None:
7693
return (m.group(1), m.group(2))
7794
else:
7895
return None
7996

8097

8198
def parse_basicauth(authstr):
82-
auth = decodebasicauth(firstmatch('Basic (.*)', authstr))
99+
string_wrapped_binary_credentials = firstmatch("Basic (.*)", authstr)
100+
if string_wrapped_binary_credentials is None:
101+
return None
102+
103+
# tease bytes-like object out of string, i.e. "b'credentials'"
104+
reg_exp = r"'(.*?)\'"
105+
parsed_credentials = re.search(r"'(.*?)\'", string_wrapped_binary_credentials)
106+
str_credentials = parsed_credentials.groups()[0]
107+
108+
# decode the binary encoded credentials
109+
decoded_str_credentials = base64.decodebytes(
110+
bytes(str_credentials, 'utf-8')
111+
).decode()
112+
113+
auth = split_basicauth(decoded_str_credentials)
114+
83115
if auth is None:
84116
return None
85117
else:

opentreemap/api/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def __unicode__(self):
3131
def create(clz, user=None):
3232
secret_key = base64.urlsafe_b64encode(os.urandom(64))
3333
access_key = base64.urlsafe_b64encode(uuid.uuid4().bytes)\
34-
.replace('=', '')
34+
.replace(b'=', b'')
3535

3636
return APIAccessCredential.objects.create(
3737
user=user, access_key=access_key, secret_key=secret_key)

opentreemap/api/plots.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.shortcuts import get_object_or_404
99
from django.contrib.gis.geos import Point
1010
from django.contrib.gis.measure import D
11+
from django.http import RawPostDataException
1112

1213
from django_tinsel.exceptions import HttpBadRequestException
1314

@@ -50,8 +51,7 @@ def plots_closest_to_point(request, instance, lat, lng):
5051
distance = float(request.GET.get(
5152
'distance', settings.MAP_CLICK_RADIUS))
5253
except ValueError:
53-
raise HttpBadRequestException(
54-
'The distance parameter must be a number')
54+
raise HttpBadRequestException('The distance parameter must be a number')
5555

5656
plots = Plot.objects.distance(point)\
5757
.filter(instance=instance)\
@@ -72,7 +72,16 @@ def update_or_create_plot(request, instance, plot_id=None):
7272
# The API communicates via nested dictionaries but
7373
# our internal functions prefer dotted pairs (which
7474
# is what inline edit form users)
75-
request_dict = json.loads(request.body)
75+
# Sometimes reading from body fails, so try reading as a file-like object
76+
try:
77+
body_decoded = request.body.decode()
78+
except RawPostDataException:
79+
body_decoded = request.read().decode()
80+
81+
if body_decoded:
82+
request_dict = json.loads(body_decoded)
83+
else:
84+
request_dict = {}
7685

7786
data = {}
7887

opentreemap/api/tests.py

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33

4-
from io import StringIO
4+
from io import BytesIO
55
from json import loads, dumps
66
from urllib.parse import urlparse
77

@@ -14,6 +14,7 @@
1414
import datetime
1515
import psycopg2
1616
from unittest.case import skip
17+
from django_tinsel.exceptions import HttpBadRequestException
1718

1819
from django.db import connection
1920
from django.contrib.auth.models import AnonymousUser
@@ -96,11 +97,11 @@ def send_json_body(url, body_object, client, method, user=None):
9697
are posting form data, so you need to manually setup the parameters
9798
to override that default functionality.
9899
"""
99-
body_string = dumps(body_object)
100-
body_stream = StringIO(body_string)
100+
body_binary_string = dumps(body_object).encode()
101+
body_stream = BytesIO(body_binary_string)
101102
parsed_url = urlparse(url)
102103
client_params = {
103-
'CONTENT_LENGTH': len(body_string),
104+
'CONTENT_LENGTH': len(body_binary_string),
104105
'CONTENT_TYPE': 'application/json',
105106
'PATH_INFO': _get_path(parsed_url),
106107
'QUERY_STRING': parsed_url[4],
@@ -375,8 +376,8 @@ def test_locations_plots_endpoint_max_plots_param_must_be_a_number(self):
375376
API_PFX, self.instance.url_name))
376377
self.assertEqual(response.status_code, 400)
377378
self.assertEqual(response.content,
378-
'The max_plots parameter must be '
379-
'a number between 1 and 500')
379+
b'The max_plots parameter must be '
380+
b'a number between 1 and 500')
380381

381382
def test_locations_plots_max_plots_param_cannot_be_greater_than_500(self):
382383
response = get_signed(
@@ -385,8 +386,8 @@ def test_locations_plots_max_plots_param_cannot_be_greater_than_500(self):
385386
API_PFX, self.instance.url_name))
386387
self.assertEqual(response.status_code, 400)
387388
self.assertEqual(response.content,
388-
'The max_plots parameter must be '
389-
'a number between 1 and 500')
389+
b'The max_plots parameter must be '
390+
b'a number between 1 and 500')
390391
response = get_signed(
391392
self.client,
392393
"%s/instance/%s/locations/0,0/plots?max_plots=500" %
@@ -402,8 +403,8 @@ def test_locations_plots_endpoint_max_plots_param_cannot_be_less_than_1(
402403

403404
self.assertEqual(response.status_code, 400)
404405
self.assertEqual(response.content,
405-
'The max_plots parameter must be a '
406-
'number between 1 and 500')
406+
b'The max_plots parameter must be a '
407+
b'number between 1 and 500')
407408
response = get_signed(
408409
self.client,
409410
"%s/instance/%s/locations/0,0/plots?max_plots=1" %
@@ -419,7 +420,7 @@ def test_locations_plots_endpoint_distance_param_must_be_a_number(self):
419420

420421
self.assertEqual(response.status_code, 400)
421422
self.assertEqual(response.content,
422-
'The distance parameter must be a number')
423+
b'The distance parameter must be a number')
423424

424425
response = get_signed(
425426
self.client,
@@ -472,7 +473,7 @@ def test_create_plot_with_tree(self):
472473
data, self.client, self.user)
473474

474475
self.assertEqual(200, response.status_code,
475-
"Create failed:" + response.content)
476+
"Create failed:" + response.content.decode())
476477

477478
# Assert that a plot was added
478479
self.assertEqual(plot_count + 1, Plot.objects.count())
@@ -509,7 +510,7 @@ def test_create_plot_with_invalid_tree_returns_400(self):
509510
self.assertEqual(400,
510511
response.status_code,
511512
"Expected creating a million foot "
512-
"tall tree to return 400:" + response.content)
513+
"tall tree to return 400:" + response.content.decode())
513514

514515
body_dict = loads(response.content)
515516
self.assertTrue('fieldErrors' in body_dict,
@@ -548,7 +549,7 @@ def test_create_plot_with_geometry(self):
548549
data, self.client, self.user)
549550

550551
self.assertEqual(200, response.status_code,
551-
"Create failed:" + response.content)
552+
"Create failed:" + response.content.decode())
552553

553554
# Assert that a plot was added
554555
self.assertEqual(plot_count + 1, Plot.objects.count())
@@ -1334,8 +1335,8 @@ def setUp(self):
13341335
'sort_key': 'Date'}
13351336
]
13361337
self.instance.save()
1337-
self.instance.logo.save(Instance.test_png_name,
1338-
File(open(Instance.test_png_path, 'r')))
1338+
with open(Instance.test_png_path, 'rb') as f:
1339+
self.instance.logo.save(Instance.test_png_name, f)
13391340

13401341
def test_returns_config_colors(self):
13411342
request = sign_request_as_user(make_request(), self.user)
@@ -1568,7 +1569,7 @@ def _test_post_photo(self, path):
15681569
self.instance.url_name,
15691570
plot_id)
15701571

1571-
with open(path) as img:
1572+
with open(path, 'rb') as img:
15721573
req = self.factory.post(
15731574
url, {'name': 'afile', 'file': img})
15741575

@@ -1621,7 +1622,7 @@ def testUploadPhoto(self):
16211622
url = reverse('update_user_photo', kwargs={'version': 3,
16221623
'user_id': peon.pk})
16231624

1624-
with open(TreePhotoTest.test_jpeg_path) as img:
1625+
with open(TreePhotoTest.test_jpeg_path, 'rb') as img:
16251626
req = self.factory.post(
16261627
url, {'name': 'afile', 'file': img})
16271628

@@ -1648,7 +1649,7 @@ def testCanOnlyUploadAsSelf(self):
16481649
grunt = make_user(username='grunt', password='pw')
16491650
grunt.save()
16501651

1651-
with open(TreePhotoTest.test_jpeg_path) as img:
1652+
with open(TreePhotoTest.test_jpeg_path, 'rb') as img:
16521653
req = self.factory.post(
16531654
url, {'name': 'afile', 'file': img})
16541655

@@ -1883,7 +1884,7 @@ def testTimestampVoidsSignature(self):
18831884
acred = APIAccessCredential.create()
18841885
url = ('http://testserver.com/test/blah?'
18851886
'timestamp=%%s&'
1886-
'k1=4&k2=a&access_key=%s' % acred.access_key)
1887+
'k1=4&k2=a&access_key=%s' % acred.access_key.decode())
18871888

18881889
curtime = datetime.datetime.now()
18891890
invalid = curtime - datetime.timedelta(minutes=100)
@@ -1902,7 +1903,7 @@ def testPOSTBodyChangesSig(self):
19021903
url = "%s/i/plots/1/tree/photo" % API_PFX
19031904

19041905
def get_sig(path):
1905-
with open(path) as img:
1906+
with open(path, 'rb') as img:
19061907
req = self.factory.post(
19071908
url, {'name': 'afile', 'file': img})
19081909

@@ -1949,14 +1950,14 @@ def testMalformedTimestamp(self):
19491950

19501951
url = ('http://testserver.com/test/blah?'
19511952
'timestamp=%%s&'
1952-
'k1=4&k2=a&access_key=%s' % acred.access_key)
1953+
'k1=4&k2=a&access_key=%s' % acred.access_key.decode())
19531954

19541955
req = self.sign_and_send(url % ('%sFAIL' % timestamp),
19551956
acred.secret_key)
19561957

19571958
self.assertEqual(req.status_code, 400)
19581959

1959-
req = self.sign_and_send(url % timestamp, acred.secret_key)
1960+
req = self.sign_and_send(url % timestamp, acred.secret_key.decode())
19601961

19611962
self.assertRequestWasSuccess(req)
19621963

@@ -1972,7 +1973,7 @@ def testMissingAccessKey(self):
19721973

19731974
self.assertEqual(req.status_code, 400)
19741975

1975-
req = self.sign_and_send('%s&access_key=%s' % (url, acred.access_key),
1976+
req = self.sign_and_send('%s&access_key=%s' % (url, acred.access_key.decode()),
19761977
acred.secret_key)
19771978

19781979
self.assertRequestWasSuccess(req)
@@ -1987,9 +1988,8 @@ def testAuthenticatesAsUser(self):
19871988
req = self.sign_and_send('http://testserver.com/test/blah?'
19881989
'timestamp=%s&'
19891990
'k1=4&k2=a&access_key=%s' %
1990-
(timestamp, acred.access_key),
1991-
acred.secret_key)
1992-
1991+
(timestamp, acred.access_key.decode()),
1992+
acred.secret_key.decode())
19931993
self.assertEqual(req.user.pk, peon.pk)
19941994

19951995

@@ -2003,7 +2003,7 @@ def test_401(self):
20032003
self.assertEqual(ret.status_code, 401)
20042004

20052005
def test_ok(self):
2006-
auth = base64.b64encode("jim:password")
2006+
auth = base64.b64encode(b"jim:password")
20072007
withauth = {"HTTP_AUTHORIZATION": "Basic %s" % auth}
20082008

20092009
ret = get_signed(self.client, "%s/user" % API_PFX, **withauth)
@@ -2015,14 +2015,14 @@ def test_malformed_auth(self):
20152015
ret = get_signed(self.client, "%s/user" % API_PFX, **withauth)
20162016
self.assertEqual(ret.status_code, 401)
20172017

2018-
auth = base64.b64encode("foobar")
2018+
auth = base64.b64encode(b"foobar")
20192019
withauth = {"HTTP_AUTHORIZATION": "Basic %s" % auth}
20202020

20212021
ret = get_signed(self.client, "%s/user" % API_PFX, **withauth)
20222022
self.assertEqual(ret.status_code, 401)
20232023

20242024
def test_bad_cred(self):
2025-
auth = base64.b64encode("jim:passwordz")
2025+
auth = base64.b64encode(b"jim:passwordz")
20262026
withauth = {"HTTP_AUTHORIZATION": "Basic %s" % auth}
20272027

20282028
ret = get_signed(self.client, "%s/user" % API_PFX, **withauth)
@@ -2034,7 +2034,7 @@ def test_user_has_rep(self):
20342034
ijim.reputation = 1001
20352035
ijim.save()
20362036

2037-
auth = base64.b64encode("jim:password")
2037+
auth = base64.b64encode(b"jim:password")
20382038
withauth = dict(list(self.sign.items()) +
20392039
[("HTTP_AUTHORIZATION", "Basic %s" % auth)])
20402040

opentreemap/api/user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def wrapper(request, *args, **kwargs):
138138
# You can't directly set a new request body
139139
# (http://stackoverflow.com/a/22745559)
140140
request._body = body
141-
request._stream = BytesIO(body)
141+
request._stream = BytesIO(body.encode())
142142

143143
return user_view_fn(request, *args, **kwargs)
144144

0 commit comments

Comments
 (0)