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

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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
44

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-
F438 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