10000 Fix flaky base jwt tests · Muix2015/twilio-python@087dd2e · GitHub
[go: up one dir, main page]

Skip to content

Commit 087dd2e

Browse files
committed
Fix flaky base jwt tests
1 parent a4111d5 commit 087dd2e

File tree

4 files changed

+102
-76
lines changed

4 files changed

+102
-76
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ analysis:
1717
. venv/bin/activate; flake8 --ignore=E123,E126,E128,E501,W391,W291,W293,F401 tests
1818
. venv/bin/activate; flake8 --ignore=F401,W391,W291,W293 twilio --max-line-length=300
1919

20-
test: analysis
20+
test:
2121
. venv/bin/activate; \
2222
find tests -type d | xargs nosetests
2323

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#
1414
# You need to have the setuptools module installed. Try reading the setuptools
1515
# documentation: http://pypi.python.org/pypi/setuptools
16-
REQUIRES = ["httplib2 >= 0.7", "six", "pytz"]
16+
REQUIRES = ["httplib2 >= 0.7", "six", "pytz", "PyJWT == 1.4.2"]
1717

1818
if sys.version_info < (2, 6):
1919
REQUIRES.append('simplejson')

tests/unit/jwt/test_jwt.py

Lines changed: 99 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22
import jwt as jwt_lib
3-
import time
3+
import time as real_time
4< 8000 /code>4

55
from nose.tools import assert_true
66
from mock import patch
@@ -37,153 +37,164 @@ def assertIn(self, foo, bar, msg=None):
3737
return assert_true(foo in bar, msg=(msg or "%s not found in %s" % (foo, bar)))
3838

3939
def now(self):
40-
return int(time.time())
40+
return int(real_time.time())
41+
42+
def assertJwtsEqual(self, jwt, key, expected_payload=None, expected_headers=None):
43+
expected_headers = expected_headers or {}
44+
expected_payload = expected_payload or {}
45+
46+
decoded_payload = jwt_lib.decode(jwt, key, verify=False)
47+
decoded_headers = jwt_lib.get_unverified_header(jwt)
48+
49+
self.assertEqual(expected_headers, decoded_headers)
50+
self.assertEqual(expected_payload, decoded_payload)
4151

4252
@patch('time.time')
4353
def test_basic_encode(self, time_mock):
4454
time_mock.return_value = 0.0
4555

4656
jwt = DummyJwt('secret_key', 'issuer', headers={}, payload={})
47-
expected_jwt = jwt_lib.encode(
48-
{'iss': 'issuer', 'exp': 3600, 'nbf': 0},
49-
'secret_key',
50-
algorithm='HS256'
51-
)
5257

53-
self.assertEqual(expected_jwt, jwt.to_jwt())
58+
self.assertJwtsEqual(
59+
jwt.to_jwt(), 'secret_key',
60+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
61+
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0},
62+
)
5463

5564
@patch('time.time')
5665
def test_encode_with_subject(self, time_mock):
5766
time_mock.return_value = 0.0
5867

5968
jwt = DummyJwt('secret_key', 'issuer', subject='subject', headers={}, payload={})
60-
expected_jwt = jwt_lib.encode(
61-
{'iss': 'issuer', 'exp': 3600, 'nbf': 0, 'sub': 'subject'},
62-
'secret_key',
63-
algorithm='HS256'
64-
)
6569

66-
self.assertEqual(expected_jwt, jwt.to_jwt())
70+
self.assertJwtsEqual(
71+
jwt.to_jwt(), 'secret_key',
72+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
73+
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0, 'sub': 'subject'},
74+
)
6775

6876
@patch('time.time')
6977
def test_encode_custom_ttl(self, time_mock):
7078
time_mock.return_value = 0.0
7179

7280
jwt = DummyJwt('secret_key', 'issuer', ttl=10, headers={}, payload={})
73-
expected_jwt = jwt_lib.encode(
74-
{'iss': 'issuer', 'exp': 10, 'nbf': 0},
75-
'secret_key',
76-
algorithm='HS256'
77-
)
7881

79-
self.assertEqual(expected_jwt, jwt.to_jwt())
82+
self.assertJwtsEqual(
83+
jwt.to_jwt(), 'secret_key',
84+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
85+
expected_payload={'iss': 'issuer', 'exp': 10, 'nbf': 0},
86+
)
8087

8188
@patch('time.time')
8289
def test_encode_ttl_added_to_current_time(self, time_mock):
8390
time_mock.return_value = 50.0
8491

