Skip to content

Commit

Permalink
fix!: update code and tests for pyjwt>=2.0.0 (#560)
Browse files Browse the repository at this point in the history
Co-authored-by: Elmer Thomas <ethomas@twilio.com>
Co-authored-by: Elise Shanholtz <eshanholtz@twilio.com>
Co-authored-by: Shwetha Radhakrishna <shwetha-manvinkurke@users.noreply.github.com>
Co-authored-by: Jennifer Mah <42650198+JenniferMah@users.noreply.github.com>
Co-authored-by: Jennifer Mah <jennifermah98@gmail.com>
  • Loading branch information
6 people authored Sep 22, 2021
1 parent 4147180 commit e2df36a
Show file tree
Hide file tree
Showing 26 changed files with 72 additions and 190 deletions.
5 changes: 1 addition & 4 deletions .deepsource.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ version = 1

exclude_patterns = [
'examples/**',

# auto-generated files
'twilio/rest/**',
'twilio/twiml/**',
'tests/integration/**',

# compat files
'twilio/compat.py',
]

test_patterns = [
Expand Down
11 changes: 4 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
dist: xenial
language: python
python:
- '2.7'
- '3.4'
- '3.5'
- '3.6'
- '3.7'
- '3.8'
- '3.9'
- "3.6"
- "3.7"
- "3.8"
- "3.9"
services:
- docker
jobs:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.PHONY: clean install analysis test test-install develop docs docs-install

venv:
@python --version || (echo "Python is not installed, please install Python 2 or Python 3"; exit 1);
@python --version || (echo "Python is not installed, Python 3.6+"; exit 1);
virtualenv --python=python venv

install: venv
Expand Down
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ Please consult the [official migration guide](https://www.twilio.com/docs/librar

This library supports the following Python implementations:

* Python 2.7
* Python 3.4
* Python 3.5
* Python 3.6
* Python 3.7
* Python 3.8
Expand Down
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
flake8
mock
nose
six
requests>=2.0.0
PyJWT==1.7.1
PyJWT>=2.0.0, <3.0.0
twine
17 changes: 3 additions & 14 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from __future__ import with_statement
from setuptools import setup, find_packages

with open('README.md') as f:
Expand All @@ -20,19 +19,12 @@
author_email="help@twilio.com",
url="https://github.com/twilio/twilio-python/",
keywords=["twilio", "twiml"],
python_requires='>=3.6.0',
install_requires=[
"six",
"pytz",
"PyJWT == 1.7.1",
"requests >= 2.0.0",
"PyJWT >= 2.0.0, < 3.0.0",
],
extras_require={
':python_version<"3.0"': [
"requests[security] >= 2.0.0",
],
':python_version>="3.0"': [
"requests >= 2.0.0"
],
},
packages=find_packages(exclude=['tests', 'tests.*']),
include_package_data=True,
classifiers=[
Expand All @@ -41,9 +33,6 @@
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
Expand Down
4 changes: 1 addition & 3 deletions tests/unit/jwt/test_client_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ def test_jwt_signing(self):
private_key = private_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption())

jwt = ClientValidationJwt('AC123', 'SK123', 'CR123', private_key, vp)
decoded = Jwt.from_jwt(jwt.to_jwt(), public_key)
decoded = ClientValidationJwt.from_jwt(jwt.to_jwt(), public_key)

self.assertDictContainsSubset({
'hrh': 'authorization;host',
Expand All @@ -282,5 +282,3 @@ def test_jwt_signing(self):
'cty': 'twilio-pkrv;v=1',
'kid': 'CR123'
}, decoded.headers)


53 changes: 15 additions & 38 deletions tests/unit/jwt/test_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@

class DummyJwt(Jwt):
"""Jwt implementation that allows setting arbitrary payload and headers for testing."""
def __init__(self, secret_key, issuer, subject=None, algorithm='HS256', nbf=Jwt.GENERATE,
ttl=3600, valid_until=None, headers=None, payload=None):

ALGORITHM = 'HS256'

def __init__(self, secret_key, issuer, subject=None, algorithm=None,
nbf=Jwt.GENERATE, ttl=3600, valid_until=None, headers=None,
payload=None):
super(DummyJwt, self).__init__(
secret_key=secret_key,
issuer=issuer,
subject=subject,
algorithm=algorithm,
algorithm=algorithm or self.ALGORITHM,
nbf=nbf,
ttl=ttl,
valid_until=valid_until
Expand All @@ -43,7 +47,7 @@ def assertJwtsEqual(self, jwt, key, expected_payload=None, expected_headers=None
expected_headers = expected_headers or {}
expected_payload = expected_payload or {}

decoded_payload = jwt_lib.decode(jwt, key, verify=False)
decoded_payload = jwt_lib.decode(jwt, key, algorithms=["HS256"], options={"verify_signature": False})
decoded_headers = jwt_lib.get_unverified_header(jwt)

self.assertEqual(expected_headers, decoded_headers)
Expand Down Expand Up @@ -146,37 +150,11 @@ def test_encode_custom_nbf(self, time_mock):
expected_payload={'iss': 'issuer', 'exp': 10, 'nbf': 5},
)

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

jwt = DummyJwt('secret_key', 'issuer', algorithm='HS512', headers={}, payload={})

self.assertJwtsEqual(
jwt.to_jwt(), 'secret_key',
expected_headers={'typ': 'JWT', 'alg': 'HS512'},
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0},
)

@patch('time.time')
def test_encode_override_algorithm(self, time_mock):
time_mock.return_value = 0.0

jwt = DummyJwt('secret_key', 'issuer', algorithm='HS256', headers={}, payload={})

self.assertJwtsEqual(
jwt.to_jwt(algorithm='HS512'),
'secret_key',
expected_headers={'typ': 'JWT', 'alg': 'HS512'},
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0},
)

@patch('time.time')
def test_encode_with_headers(self, time_mock):
time_mock.return_value = 0.0

jwt = DummyJwt('secret_key', 'issuer', algorithm='HS256', headers={'sooper': 'secret'},
payload={})
jwt = DummyJwt('secret_key', 'issuer', headers={'sooper': 'secret'}, payload={})

self.assertJwtsEqual(
jwt.to_jwt(), 'secret_key',
Expand All @@ -188,7 +166,7 @@ def test_encode_with_headers(self, time_mock):
def test_encode_with_payload(self, time_mock):
time_mock.return_value = 0.0

jwt = DummyJwt('secret_key', 'issuer', algorithm='HS256', payload={'root': 'true'})
jwt = DummyJwt('secret_key', 'issuer', payload={'root': 'true'})

self.assertJwtsEqual(
jwt.to_jwt(), 'secret_key',
Expand All @@ -208,10 +186,6 @@ def test_encode_with_payload_and_headers(self, time_mock):
expected_payload={'iss': 'issuer', 'exp': 3600, 'nbf': 0, 'pay': 'me'},
)

def test_encode_invalid_crypto_alg_fails(self):
jwt = DummyJwt('secret_key', 'issuer', algorithm='PlzDontTouchAlgorithm')
self.assertRaises(NotImplementedError, jwt.to_jwt)

def test_encode_no_key_fails(self):
jwt = DummyJwt(None, 'issuer')
self.assertRaises(ValueError, jwt.to_jwt)
Expand All @@ -236,15 +210,18 @@ def test_encode_decode(self):
'sick': 'sick',
}, decoded_jwt.payload)

def test_encode_decode_mismatched_algorithms(self):
jwt = DummyJwt('secret_key', 'issuer', algorithm='HS512', subject='hey', payload={'sick': 'sick'})
self.assertRaises(JwtDecodeError, Jwt.from_jwt, jwt.to_jwt())

def test_decode_bad_secret(self):
jwt = DummyJwt('secret_key', 'issuer')
self.assertRaises(JwtDecodeError, Jwt.from_jwt, jwt.to_jwt(), 'letmeinplz')

def test_decode_modified_jwt_fails(self):
jwt = DummyJwt('secret_key', 'issuer')
example_jwt = jwt.to_jwt().decode('utf-8')
example_jwt = jwt.to_jwt()
example_jwt = 'ABC' + example_jwt[3:]
example_jwt = example_jwt.encode('utf-8')

self.assertRaises(JwtDecodeError, Jwt.from_jwt, example_jwt, 'secret_key')

Expand Down
14 changes: 2 additions & 12 deletions tests/unit/test_request_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import unittest

from nose.tools import assert_equal, assert_true
from six import b, u

from twilio.request_validator import RequestValidator

Expand All @@ -26,22 +25,13 @@ def setUp(self):
self.bodyHash = "0a1ff7634d9ab3b95db5c9a2dfe9416e41502b283a80c7cf19632632f96e6620"
self.uriWithBody = self.uri + "&bodySHA256=" + self.bodyHash

def test_compute_signature_bytecode(self):
expected = b(self.expected)
signature = self.validator.compute_signature(self.uri,
self.params,
utf=False)
assert_equal(signature, expected)

def test_compute_signature(self):
expected = (self.expected)
signature = self.validator.compute_signature(self.uri,
self.params,
utf=True)
signature = self.validator.compute_signature(self.uri, self.params)
assert_equal(signature, expected)

def test_compute_hash_unicode(self):
expected = u(self.bodyHash)
expected = self.bodyHash
body_hash = self.validator.compute_hash(self.body)

assert_equal(expected, body_hash)
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/twiml/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import unittest

from nose.tools import raises
from six import text_type

from twilio.twiml import (
format_language,
Expand All @@ -13,7 +12,7 @@

class TwilioTest(unittest.TestCase):
def strip(self, xml):
return text_type(xml)
return str(xml)

@raises(TwiMLException)
def test_append_fail(self):
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/twiml/test_voice_response.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from nose.tools import assert_equal
from six import u
from tests.unit.twiml import TwilioTest
from twilio.twiml.voice_response import VoiceResponse, Dial, Enqueue, Gather

Expand Down Expand Up @@ -82,7 +81,7 @@ def test_say_hello_world(self):
def test_say_french(self):
""" should say hello monkey """
r = VoiceResponse()
r.say(u('n\xe9cessaire et d\'autres'))
r.say('n\xe9cessaire et d\'autres')

assert_equal(
self.strip(r),
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py27, py3{4,5,6,7,8,9}, pypy
envlist = py3{6,7,8,9}, pypy
skip_missing_interpreters = true

[testenv]
Expand Down
10 changes: 4 additions & 6 deletions twilio/base/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
import sys

from six import u


class TwilioException(Exception):
pass
Expand Down Expand Up @@ -32,16 +30,16 @@ def __str__(self):
""" Try to pretty-print the exception, if this is going on screen. """

def red(words):
return u("\033[31m\033[49m%s\033[0m") % words
return "\033[31m\033[49m%s\033[0m" % words

def white(words):
return u("\033[37m\033[49m%s\033[0m") % words
return "\033[37m\033[49m%s\033[0m" % words

def blue(words):
return u("\033[34m\033[49m%s\033[0m") % words
return "\033[34m\033[49m%s\033[0m" % words

def teal(words):
return u("\033[36m\033[49m%s\033[0m") % words
return "\033[36m\033[49m%s\033[0m" % words

def get_uri(code):
return "https://www.twilio.com/docs/errors/{0}".format(code)
Expand Down
3 changes: 1 addition & 2 deletions twilio/base/values.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from six import iteritems
unset = object()


Expand All @@ -9,4 +8,4 @@ def of(d):
:param dict d: A dict to strip.
:return dict: A dict with unset values removed.
"""
return {k: v for k, v in iteritems(d) if v != unset}
return {k: v for k, v in d.items() if v != unset}
17 changes: 0 additions & 17 deletions twilio/compat.py

This file was deleted.

2 changes: 1 addition & 1 deletion twilio/http/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from requests import Request, Session, hooks
from requests.adapters import HTTPAdapter
from twilio.compat import urlencode
from urllib.parse import urlencode
from twilio.http import HttpClient
from twilio.http.request import Request as TwilioRequest
from twilio.http.response import Response
Expand Down
2 changes: 1 addition & 1 deletion twilio/http/request.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from twilio.compat import urlencode
from urllib.parse import urlencode


class Request(object):
Expand Down
2 changes: 1 addition & 1 deletion twilio/http/validation_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from requests import Request, Session

from twilio.base.exceptions import TwilioRestException
from twilio.compat import urlparse
from urllib.parse import urlparse
from twilio.http import HttpClient
from twilio.http.response import Response
from twilio.jwt.validation import ClientValidationJwt
Expand Down
Loading

0 comments on commit e2df36a

Please sign in to comment.