diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6106019..a9a3316 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.10.0 +current_version = 3.11.0 commit = True tag = True diff --git a/.gitignore b/.gitignore index f14b9eb..6924f93 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ pip-delete-this-directory.txt .coverage .cache coverage.xml +htmlcov/ # Translations *.mo @@ -55,3 +56,6 @@ env/ # Pyenv .python-version + +# Credentials +credentials/ \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index 9c453ea..b7109a0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +# Release 3.11.0 +- OpenTok SDK now accepts Vonage credentials so it's possible to use the existing OpenTok SDK with the Vonage Video API +- Add additional headers to some requests +- Internal changes to the `Opentok.create_session` method + # Release 3.10.0 - Add new `max_bitrate` option for archives - Change to create JWTs by default in the `Client.generate_token` method. T1 tokens can still be created by setting `use_jwt=False` when generating a token. diff --git a/Makefile b/Makefile index 01ecd3b..1d3f861 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ clean: rm -rf dist build coverage: - pytest -v --cov + pytest -v --cov=opentok coverage html test: diff --git a/opentok/opentok.py b/opentok/opentok.py index 7bcf3f4..667067b 100644 --- a/opentok/opentok.py +++ b/opentok/opentok.py @@ -1,4 +1,5 @@ from datetime import datetime # generate_token +import re from typing import List, Optional # imports List, Optional type hint import calendar # generate_token import base64 # generate_token @@ -7,6 +8,7 @@ import hmac # _sign_string import hashlib from typing import List +import uuid import requests # create_session, archiving import json # archiving import platform # user-agent @@ -19,7 +21,7 @@ # compat -from six.moves.urllib.parse import urlencode +from urllib.parse import urlencode from six import text_type, u, b, PY3 from enum import Enum @@ -111,6 +113,11 @@ class ArchiveModes(Enum): class Client(object): """Use this SDK to create tokens and interface with the server-side portion of the Opentok API. + + You can also interact with this client object with Vonage credentials. Instead of passing + on OpenTok API key and secret, you can pass in a Vonage application ID and private key, + e.g. api_key=VONAGE_APPLICATION_ID, api_secret=VONAGE_PRIVATE_KEY. You do not need to set the API + URL differently, the SDK will set this for you. """ TOKEN_SENTINEL = "T1==" @@ -124,11 +131,25 @@ def __init__( timeout=None, app_version=None, ): + + if isinstance(api_secret, (str, bytes)) and re.search( + "[.][a-zA-Z0-9_]+$", api_secret + ): + # We have a private key so we assume we are using Vonage credentials + self._using_vonage = True + self._api_url = 'https://video.api.vonage.com' + with open(api_secret, "rb") as key_file: + self.api_secret = key_file.read() + else: + # We are using OpenTok credentials + self._using_vonage = False + self.api_secret = api_secret + self._api_url = api_url + self.api_key = str(api_key) - self.api_secret = api_secret self.timeout = timeout self._proxies = None - self.endpoints = Endpoints(api_url, self.api_key) + self.endpoints = Endpoints(self._api_url, self.api_key) self._app_version = __version__ if app_version == None else app_version self._user_agent = ( f"OpenTok-Python-SDK/{self.app_version} python/{platform.python_version()}" @@ -306,14 +327,13 @@ def generate_token( if use_jwt: payload = {} - payload['iss'] = self.api_key - payload['ist'] = 'project' + + payload['session_id'] = session_id + payload['role'] = role.value payload['iat'] = now payload["exp"] = expire_time - payload['nonce'] = random.randint(0, 999999) - payload['role'] = role.value payload['scope'] = 'session.connect' - payload['session_id'] = session_id + if initial_layout_class_list: payload['initial_layout_class_list'] = ( initial_layout_class_list_serialized @@ -321,9 +341,27 @@ def generate_token( if data: payload['connection_data'] = data - headers = {'alg': 'HS256', 'typ': 'JWT'} + if not self._using_vonage: + payload['iss'] = self.api_key + payload['ist'] = 'project' + payload['nonce'] = random.randint(0, 999999) + + headers = {'alg': 'HS256', 'typ': 'JWT'} - token = encode(payload, self.api_secret, algorithm="HS256", headers=headers) + token = encode( + payload, self.api_secret, algorithm="HS256", headers=headers + ) + else: + payload['application_id'] = self.api_key + payload['jti'] = str(uuid.uuid4()) + payload['subject'] = 'video' + payload['acl'] = {'paths': {'/session/**': {}}} + + headers = {'alg': 'RS256', 'typ': 'JWT'} + + token = encode( + payload, self.api_secret, algorithm="RS256", headers=headers + ) return token @@ -500,39 +538,54 @@ def create_session( "POST to %r with params %r, headers %r, proxies %r", self.endpoints.get_session_url(), options, - self.get_headers(), + self.get_json_headers(), self.proxies, ) - response = requests.post( - self.endpoints.get_session_url(), - data=options, - headers=self.get_headers(), - proxies=self.proxies, - timeout=self.timeout, - ) + if not self._using_vonage: + response = requests.post( + self.endpoints.get_session_url(), + data=options, + headers=self.get_headers(), + proxies=self.proxies, + timeout=self.timeout, + ) + else: + headers = self.get_headers() + response = requests.post( + self.endpoints.get_session_url(), + data=options, + headers=headers, + proxies=self.proxies, + timeout=self.timeout, + ) response.encoding = "utf-8" - if response.status_code == 403: raise AuthError("Failed to create session, invalid credentials") if not response.content: raise RequestError() - dom = xmldom.parseString(response.content.decode("utf-8")) except Exception as e: raise RequestError("Failed to create session: %s" % str(e)) try: - error = dom.getElementsByTagName("error") - if error: - error = error[0] - raise AuthError( - "Failed to create session (code=%s): %s" - % ( - error.attributes["code"].value, - error.firstChild.attributes["message"].value, + content_type = response.headers["Content-Type"] + # Legacy behaviour + if content_type != "application/json": + dom = xmldom.parseString(response.content.decode("utf-8")) + error = dom.getElementsByTagName("error") + if error: + error = error[0] + raise AuthError( + "Failed to create session (code=%s): %s" + % ( + error.attributes["code"].value, + error.firstChild.attributes["message"].value, + ) ) + session_id = ( + dom.getElementsByTagName("session_id")[0].childNodes[0].nodeValue ) - - session_id = dom.getElementsByTagName("session_id")[0].childNodes[0].nodeValue + else: + session_id = response.json()[0]["session_id"] return Session( self, session_id, @@ -546,12 +599,19 @@ def create_session( def get_headers(self): """For internal use.""" + if not self._using_vonage: + return { + "User-Agent": "OpenTok-Python-SDK/" + + self.app_version + + " python/" + + platform.python_version(), + "X-OPENTOK-AUTH": self._create_jwt_auth_header(), + "Accept": "application/json", + } return { - "User-Agent": "OpenTok-Python-SDK/" - + self.app_version - + " python/" - + platform.python_version(), - "X-OPENTOK-AUTH": self._create_jwt_auth_header(), + "User-Agent": self.user_agent + " OpenTok-With-Vonage-API-Backend", + "Authorization": "Bearer " + self._create_jwt_auth_header(), + "Accept": "application/json", } def headers(self): @@ -1859,13 +1919,13 @@ def stop_render(self, render_id): logger.debug( "DELETE to %r with headers %r, proxies %r", self.endpoints.get_render_url(render_id=render_id), - self.get_headers(), + self.get_json_headers(), self.proxies, ) response = requests.delete( self.endpoints.get_render_url(render_id=render_id), - headers=self.get_headers(), + headers=self.get_json_headers(), proxies=self.proxies, timeout=self.timeout, ) @@ -1896,14 +1956,14 @@ def list_renders(self, offset=0, count=50): logger.debug( "GET to %r with headers %r, params %r, proxies %r", self.endpoints.get_render_url(), - self.get_headers(), + self.get_json_headers(), query_params, self.proxies, ) response = requests.get( self.endpoints.get_render_url(), - headers=self.get_headers(), + headers=self.get_json_headers(), params=query_params, proxies=self.proxies, timeout=self.timeout, @@ -2090,14 +2150,21 @@ def _sign_string(self, string, secret): def _create_jwt_auth_header(self): payload = { "ist": "project", - "iss": self.api_key, "iat": int(time.time()), # current time in unix time (seconds) "exp": int(time.time()) + (60 * self._jwt_livetime), # 3 minutes in the future (seconds) - "jti": "{0}".format(0, random.random()), } - return encode(payload, self.api_secret, algorithm="HS256") + if not self._using_vonage: + payload["iss"] = self.api_key + payload["jti"] = str(random.random()) + return encode(payload, self.api_secret, algorithm="HS256") + + payload["application_id"] = self.api_key + payload["jti"] = str(uuid.uuid4()) + headers = {"typ": "JWT", "alg": "RS256"} + + return encode(payload, self.api_secret, algorithm='RS256', headers=headers) def mute_all( self, session_id: str, excludedStreamIds: Optional[List[str]] @@ -2127,7 +2194,7 @@ def mute_all( options = {"active": True, "excludedStreams": []} response = requests.post( - url, headers=self.get_headers(), data=json.dumps(options) + url, headers=self.get_json_headers(), data=json.dumps(options) ) if response: @@ -2164,7 +2231,7 @@ def disable_force_mute(self, session_id: str) -> requests.Response: url = self.endpoints.get_mute_all_url(session_id) response = requests.post( - url, headers=self.get_headers(), data=json.dumps(options) + url, headers=self.get_json_headers(), data=json.dumps(options) ) try: @@ -2198,7 +2265,7 @@ def mute_stream(self, session_id: str, stream_id: str) -> requests.Response: if stream_id: url = self.endpoints.get_stream_url(session_id, stream_id) + "/mute" - response = requests.post(url, headers=self.get_headers()) + response = requests.post(url, headers=self.get_json_headers()) if response: return response @@ -2315,7 +2382,7 @@ def mute_all( options = {"active": True, "excludedStreams": []} response = requests.post( - url, headers=self.get_headers(), data=json.dumps(options) + url, headers=self.get_json_headers(), data=json.dumps(options) ) if response: @@ -2350,7 +2417,7 @@ def disable_force_mute(self, session_id: str) -> requests.Response: url = self.endpoints.get_mute_all_url(session_id) response = requests.post( - url, headers=self.get_headers(), data=json.dumps(options) + url, headers=self.get_json_headers(), data=json.dumps(options) ) try: @@ -2382,7 +2449,7 @@ def mute_stream(self, session_id: str, stream_id: str) -> requests.Response: if stream_id: url = self.endpoints.get_stream_url(session_id, stream_id) + "/mute" - response = requests.post(url, headers=self.get_headers()) + response = requests.post(url, headers=self.get_json_headers()) if response: return response diff --git a/opentok/version.py b/opentok/version.py index 9650d1a..bdb2381 100644 --- a/opentok/version.py +++ b/opentok/version.py @@ -1,3 +1,3 @@ # see: http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers -__version__ = "3.10.0" +__version__ = "3.11.0" diff --git a/tests/fake_data/dummy_private_key.txt b/tests/fake_data/dummy_private_key.txt new file mode 100644 index 0000000..163ff36 --- /dev/null +++ b/tests/fake_data/dummy_private_key.txt @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQdAHqJHs/a+Ra +2ubvSd1vz/aWlJ9BqnMUtB7guTlyggdENAbleIkzep6mUHepDJdQh8Qv6zS3lpUe +K0UkDfr1/FvsvxurGw/YYPagUEhP/HxMbs2rnQTiAdWOT+Ux9vPABoyNYvZB90xN +IVhBDRWgkz1HPQBRNjFcm3NOol83h5Uwp5YroGTWx+rpmIiRhQj3mv6luk102d95 +4ulpPpzcYWKIpJNdclJrEkBZaghDZTOpbv79qd+ds9AVp1j8i9cG/owBJpsJWxfw +StMDpNeEZqopeQWmA121sSEsxpAbKJ5DA7F/lmckx74sulKHX1fDWT76cRhloaEQ +VmETdj0VAgMBAAECggEAZ+SBtchz8vKbsBqtAbM/XcR5Iqi1TR2eWMHDJ/65HpSm ++XuyujjerN0e6EZvtT4Uxmq8QaPJNP0kmhI31hXvsB0UVcUUDa4hshb1pIYO3Gq7 +Kr8I29EZB2mhndm9Ii9yYhEBiVA66zrNeR225kkWr97iqjhBibhoVr8Vc6oiqcIP +nFy5zSFtQSkhucaPge6rW00JSOD3wg2GM+rgS6r22t8YmqTzAwvwfil5pQfUngal +oywqLOf6CUYXPBleJc1KgaIIP/cSvqh6b/t25o2VXnI4rpRhtleORvYBbH6K6xLa +OWgg6B58T+0/QEqtZIAn4miYtVCkYLB78Ormc7Q9ewKBgQDuSytuYqxdZh/L/RDU +CErFcNO5I1e9fkLAs5dQEBvvdQC74+oA1MsDEVv0xehFa1JwPKSepmvB2UznZg9L +CtR7QKMDZWvS5xx4j0E/b+PiNQ/tlcFZB2UZ0JwviSxdd7omOTscq9c3RIhFHar1 +Y38Fixkfm44Ij/K3JqIi2v2QMwKBgQDf8TYOOmAr9UuipUDxMsRSqTGVIY8B+aEJ +W+2aLrqJVkLGTRfrbjzXWYo3+n7kNJjFgNkltDq6HYtufHMYRs/0PPtNR0w0cDPS +Xr7m2LNHTDcBalC/AS4yKZJLNLm+kXA84vkw4qiTjc0LSFxJkouTQzkea0l8EWHt +zRMv/qYVlwKBgBaJOWRJJK/4lo0+M7c5yYh+sSdTNlsPc9Sxp1/FBj9RO26JkXne +pgx2OdIeXWcjTTqcIZ13c71zhZhkyJF6RroZVNFfaCEcBk9IjQ0o0c504jq/7Pc0 +gdU9K2g7etykFBDFXNfLUKFDc/fFZIOskzi8/PVGStp4cqXrm23cdBqNAoGBAKtf +A2bP9ViuVjsZCyGJIAPBxlfBXpa8WSe4WZNrvwPqJx9pT6yyp4yE0OkVoJUyStaZ +S5M24NocUd8zDUC+r9TP9d+leAOI+Z87MgumOUuOX2mN2kzQsnFgrrsulhXnZmSx +rNBkI20HTqobrcP/iSAgiU1l/M4c3zwDe3N3A9HxAoGBAM2hYu0Ij6htSNgo/WWr +IEYYXuwf8hPkiuwzlaiWhD3eocgd4S8SsBu/bTCY19hQ2QbBPaYyFlNem+ynQyXx +IOacrgIHCrYnRCxjPfFF/MxgUHJb8ZoiexprP/FME5p0PoRQIEFYa+jVht3hT5wC +9aedWufq4JJb+akO6MVUjTvs +-----END PRIVATE KEY----- diff --git a/tests/fake_data/dummy_public_key.txt b/tests/fake_data/dummy_public_key.txt new file mode 100644 index 0000000..a171508 --- /dev/null +++ b/tests/fake_data/dummy_public_key.txt @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0HQB6iR7P2vkWtrm70nd +b8/2lpSfQapzFLQe4Lk5coIHRDQG5XiJM3qeplB3qQyXUIfEL+s0t5aVHitFJA36 +9fxb7L8bqxsP2GD2oFBIT/x8TG7Nq50E4gHVjk/lMfbzwAaMjWL2QfdMTSFYQQ0V +oJM9Rz0AUTYxXJtzTqJfN4eVMKeWK6Bk1sfq6ZiIkYUI95r+pbpNdNnfeeLpaT6c +3GFiiKSTXXJSaxJAWWoIQ2UzqW7+/anfnbPQFadY/IvXBv6MASabCVsX8ErTA6TX +hGaqKXkFpgNdtbEhLMaQGyieQwOxf5ZnJMe+LLpSh19Xw1k++nEYZaGhEFZhE3Y9 +FQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/helpers.py b/tests/helpers.py index 5cd4a91..c314520 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -26,7 +26,7 @@ def token_decoder(token: str, secret: str = None): return token_data encoded = token.replace('Bearer ', '').strip() - return decode(encoded, secret, algorithms='HS256') + return decode(encoded, secret, algorithms=['HS256', 'RS256']) def token_signature_validator(token, secret): diff --git a/tests/test_session_creation.py b/tests/test_session_creation.py index dc65493..e7f2565 100644 --- a/tests/test_session_creation.py +++ b/tests/test_session_creation.py @@ -1,7 +1,9 @@ +from time import time +from jwt import decode import pytest import unittest from six import u, b -from six.moves.urllib.parse import parse_qs +from urllib.parse import parse_qs from expects import * import httpretty from .validate_jwt import validate_jwt_header @@ -28,11 +30,9 @@ def test_create_default_session(self): httpretty.register_uri( httpretty.POST, u("https://api.opentok.com/session/create"), - body=u( - '1_MX4xMjM0NTZ-fk1vbiBNYXIgMTcgMDA6NDE6MzEgUERUIDIwMTR-MC42ODM3ODk1MzQ0OTQyODA4fg123456Mon Mar 17 00:41:31 PDT 2014' - ), + body='[{"session_id":"1_MX40NzY2NTk3MX5-MTczOTUzODg5NDk1OX5ROW1jWEcxUXJOM1RJWXU4eStwcHgvZ3N-UH5-","project_id":"47665971","partner_id":"47665971","create_dt":"Fri Feb 14 05:14:54 PST 2025","session_status":null,"status_invalid":null,"media_server_hostname":null,"messaging_server_url":null,"messaging_url":null,"symphony_address":null,"properties":null,"ice_server":null,"session_segment_id":"9c5fdd5b-7f5b-408f-8633-db95d0054fad","ice_servers":null,"ice_credential_expiration":86100}]', status=200, - content_type=u("text/xml"), + content_type=u("application/json"), ) session = self.opentok.create_session() @@ -54,7 +54,79 @@ def test_create_default_session(self): have_property( u("session_id"), u( - "1_MX4xMjM0NTZ-fk1vbiBNYXIgMTcgMDA6NDE6MzEgUERUIDIwMTR-MC42ODM3ODk1MzQ0OTQyODA4fg" + "1_MX40NzY2NTk3MX5-MTczOTUzODg5NDk1OX5ROW1jWEcxUXJOM1RJWXU4eStwcHgvZ3N-UH5-" + ), + ) + ) + expect(session).to(have_property(u("media_mode"), MediaModes.relayed)) + expect(session).to(have_property(u("location"), None)) + expect(session).to(have_property(u("e2ee"), False)) + + @httpretty.activate + def test_create_default_vonage_session(self): + httpretty.register_uri( + httpretty.POST, + u("https://video.api.vonage.com/session/create"), + body=""" + [ + { + "session_id": "1_MX4yOWY3NjBmOC03Y2UxLTQ2YzktYWRlMy1mMmRlZGVlNGVkNWZ-fjE3MjY0NjI1ODg2NDd-MTF4TGExYmJoelBlR1FHbVhzbWd4STBrfn5-", + "project_id": "29f760f8-7ce1-46c9-ade3-f2dedee4ed5f", + "partner_id": "29f760f8-7ce1-46c9-ade3-f2dedee4ed5f", + "create_dt": "Sun Sep 15 21:56:28 PDT 2024", + "session_status": null, + "status_invalid": null, + "media_server_hostname": null, + "messaging_server_url": null, + "messaging_url": null, + "symphony_address": null, + "properties": null, + "ice_server": null, + "session_segment_id": "35308566-4012-4c1e-90f7-cc15b5a390fe", + "ice_servers": null, + "ice_credential_expiration": 86100 + } + ]""", + status=200, + content_type="application/json", + ) + + self.api_secret = './tests/fake_data/dummy_private_key.txt' + vonage_wrapper = Client(self.api_key, self.api_secret) + session = vonage_wrapper.create_session() + + public_key = "" + with open('./tests/fake_data/dummy_public_key.txt', 'r') as file: + public_key = file.read() + + decoded_jwt = decode( + httpretty.last_request().headers[u("Authorization")].split(None, 1)[1], + public_key, + algorithms=["RS256"], + ) + + expect(decoded_jwt["application_id"]).to(equal(vonage_wrapper.api_key)) + expect(decoded_jwt["ist"]).to(equal("project")) + expect(decoded_jwt["exp"]).to(be_above(time())) + + expect(httpretty.last_request().headers[u("user-agent")]).to( + equal( + u("OpenTok-Python-SDK/") + + __version__ + + " python/" + + platform.python_version() + + " OpenTok-With-Vonage-API-Backend" + ) + ) + body = parse_qs(httpretty.last_request().body) + expect(body).to(have_key(b("p2p.preference"), [b("enabled")])) + expect(body).to(have_key(b("archiveMode"), [b("manual")])) + expect(session).to(be_a(Session)) + expect(session).to( + have_property( + u("session_id"), + u( + "1_MX4yOWY3NjBmOC03Y2UxLTQ2YzktYWRlMy1mMmRlZGVlNGVkNWZ-fjE3MjY0NjI1ODg2NDd-MTF4TGExYmJoelBlR1FHbVhzbWd4STBrfn5-" ), ) ) @@ -67,11 +139,9 @@ def test_create_routed_session(self): httpretty.register_uri( httpretty.POST, u("https://api.opentok.com/session/create"), - body=u( - '1_MX4xMjM0NTZ-fk1vbiBNYXIgMTcgMDA6NDE6MzEgUERUIDIwMTR-MC42ODM3ODk1MzQ0OTQyODA4fg123456Mon Mar 17 00:41:31 PDT 2014' - ), + body='[{"session_id":"1_MX40NzY2NTk3MX5-MTczOTUzODg5NDk1OX5ROW1jWEcxUXJOM1RJWXU4eStwcHgvZ3N-UH5-","project_id":"47665971","partner_id":"47665971","create_dt":"Fri Feb 14 05:14:54 PST 2025","session_status":null,"status_invalid":null,"media_server_hostname":null,"messaging_server_url":null,"messaging_url":null,"symphony_address":null,"properties":null,"ice_server":null,"session_segment_id":"9c5fdd5b-7f5b-408f-8633-db95d0054fad","ice_servers":null,"ice_credential_expiration":86100}]', status=200, - content_type=u("text/xml"), + content_type=u("application/json"), ) session = self.opentok.create_session(media_mode=MediaModes.routed) @@ -88,7 +158,7 @@ def test_create_routed_session(self): have_property( u("session_id"), u( - "1_MX4xMjM0NTZ-fk1vbiBNYXIgMTcgMDA6NDE6MzEgUERUIDIwMTR-MC42ODM3ODk1MzQ0OTQyODA4fg" + "1_MX40NzY2NTk3MX5-MTczOTUzODg5NDk1OX5ROW1jWEcxUXJOM1RJWXU4eStwcHgvZ3N-UH5-" ), ) ) diff --git a/tests/test_stream.py b/tests/test_stream.py index c568724..4b32fec 100644 --- a/tests/test_stream.py +++ b/tests/test_stream.py @@ -1,8 +1,7 @@ import unittest -from six import text_type, u, b, PY2, PY3 +from six import u from opentok import Client, Stream, StreamList, __version__, SetStreamClassError import httpretty -import json import textwrap from expects import * @@ -163,7 +162,7 @@ def test_get_stream_list(self): @httpretty.activate def test_set_stream_class_lists(self): - """ Test set stream class functionality """ + """Test set stream class functionality""" payload = [ { "id": "7b09ec3c-26f9-43d7-8197-f608f13d4fb6", @@ -197,7 +196,7 @@ def test_set_stream_class_lists(self): @httpretty.activate def test_set_stream_class_lists_throws_exception(self): - """ Test invalid request in set stream class list """ + """Test invalid request in set stream class list""" # invalid payload payload = [{"id": "7b09ec3c-26f9-43d7-8197-f608f13d4fb6"}] diff --git a/tests/test_token_generation.py b/tests/test_token_generation.py index 07e943a..e9e7b9d 100644 --- a/tests/test_token_generation.py +++ b/tests/test_token_generation.py @@ -1,10 +1,9 @@ import pytest import unittest -from six import text_type, u, PY2, PY3 +from six import text_type, u import time import datetime import calendar -import pytz from opentok import Client, Roles, OpenTokException @@ -31,6 +30,17 @@ def test_generate_plain_token_jwt(self): assert isinstance(token, text_type) assert token_decoder(token, self.api_secret)[u("session_id")] == self.session_id + def test_generate_plain_token_jwt_vonage_wrapper(self): + self.api_secret = './tests/fake_data/dummy_private_key.txt' + vonage_wrapper = Client(self.api_key, self.api_secret) + public_key = "" + with open('./tests/fake_data/dummy_public_key.txt', 'r') as file: + public_key = file.read() + + token = vonage_wrapper.generate_token(self.session_id) + assert isinstance(token, text_type) + assert token_decoder(token, public_key)[u("session_id")] == self.session_id + def test_generate_role_token(self): token = self.opentok.generate_token(self.session_id, Roles.moderator) assert isinstance(token, text_type) diff --git a/tests/validate_jwt.py b/tests/validate_jwt.py index 00e4aa7..592f614 100644 --- a/tests/validate_jwt.py +++ b/tests/validate_jwt.py @@ -5,7 +5,7 @@ def validate_jwt_header(self, jsonwebtoken): - claims = decode(jsonwebtoken, self.api_secret, algorithms=[u("HS256")]) + claims = decode(jsonwebtoken, self.api_secret, algorithms=["HS256", "RS256"]) expect(claims).to(have_key(u("iss"))) expect(claims[u("iss")]).to(equal(self.api_key)) expect(claims).to(have_key(u("ist")))