8000 Merge pull request #38 from twilio/identity-tokens · isprime/twilio-python@a1206d0 · GitHub
[go: up one dir, main page]

Skip to content

Commit a1206d0

Browse files
committed
Merge pull request twilio#38 from twilio/identity-tokens
First person grants
2 parents 366c029 + cdde3b6 commit a1206d0

File tree

2 files changed

+85
-75
lines changed

2 files changed

+85
-75
lines changed

tests/test_access_token.py

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from nose.tools import assert_equal
44
from twilio.jwt import decode
5-
from twilio.access_token import AccessToken
5+
from twilio.access_token import AccessToken, ConversationsGrant, IpMessagingGrant
66

77
ACCOUNT_SID = 'AC123'
88
SIGNING_KEY_SID = 'SK123'
@@ -26,56 +26,45 @@ def _validate_claims(self, payload):
2626
assert_is_not_none(payload['grants'])
2727

2828
def test_empty_grants(self):
29-
scat = AccessToken(SIGNING_KEY_SID, ACCOUNT_SID, 'secret')
29+
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
3030
token = str(scat)
31-
assert_is_not_none(token)
32-
payload = decode(token, 'secret')
33-
self._validate_claims(payload)
34-
assert_equal([], payload['grants'])
3531

36-
def test_single_grant(self):
37-
scat = AccessToken(SIGNING_KEY_SID, ACCOUNT_SID, 'secret')
38-
scat.add_grant('https://api.twilio.com/**')
39-
token = str(scat)
4032
assert_is_not_none(token)
4133
payload = decode(token, 'secret')
4234
self._validate_claims(payload)
43-
assert_equal(1, len(payload['grants']))
44-
assert_equal('https://api.twilio.com/**', payload['grants'][0]['res'])
45-
assert_equal(['*'], payload['grants'][0]['act'])
35+
assert_equal({}, payload['grants'])
36+
37+
def test_conversations_grant(self):
38+
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
39+
scat.add_grant(ConversationsGrant())
4640

47-
def test_endpoint_grant(self):
48-
scat = AccessToken(SIGNING_KEY_SID, ACCOUNT_SID, 'secret')
49-
scat.add_endpoint_grant('bob')
5041
token = str(scat)
5142
assert_is_not_none(token)
5243
payload = decode(token, 'secret')
5344
self._validate_claims(payload)
5445
assert_equal(1, len(payload['grants']))
55-
assert_equal('sip:bob@AC123.endpoint.twilio.com',
56-
payload['grants'][0]['res'])
57-
assert_equal(['listen', 'invite'], payload['grants'][0]['act'])
46+
assert_equal({}, payload['grants']['rtc'])
47+
48+
def test_ip_messaging_grant(self):
49+
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
50+
scat.add_grant(IpMessagingGrant())
5851

59-
def test_rest_grant(self):
60-
scat = AccessToken(SIGNING_KEY_SID, ACCOUNT_SID, 'secret')
61-
scat.add_rest_grant('/Apps')
6252
token = str(scat)
6353
assert_is_not_none(token)
6454
payload = decode(token, 'secret')
6555
self._validate_claims(payload)
6656
assert_equal(1, len(payload['grants']))
67-
assert_equal('https://api.twilio.com/2010-04-01/Accounts/AC123/Apps',
68-
payload['grants'][0]['res'])
69-
assert_equal(['*'], payload['grants'][0]['act'])
57+
assert_equal({}, payload['grants']['ip_messaging'])
58+
59+
def test_grants(self):
60+
scat = AccessToken(ACCOUNT_SID, SIGNING_KEY_SID, 'secret')
61+
scat.add_grant(ConversationsGrant())
62+
scat.add_grant(IpMessagingGrant())
7063

71-
def test_enable_nts(self):
72-
scat = AccessToken(SIGNING_KEY_SID, ACCOUNT_SID, 'secret')
73-
scat.enable_nts()
7464
token = str(scat)
7565
assert_is_not_none(token)
7666
payload = decode(token, 'secret')
7767
self._validate_claims(payload)
78-
assert_equal(1, len(payload['grants']))
79-
assert_equal('https://api.twilio.com/2010-04-01/Accounts/AC123/Tokens.json',
80-
payload['grants'][0]['res'])
81-
assert_equal(['POST'], payload['grants'][0]['act'])
68+
assert_equal(2, len(payload['grants']))
69+
assert_equal({}, payload['grants']['rtc'])
70+
assert_equal({}, payload['grants']['ip_messaging'])

twilio/access_token.py

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,91 @@
11
import time
22
import jwt
33

