Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Unify JWT based helpers #306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ python:
- "3.3"
- "3.4"
install:
- pip install .
- pip install -r requirements.txt
- pip install -r tests/requirements.txt
- make install
- make test-install
script:
- make ci
- make test
7 changes: 1 addition & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,12 @@ develop: venv

analysis:
. venv/bin/activate; flake8 --ignore=E123,E126,E128,E501,W391,W291,W293,F401 tests
. venv/bin/activate; flake8 --ignore=F401,W391,W291,W293 twilio --max-line-length=300
. venv/bin/activate; flake8 --ignore=E402,F401,W391,W291,W293 twilio --max-line-length=300

test: analysis
. venv/bin/activate; \
find tests -type d | xargs nosetests

ci:
flake8 --ignore=E123,E126,E128,E501,W391,W291,W293,F401 tests
flake8 --ignore=F401,W391,W291,W293 twilio --max-line-length=300
find tests -type d | xargs nosetests

cover:
. venv/bin/activate; \
find tests -type d | xargs nosetests --with-coverage --cover-inclusive --cover-erase --cover-package=twilio
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
six
httplib2
socksipy-branch
PyJWT==1.4.2
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#
# You need to have the setuptools module installed. Try reading the setuptools
# documentation: http://pypi.python.org/pypi/setuptools
REQUIRES = ["httplib2 >= 0.7", "six", "pytz"]
REQUIRES = ["httplib2 >= 0.7", "six", "pytz", "PyJWT == 1.4.2"]

if sys.version_info < (2, 6):
REQUIRES.append('simplejson')
Expand Down
132 changes: 86 additions & 46 deletions tests/unit/jwt/test_access_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@

from datetime import datetime
from nose.tools import assert_equal
from twilio.jwt import decode
from twilio.jwt.access_token import AccessToken, ConversationsGrant, IpMessagingGrant, SyncGrant, VoiceGrant, VideoGrant

from twilio.jwt.access_token import AccessToken
from twilio.jwt.access_token.grants import (
ConversationsGrant,
IpMessagingGrant,
SyncGrant,
VoiceGrant,
VideoGrant
)

ACCOUNT_SID = 'AC123'
SIGNING_KEY_SID = 'SK123'
Expand Down Expand Up @@ -38,102 +45,109 @@ def _validate_claims(self, payload):

def test_empty_grants(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
token = str(scat)
token = scat.to_jwt()

assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal({}, payload['grants'])
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal({}, decoded_token.payload['grants'])

def test_nbf(self):
now = int(time.mktime(datetime.now().timetuple()))
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret', nbf=now)
token = str(scat)
token = scat.to_jwt()

assert_is_not_none(token)
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(now, decoded_token.nbf)

def test_headers(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
token = scat.to_jwt()
assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal(now, payload['nbf'])
decoded_token = AccessToken.from_jwt(token, 'secret')
self.assertEqual(decoded_token.headers['cty'], 'twilio-fpa;v=1')

def test_identity(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret', identity='[email protected]')
token = str(scat)
token = scat.to_jwt()

assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal({
'identity': '[email protected]'
}, payload['grants'])
}, decoded_token.payload['grants'])

def test_conversations_grant(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
scat.add_grant(ConversationsGrant(configuration_profile_sid='CP123'))

token = str(scat)
token = scat.to_jwt()
assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal(1, len(payload['grants']))
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(1, len(decoded_token.payload['grants']))
assert_equal({
'configuration_profile_sid': 'CP123'
}, payload['grants']['rtc'])
}, decoded_token.payload['grants']['rtc'])

def test_video_grant(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
scat.add_grant(VideoGrant(configuration_profile_sid='CP123'))

token = str(scat)
token = scat.to_jwt()
assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal(1, len(payload['grants']))
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(1, len(decoded_token.payload['grants']))
assert_equal({
'configuration_profile_sid': 'CP123'
}, payload['grants']['video'])
}, decoded_token.payload['grants']['video'])