8592
jwt = DummyJwt('secret_key', 'issuer', ttl=10, headers={}, payload={})
86-
expected_jwt = jwt_lib.encode(
87-
{'iss': 'issuer', 'exp': 60, 'nbf': 50},
88-
'secret_key',
89-
algorithm='HS256'
90-
)
9193

92-
self.assertEqual(expected_jwt, jwt.to_jwt())
94+
self.assertJwtsEqual(
95+
jwt.to_jwt(), 'secret_key',
96+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
97+
expected_payload={'iss': 'issuer', 'exp': 60, 'nbf': 50},
98+
)
9399

94100
@patch('time.time')
95101
def test_encode_override_ttl(self, time_mock):
96102
time_mock.return_value = 0.0
97103

98104
jwt = DummyJwt('secret_key', 'issuer', ttl=10, headers={}, payload={})
99-
expected_jwt = jwt_lib.encode(
100-
{'iss': 'issuer', 'exp': 20, 'nbf': 0},
105+
106+
self.assertJwtsEqual(
107+
jwt.to_jwt(ttl=20),
101108
'secret_key',
102-
algorithm='HS256'
109+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
110+
expected_payload={'iss': 'issuer', 'exp': 20, 'nbf': 0},
103111
)
104112

105-
self.assertEqual(expected_jwt, jwt.to_jwt(ttl=20))
106-
107113
@patch('time.time')
108114
def test_encode_valid_until_overrides_ttl(self, time_mock):
109115
time_mock.return_value = 0.0
110116

111117
jwt = DummyJwt('secret_key', 'issuer', ttl=10, valid_until=70, headers={}, payload={})
112-
expected_jwt = jwt_lib.encode(
113-
{'iss': 'issuer', 'exp': 70, 'nbf': 0},
114-
'secret_key',
115-
algorithm='HS256'
116-
)
117118

118-
self.assertEqual(expected_jwt, jwt.to_jwt())
119+
self.assertJwtsEqual(
120+
jwt.to_jwt(), 'secret_key',
121+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
122+
expected_payload={'iss': 'issuer', 'exp': 70, 'nbf': 0},
123+
)
119124

120125
@patch('time.time')
121126
def test_encode_custom_nbf(self, time_mock):
122127
time_mock.return_value = 0.0
123128

124129
jwt = DummyJwt('secret_key', 'issuer', ttl=10, nbf=5, headers={}, payload={})
125-
expected_jwt = jwt_lib.encode(
126-
{'iss': 'issuer', 'exp': 10, 'nbf': 5},
127-
'secret_key',
128-
algorithm='HS256'
129-
)
130130

131-
self.assertEqual(expected_jwt, jwt.to_jwt())
131+
self.assertJwtsEqual(
132+
jwt.to_jwt(), 'secret_key',
133+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
134+
expected_payload={'iss': 'issuer', 'exp': 10, 'nbf': 5},
135+
)
132136

133137
@patch('time.time')
134138
def test_encode_custom_algorithm(self, time_mock):
135139
time_mock.return_value = 0.0
136140

137141
jwt = DummyJwt('secret_key', 'issuer', algorithm='HS512', headers={}, payload={})
138-
expected_jwt = jwt_lib.encode(
139-
{'iss': 'issuer', 'exp': 3600, 'nbf': 0},
140-
'secret_key',
141-
algorithm='HS512'
142-
)
143142

144-
self.assertEqual(expected_jwt, jwt.to_jwt())
143+
self.assertJwtsEqual(
144+
jwt.to_jwt(), 'secret_key',
145+
expected_headers={'typ': 'JWT', 'alg': 'HS512'},
146+
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0},
147+
)
145148

146149
@patch('time.time')
147-
def test_encode_with_headers(self, time_mock):
150+
def test_encode_override_algorithm(self, time_mock):
148151
time_mock.return_value = 0.0
149-
headers = {'sooper': 'secret'}
150152

151-
jwt = DummyJwt('secret_key', 'issuer', algorithm='HS512', headers=headers, payload={})
152-
expected_jwt = jwt_lib.encode(
153-
{'iss': 'issuer', 'exp': 3600, 'nbf': 0},
153+
jwt = DummyJwt('secret_key', 'issuer', algorithm='HS256', headers={}, payload={})
154+
155+
self.assertJwtsEqual(
156+
jwt.to_jwt(algorithm='HS512'),
154157
'secret_key',
155-
algorithm='HS512',
156-
headers=headers
158+
expected_headers={'typ': 'JWT', 'alg': 'HS512'},
159+
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0},
157160
)
158161