4-
ALL = '*'
54

6-
# HTTP Actions
7-
HTTP_DELETE = 'DELETE'
8-
HTTP_GET = 'GET'
9-
HTTP_POST = 'POST'
10-
HTTP_PUT = 'PUT'
5+
class IpMessagingGrant(object):
6+
""" Grant to access Twilio IP Messaging """
7+
def __init__(self, service_sid=None, endpoint_id=None,
8+
role_sid=None, credential_sid=None):
9+
self.service_sid = service_sid
10+
self.endpoint_id = endpoint_id
11+
self.deployment_role_sid = role_sid
12+
self.push_credential_sid = credential_sid
1113

12-
# Client Actions
13-
CLIENT_LISTEN = 'listen'
14-
CLIENT_INVITE = 'invite'
14+
@property
15+
def key(self):
16+
return "ip_messaging"
17+
18+
def to_payload(self):
19+
grant = {}
20+
if self.service_sid:
21+
grant['service_sid'] = self.service_sid
22+
if self.endpoint_id:
23+
grant['endpoint_id'] = self.endpoint_id
24+
if self.deployment_role_sid:
25+
grant['deployment_role_sid'] = self.deployment_role_sid
26+
if self.push_credential_sid:
27+
grant['push_credential_sid'] = self.push_credential_sid
28+
29+
return grant
30+
31+
32+
class ConversationsGrant(object):
33+
""" Grant to access Twilio Conversations """
34+
def __init__(self, configuration_profile_sid=None):
35+
self.configuration_profile_sid = configuration_profile_sid
36+
37+
@property
38+
def key(self):
39+
return "rtc"
40+
41+
def to_payload(self):
42+
grant = {}
43+
if self.configuration_profile_sid:
44+
grant['configuration_profile_sid'] = self.configuration_profile_sid
45+
46+
return grant
1547

1648

1749
class AccessToken(object):
18-
def __init__(self, signing_key_sid, account_sid, secret, ttl=3600):
19-
self.signing_key_sid = signing_key_sid
50+
""" Access Token used to access Twilio Resources """
51+
def __init__(self, account_sid, signing_key_sid, secret,
52+
identity=None, ttl=3600):
2053
self.account_sid = account_sid
54+
self.signing_key_sid = signing_key_sid
2155
self.secret = secret
56+
57+
self.identity = identity
2258
self.ttl = ttl
2359
self.grants = []
2460

25-
def add_grant(self, resource, actions=ALL):
26-
if not isinstance(actions, list):
27-
actions = [actions]
28-
29-
self.grants.append({
30-
'res': resource,
31-
'act': actions,
32-
})
33-
return self
34-
35-
def add_rest_grant(self, uri, actions=ALL):
36-
resource = 'https://api.twilio.com/2010-04-01/Accounts/{0}/{1}'.format(
37-
self.account_sid,
38-
uri.lstrip('/'),
39-
)
40-
return self.add_grant(resource, actions)
41-
42-
def add_endpoint_grant(self, endpoint, actions=None):
43-
actions = actions or [CLIENT_LISTEN, CLIENT_INVITE]
44-
resource = 'sip:{0}@{1}.endpoint.twilio.com'.format(
45-
endpoint,
46-
self.account_sid
47-
)
48-
return self.add_grant(resource, actions)
49-
50-
def enable_nts(self):
51-
return self.add_rest_grant('/Tokens.json', HTTP_POST)
52-
53-
def to_jwt(self):
61+
def add_grant(self, grant):
62+
self.grants.append(grant)
63+
64+
def to_jwt(self, algorithm='HS256'):
5465
now = int(time.time())
5566
headers = {
56-
"cty": "twilio-sat;v=1"
67+
"typ": "JWT",
68+
"cty": "twilio-fpa;v=1"
5769
}
70+
71+
grants = {}
72+
if self.identity:
73+
grants["identity"] = self.identity
74+
75+
for grant in self.grants:
76+
grants[grant.key] = grant.to_payload()
77+
5878
payload = {
5979
"jti": '{0}-{1}'.format(self.signing_key_sid, now),
6080
"iss": self.signing_key_sid,
6181
"sub": self.account_sid,
6282
"nbf": now,
6383
"exp": now + self.ttl,
64-
"grants": self.grants
84+
"grants": grants
6585
}
6686

67-
return jwt.encode(payload, self.secret, headers=headers)
87+
return jwt.encode(payload, self.secret, headers=headers,
88+
algorithm=algorithm)
6889

6990
def __str__(self):
7091
return self.to_jwt().decode('utf-8')

0 commit comments

Comments
 (0)
0