def test_ip_messaging_grant(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
scat.add_grant(IpMessagingGrant(service_sid='IS123', push_credential_sid='CR123'))

token = str(scat)
token = scat.to_jwt()
assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal(1, len(payload['grants']))
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(1, len(decoded_token.payload['grants']))
assert_equal({
'service_sid': 'IS123',
'push_credential_sid': 'CR123'
}, payload['grants']['ip_messaging'])
}, decoded_token.payload['grants']['ip_messaging'])

def test_sync_grant(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
scat.identity = "bender"
scat.add_grant(SyncGrant(service_sid='IS123', endpoint_id='blahblahendpoint'))

token = str(scat)
token = scat.to_jwt()
assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal(2, len(payload['grants']))
assert_equal("bender", payload['grants']['identity'])
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(2, len(decoded_token.payload['grants']))
assert_equal("bender", decoded_token.payload['grants']['identity'])
assert_equal({
'service_sid': 'IS123',
'endpoint_id': 'blahblahendpoint'
}, payload['grants']['data_sync'])
}, decoded_token.payload['grants']['data_sync'])

def test_grants(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
scat.add_grant(ConversationsGrant())
scat.add_grant(IpMessagingGrant())

token = str(scat)
token = scat.to_jwt()
assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal(2, len(payload['grants']))
assert_equal({}, payload['grants']['rtc'])
assert_equal({}, payload['grants']['ip_messaging'])
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(2, len(decoded_token.payload['grants']))
assert_equal({}, decoded_token.payload['grants']['rtc'])
assert_equal({}, decoded_token.payload['grants']['ip_messaging'])

def test_programmable_voice_grant(self):
grant = VoiceGrant(
Expand All @@ -146,16 +160,42 @@ def test_programmable_voice_grant(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
scat.add_grant(grant)

token = str(scat)
token = scat.to_jwt()
assert_is_not_none(token)
payload = decode(token, 'secret')
self._validate_claims(payload)
assert_equal(1, len(payload['grants']))
decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(1, len(decoded_token.payload['grants']))
assert_equal({
'outgoing': {
'application_sid': 'AP123',
'params': {
'foo': 'bar'
}
}
}, payload['grants']['voice'])
}, decoded_token.payload['grants']['voice'])

def test_pass_grants_in_constructor(self):
grants = [
ConversationsGrant(),
IpMessagingGrant()
]
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret', grants=grants)

token = scat.to_jwt()
assert_is_not_none(token)

decoded_token = AccessToken.from_jwt(token, 'secret')
self._validate_claims(decoded_token.payload)
assert_equal(2, len(decoded_token.payload['grants']))
assert_equal({}, decoded_token.payload['grants']['rtc'])
assert_equal({}, decoded_token.payload['grants']['ip_messaging'])

def test_constructor_validates_grants(self):
grants = [ConversationsGrant, 'GrantMeAccessToEverything']
self.assertRaises(ValueError, AccessToken, ACCOUNT_SID, SIGNING_KEY_SID, 'secret',
grants=grants)

def test_add_grant_validates_grant(self):
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
scat.add_grant(ConversationsGrant())
self.assertRaises(ValueError, scat.add_grant, 'GrantRootAccess')
114 changes: 114 additions & 0 deletions tests/unit/jwt/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import unittest

import time
from nose.tools import assert_true, assert_equal

from twilio.jwt import Jwt
from twilio.jwt.client import ClientCapabilityToken, ScopeURI


class ClientCapabilityTokenTest(unittest.TestCase):

def assertIn(self, foo, bar, msg=None):
"""backport for 2.6"""
return assert_true(foo in bar, msg=(msg or "%s not found in %s" % (foo, bar)))

def now(self):
return int(time.time())

def test_no_permissions(self):
token = ClientCapabilityToken("AC123", "XXXXX")
assert_equal(len(token._generate_payload()), 1)
assert_equal(token._generate_payload()["scope"], '')

def test_inbound_permissions(self):
token = ClientCapabilityToken("AC123", "XXXXX")
token.allow_client_incoming("andy")

eurl = "scope:client:incoming?clientName=andy"
assert_equal(len(token._generate_payload()), 1)
assert_equal(token._generate_payload()['scope'], eurl)

def test_outbound_permissions(self):
token = ClientCapabilityToken("AC123", "XXXXX")
token.allow_client_outgoing("AP123")

eurl = "scope:client:outgoing?appSid=AP123"

assert_equal(len(token._generate_payload()), 1)
self.assertIn(eurl, token._generate_payload()['scope'])

def test_outbound_permissions_params(self):
token = ClientCapabilityToken("AC123", "XXXXX")
token.allow_client_outgoing("AP123", foobar=3)

eurl = "scope:client:outgoing?appParams=foobar%3D3&appSid=AP123"
assert_equal(token.payload["scope"], eurl)

def test_events(self):
token = ClientCapabilityToken("AC123", "XXXXX")
token.allow_event_stream()

event_uri = "scope:stream:subscribe?path=%2F2010-04-01%2FEvents"
assert_equal(token.payload["scope"], event_uri)

def test_events_with_filters(self):
token = ClientCapabilityToken("AC123", "XXXXX")
token.allow_event_stream(foobar="hey")

event_uri = "scope:stream:subscribe?params=foobar%3Dhey&path=%2F2010-04-01%2FEvents"
assert_equal(token.payload["scope"], event_uri)

def test_decode(self):
token = ClientCapabilityToken("AC123", "XXXXX")
token.allow_client_outgoing("AP123", foobar=3)
token.allow_client_incoming("andy")
token.allow_event_stream()

outgoing_uri = "scope:client:outgoing?appParams=foobar%3D3&appSid=AP123&clientName=andy"
incoming_uri = "scope:client:incoming?clientName=andy"
event_uri = "scope:stream:subscribe?path=%2F2010-04-01%2FEvents"

result = Jwt.from_jwt(token.to_jwt(), "XXXXX")
scope = result.payload["scope"].split(" ")

self.assertIn(outgoing_uri, scope)
self.assertIn(incoming_uri, scope)
self.assertIn(event_uri, scope)

def test_encode_full_payload(self):
token = ClientCapabilityToken("AC123", "XXXXX")
token.allow_event_stream(foobar="hey")
token.allow_client_incoming("andy")

event_uri = "scope:stream:subscribe?params=foobar%3Dhey&path=%2F2010-04-01%2FEvents"
incoming_uri = "scope:client:incoming?clientName=andy"

self.assertIn(event_uri, token.payload["scope"])
self.assertIn(incoming_uri, token.payload["scope"])
self.assertEqual(token.payload['iss'], 'AC123')
self.assertGreaterEqual(token.payload['exp'], self.now())

def test_pass_scopes_in_constructor(self):
token = ClientCapabilityToken('AC123', 'XXXXX', allow_client_outgoing={
'application_sid': 'AP123',
'param1': 'val1'
})
outgoing_uri = "scope:client:outgoing?appParams=param1%3Dval1&appSid=AP123"
result = Jwt.from_jwt(token.to_jwt(), "XXXXX")
self.assertEqual(outgoing_uri, result.payload["scope"])


class ScopeURITest(unittest.TestCase):

def test_to_payload_no_params(self):
scope_uri = ScopeURI('service', 'godmode')
self.assertEqual('scope:service:godmode', scope_uri.to_payload())

def test_to_payload_with_params(self):
scope_uri = ScopeURI('service', 'godmode', {'key': 'val'})
self.assertEqual('scope:service:godmode?key=val', scope_uri.to_payload())

def test_to_payload_with_params_encoded(self):
scope_uri = ScopeURI('service', 'godmode', {'key with space': 'val'})
self.assertEqual('scope:service:godmode?key+with+space=val', scope_uri.to_payload())
Loading