diff --git a/CHANGES.md b/CHANGES.md
index 45e7b7a..10a8bf2 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,7 @@
-# Release v3.4.0
+# Release v3.5.0
+- Support for end-to-end encryption (E2EE) that can be specified by a user when creating an OpenTok session.
+# Release v3.4.0
- Support for Audio Connector API via `connect_audio_to_websocket` method
# Release v3.3.0
diff --git a/README.rst b/README.rst
index 2bf36fa..811a70b 100644
--- a/README.rst
+++ b/README.rst
@@ -48,7 +48,7 @@ Import the package at the top of any file where you will use it. At the very lea
Creating Sessions
~~~~~~~~~~~~~~~~~
-To create an OpenTok Session, use the ``opentok.create_session()`` method. There are three optional
+To create an OpenTok Session, use the ``opentok.create_session()`` method. There are optional
keyword parameters for this method:
* ``location`` which can be set to a string containing an IP address.
@@ -62,6 +62,10 @@ keyword parameters for this method:
* ``archive_mode`` which specifies whether the session will be automatically archived (``always``)
or not (``manual``).
+* ``e2ee`` which is a boolean. This specifies whether to enable
+`end-to-end encryption `_
+for the OpenTok session.
+
This method returns a ``Session`` object. Its ``session_id`` attribute is useful when saving to a persistent
store (such as a database).
diff --git a/opentok/opentok.py b/opentok/opentok.py
index 09f62fa..d8d5e15 100644
--- a/opentok/opentok.py
+++ b/opentok/opentok.py
@@ -301,6 +301,7 @@ def create_session(
location=None,
media_mode=MediaModes.relayed,
archive_mode=ArchiveModes.manual,
+ e2ee=False,
):
"""
Creates a new OpenTok session and returns the session ID, which uniquely identifies
@@ -359,6 +360,9 @@ def create_session(
situate the session in its global network. If you do not set a location hint,
the OpenTok servers will be based on the first client connecting to the session.
+ :param Boolean e2ee: Whether to enable end-to-end encryption for a routed session
+ (see https://tokbox.com/developer/guides/end-to-end-encryption/).
+
:rtype: The Session object. The session_id property of the object is the session ID.
"""
@@ -395,6 +399,7 @@ def create_session(
).format(location)
)
options[u("location")] = location
+ options["e2ee"] = e2ee
try:
logger.debug(
@@ -436,13 +441,14 @@ def create_session(
session_id = (
dom.getElementsByTagName("session_id")[0].childNodes[0].nodeValue
- )
+ )
return Session(
self,
session_id,
location=location,
media_mode=media_mode,
archive_mode=archive_mode,
+ e2ee=e2ee,
)
except Exception as e:
raise OpenTokException("Failed to generate session: %s" % str(e))
diff --git a/opentok/version.py b/opentok/version.py
index 44d145a..5f50eb0 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.4.0"
+__version__ = "3.5.0"
diff --git a/tests/test_session_creation.py b/tests/test_session_creation.py
index 5466249..4c48997 100644
--- a/tests/test_session_creation.py
+++ b/tests/test_session_creation.py
@@ -54,6 +54,7 @@ def test_create_default_session(self):
)
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_routed_session(self):
@@ -87,6 +88,7 @@ def test_create_routed_session(self):
)
expect(session).to(have_property(u("media_mode"), MediaModes.routed))
expect(session).to(have_property(u("location"), None))
+ expect(session).to(have_property(u("e2ee"), False))
@httpretty.activate
def test_failure_create_routed_session(self):
@@ -138,6 +140,7 @@ def test_create_session_with_location_hint(self):
)
expect(session).to(have_property(u("media_mode"), MediaModes.relayed))
expect(session).to(have_property(u("location"), u("12.34.56.78")))
+ expect(session).to(have_property(u("e2ee"), False))
@httpretty.activate
def test_create_routed_session_with_location_hint(self):
@@ -174,6 +177,7 @@ def test_create_routed_session_with_location_hint(self):
)
expect(session).to(have_property(u("media_mode"), MediaModes.routed))
expect(session).to(have_property(u("location"), u("12.34.56.78")))
+ expect(session).to(have_property(u("e2ee"), False))
@httpretty.activate
def test_create_manual_archive_mode_session(self):
@@ -209,6 +213,7 @@ def test_create_manual_archive_mode_session(self):
)
expect(session).to(have_property(u("media_mode"), MediaModes.routed))
expect(session).to(have_property(u("archive_mode"), ArchiveModes.manual))
+ expect(session).to(have_property(u("e2ee"), False))
@httpretty.activate
def test_create_always_archive_mode_session(self):
@@ -244,6 +249,7 @@ def test_create_always_archive_mode_session(self):
)
expect(session).to(have_property(u("media_mode"), MediaModes.routed))
expect(session).to(have_property(u("archive_mode"), ArchiveModes.always))
+ expect(session).to(have_property(u("e2ee"), False))
@httpretty.activate
def test_complains_about_always_archive_mode_and_relayed_session(self):
@@ -263,5 +269,32 @@ def test_complains_about_always_archive_mode_and_relayed_session(self):
archive_mode=ArchiveModes.always,
)
+ @httpretty.activate
+ def test_create_session_with_e2ee(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'
+ ),
+ status=200,
+ content_type=u("text/xml"),
+ )
+
+ session = self.opentok.create_session(e2ee=True)
+
+ body = parse_qs(httpretty.last_request().body)
+ expect(body).to(have_key(b("e2ee"), [b'True']))
+ expect(session).to(be_a(Session))
+ expect(session).to(
+ have_property(
+ u("session_id"),
+ u(
+ "1_MX4xMjM0NTZ-fk1vbiBNYXIgMTcgMDA6NDE6MzEgUERUIDIwMTR-MC42ODM3ODk1MzQ0OTQyODA4fg"
+ ),
+ )
+ )
+ expect(session).to(have_property(u("e2ee"), True))
+
# TODO: all the cases that throw exceptions
# TODO: custom api_url requests