159-
self.assertEqual(expected_jwt, jwt.to_jwt())
162+
@patch('time.time')
163+
def test_encode_with_headers(self, time_mock):
164+
time_mock.return_value = 0.0
165+
166+
jwt = DummyJwt('secret_key', 'issuer', algorithm='HS256', headers={'sooper': 'secret'},
167+
payload={})
168+
169+
self.assertJwtsEqual(
170+
jwt.to_jwt(), 'secret_key',
171+
expected_headers={'typ': 'JWT', 'alg': 'HS256', 'sooper': 'secret'},
172+
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0},
173+
)
160174

161175
@patch('time.time')
162176
def test_encode_with_payload(self, time_mock):
163177
time_mock.return_value = 0.0
164178

165-
jwt = DummyJwt('secret_key', 'issuer', algorithm='HS512', payload={'root': 'true'})
166-
expected_jwt = jwt_lib.encode(
167-
{'iss': 'issuer', 'exp': 3600, 'nbf': 0, 'root': 'true'},
168-
'secret_key',
169-
algorithm='HS512'
170-
)
179+
jwt = DummyJwt('secret_key', 'issuer', algorithm='HS256', payload={'root': 'true'})
171180

172-
self.assertEqual(expected_jwt, jwt.to_jwt())
181+
self.assertJwtsEqual(
182+
jwt.to_jwt(), 'secret_key',
183+
expected_headers={'typ': 'JWT', 'alg': 'HS256'},
184+
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0, 'root': 'true'},
185+
)
173186

174187
@patch('time.time')
175188
def test_encode_with_payload_and_headers(self, time_mock):
176189
time_mock.return_value = 0.0
177190

178191
jwt = DummyJwt('secret_key', 'issuer', headers={'yes': 'oui'}, payload={'pay': 'me'})
179-
expected_jwt = jwt_lib.encode(
180-
{'iss': 'issuer', 'exp': 3600, 'nbf': 0, 'pay': 'me'},
181-
'secret_key',
182-
algorithm='HS256',
183-
headers={'yes': 'oui'}
184-
)
185192

186-
self.assertEqual(expected_jwt, jwt.to_jwt())
193+
self.assertJwtsEqual(
194+
jwt.to_jwt(), 'secret_key',
195+
expected_headers={'typ': 'JWT', 'alg': 'HS256', 'yes': 'oui'},
196+
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0, 'pay': 'me'},
197+
)
187198

188199
def test_encode_invalid_crypto_alg_fails(self):
189200
jwt = DummyJwt('secret_key', 'issuer', algorithm='PlzDontTouchAlgorithm')
@@ -217,6 +228,23 @@ def test_decode_bad_secret(self):
217228
jwt = DummyJwt('secret_key', 'issuer')
218229
self.assertRaises(JwtDecodeError, Jwt.from_jwt, jwt.to_jwt(), 'letmeinplz')
219230

231+
def test_decode_modified_jwt_fails(self):
232+
jwt = DummyJwt('secret_key', 'issuer')
233+
example_jwt = jwt.to_jwt().decode('utf-8')
234+
example_jwt = 'ABC' + example_jwt[3:]
235+
example_jwt = example_jwt.encode('utf-8')
236+
237+
self.assertRaises(JwtDecodeError, Jwt.from_jwt, example_jwt, 'secret_key')
238+
239+
def test_decode_validates_expiration(self):
240+
expired_jwt = DummyJwt('secret_key', 'issuer', valid_until=self.now())
241+
real_time.sleep(1)
242+
self.assertRaises(JwtDecodeError, Jwt.from_jwt, expired_jwt.to_jwt(), 'secret_key')
243+
244+
def test_decode_validates_nbf(self):
245+
expired_jwt = DummyJwt('secret_key', 'issuer', nbf=self.now() + 3600) # valid 1hr from now
246+
self.assertRaises(JwtDecodeError, Jwt.from_jwt, expired_jwt.to_jwt(), 'secret_key')
247+
220248
def test_decodes_valid_jwt(self):
221249
expiry_time = self.now() + 1000
222250
example_jwt = jwt_lib.encode(

twilio/jwt/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import simplejson as json
77

88
import time
9-
import hashlib
10-
import hmac
119
import base64
1210
from six import b
1311

@@ -149,7 +147,7 @@ def from_jwt(cls, jwt, key=''):
149147
})
150148
headers = jwt_lib.get_unverified_header(jwt)
151149
except Exception as e:
152-
raise JwtDecodeError(e.message)
150+
raise JwtDecodeError(getattr(e, 'message', str(e)))
153151

154152
return cls._from_jwt(headers, payload, key)
155153

0 commit comments

Comments
 (0)
0