mirror of
https://github.com/zulip/zulip.git
synced 2025-11-11 17:36:27 +00:00
integration: Generate dynamic name for BigBlueButton video calls.
The name for a BigBlueButton meeting is now generated from the stream name and topic name. The createTime option is used to have the user redirected to a link that is only valid for this meeting. Even if the same link in Zulip is used again, a new createTime parameter will be created, as the Meeting on the BigBlueButton server has to be recreated. Fixes #16498. Fixes #20509. Fixes #20804.
This commit is contained in:
@@ -27,6 +27,7 @@ set_global("ResizeObserver", function () {
|
|||||||
|
|
||||||
const server_events_dispatch = zrequire("server_events_dispatch");
|
const server_events_dispatch = zrequire("server_events_dispatch");
|
||||||
const compose_ui = zrequire("compose_ui");
|
const compose_ui = zrequire("compose_ui");
|
||||||
|
const compose_closed = zrequire("compose_closed_ui");
|
||||||
const compose = zrequire("compose");
|
const compose = zrequire("compose");
|
||||||
function stub_out_video_calls() {
|
function stub_out_video_calls() {
|
||||||
const $elem = $("#below-compose-content .video_link");
|
const $elem = $("#below-compose-content .video_link");
|
||||||
@@ -210,8 +211,11 @@ test("videos", ({override, override_rewire}) => {
|
|||||||
page_params.realm_video_chat_provider =
|
page_params.realm_video_chat_provider =
|
||||||
realm_available_video_chat_providers.big_blue_button.id;
|
realm_available_video_chat_providers.big_blue_button.id;
|
||||||
|
|
||||||
|
compose_closed.get_recipient_label = () => "a";
|
||||||
|
|
||||||
channel.get = (options) => {
|
channel.get = (options) => {
|
||||||
assert.equal(options.url, "/json/calls/bigbluebutton/create");
|
assert.equal(options.url, "/json/calls/bigbluebutton/create");
|
||||||
|
assert.equal(options.data.meeting_name, "a meeting");
|
||||||
options.success({
|
options.success({
|
||||||
url: "/calls/bigbluebutton/join?meeting_id=%22zulip-1%22&password=%22AAAAAAAAAA%22&checksum=%2232702220bff2a22a44aee72e96cfdb4c4091752e%22",
|
url: "/calls/bigbluebutton/join?meeting_id=%22zulip-1%22&password=%22AAAAAAAAAA%22&checksum=%2232702220bff2a22a44aee72e96cfdb4c4091752e%22",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import _ from "lodash";
|
|||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import * as channel from "./channel";
|
import * as channel from "./channel";
|
||||||
import * as compose_actions from "./compose_actions";
|
import * as compose_actions from "./compose_actions";
|
||||||
|
import {get_recipient_label} from "./compose_closed_ui";
|
||||||
import * as compose_error from "./compose_error";
|
import * as compose_error from "./compose_error";
|
||||||
import * as compose_fade from "./compose_fade";
|
import * as compose_fade from "./compose_fade";
|
||||||
import * as compose_state from "./compose_state";
|
import * as compose_state from "./compose_state";
|
||||||
@@ -616,8 +617,12 @@ export function initialize() {
|
|||||||
available_providers.big_blue_button &&
|
available_providers.big_blue_button &&
|
||||||
page_params.realm_video_chat_provider === available_providers.big_blue_button.id
|
page_params.realm_video_chat_provider === available_providers.big_blue_button.id
|
||||||
) {
|
) {
|
||||||
|
const meeting_name = get_recipient_label() + " meeting";
|
||||||
channel.get({
|
channel.get({
|
||||||
url: "/json/calls/bigbluebutton/create",
|
url: "/json/calls/bigbluebutton/create",
|
||||||
|
data: {
|
||||||
|
meeting_name,
|
||||||
|
},
|
||||||
success(response) {
|
success(response) {
|
||||||
insert_video_call_url(response.url, $target_textarea);
|
insert_video_call_url(response.url, $target_textarea);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13868,6 +13868,16 @@ paths:
|
|||||||
description: |
|
description: |
|
||||||
Create a video call URL for a BigBlueButton video call.
|
Create a video call URL for a BigBlueButton video call.
|
||||||
Requires BigBlueButton to be configured on the Zulip server.
|
Requires BigBlueButton to be configured on the Zulip server.
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: meeting_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: |
|
||||||
|
Title to use for the BigBlueButton meeting.
|
||||||
|
|
||||||
|
A good choice is something like "{stream_name} meeting".
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Success.
|
description: Success.
|
||||||
@@ -13885,12 +13895,12 @@ paths:
|
|||||||
description: |
|
description: |
|
||||||
The URL for the BigBlueButton video call.
|
The URL for the BigBlueButton video call.
|
||||||
type: string
|
type: string
|
||||||
example: "/calls/bbb/join?meeting_id=%22zulip-something%22&password=%22something%22&checksum=%22somechecksum%22"
|
example: "/calls/bigbluebutton/join?meeting_id=%22zulip-something%22&password=%22something%22&name=%22your_meeting_name%22&checksum=%22somechecksum%22"
|
||||||
example:
|
example:
|
||||||
{
|
{
|
||||||
"msg": "",
|
"msg": "",
|
||||||
"result": "success",
|
"result": "success",
|
||||||
"url": "/calls/bbb/join?meeting_id=%22zulip-something%22&password=%22something%22&checksum=%22somechecksum%22",
|
"url": "/calls/bigbluebutton/join?meeting_id=%22zulip-something%22&password=%22something%22&checksum=%22somechecksum%22",
|
||||||
}
|
}
|
||||||
|
|
||||||
components:
|
components:
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import responses
|
import responses
|
||||||
|
from django.core.signing import Signer
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
from zerver.lib.test_classes import ZulipTestCase
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
|
from zerver.lib.url_encoding import append_url_query_string
|
||||||
|
|
||||||
|
|
||||||
class TestVideoCall(ZulipTestCase):
|
class TestVideoCall(ZulipTestCase):
|
||||||
@@ -11,6 +13,15 @@ class TestVideoCall(ZulipTestCase):
|
|||||||
super().setUp()
|
super().setUp()
|
||||||
self.user = self.example_user("hamlet")
|
self.user = self.example_user("hamlet")
|
||||||
self.login_user(self.user)
|
self.login_user(self.user)
|
||||||
|
# Signing for bbb
|
||||||
|
self.signer = Signer()
|
||||||
|
self.signed_bbb_a_object = self.signer.sign_object(
|
||||||
|
{
|
||||||
|
"meeting_id": "a",
|
||||||
|
"name": "a",
|
||||||
|
"password": "a",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def test_register_video_request_no_settings(self) -> None:
|
def test_register_video_request_no_settings(self) -> None:
|
||||||
with self.settings(VIDEO_ZOOM_CLIENT_ID=None):
|
with self.settings(VIDEO_ZOOM_CLIENT_ID=None):
|
||||||
@@ -168,46 +179,70 @@ class TestVideoCall(ZulipTestCase):
|
|||||||
|
|
||||||
def test_create_bigbluebutton_link(self) -> None:
|
def test_create_bigbluebutton_link(self) -> None:
|
||||||
with mock.patch("zerver.views.video_calls.random.randint", return_value="1"), mock.patch(
|
with mock.patch("zerver.views.video_calls.random.randint", return_value="1"), mock.patch(
|
||||||
"secrets.token_bytes", return_value=b"\x00" * 7
|
"secrets.token_bytes", return_value=b"\x00" * 12
|
||||||
):
|
):
|
||||||
response = self.client_get("/json/calls/bigbluebutton/create")
|
response = self.client_get(
|
||||||
|
"/json/calls/bigbluebutton/create?meeting_name=general > meeting"
|
||||||
|
)
|
||||||
self.assert_json_success(response)
|
self.assert_json_success(response)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.json()["url"],
|
response.json()["url"],
|
||||||
"/calls/bigbluebutton/join?meeting_id=zulip-1&password=AAAAAAAAAA"
|
append_url_query_string(
|
||||||
"&checksum=d5eb2098bcd0e69a33caf2b18490991b843c8fa6be779316b4303c7990aca687",
|
"/calls/bigbluebutton/join",
|
||||||
|
"bigbluebutton="
|
||||||
|
+ self.signer.sign_object(
|
||||||
|
{
|
||||||
|
"meeting_id": "zulip-1",
|
||||||
|
"name": "general > meeting",
|
||||||
|
"password": "AAAAAAAAAAAAAAAAAAAA",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_join_bigbluebutton_redirect(self) -> None:
|
def test_join_bigbluebutton_redirect(self) -> None:
|
||||||
responses.add(
|
responses.add(
|
||||||
responses.GET,
|
responses.GET,
|
||||||
"https://bbb.example.com/bigbluebutton/api/create?meetingID=zulip-1&moderatorPW=a&attendeePW=aa&checksum=check",
|
"https://bbb.example.com/bigbluebutton/api/create?meetingID=a&name=a"
|
||||||
"<response><returncode>SUCCESS</returncode><messageKey/></response>",
|
"&moderatorPW=a&attendeePW=a&checksum=131bdec35f62fc63d5436e6f791d6d7aed7cf79ef256c03597e51d320d042823",
|
||||||
|
"<response><returncode>SUCCESS</returncode><messageKey/><createTime>0</createTime></response>",
|
||||||
)
|
)
|
||||||
response = self.client_get(
|
response = self.client_get(
|
||||||
"/calls/bigbluebutton/join",
|
"/calls/bigbluebutton/join", {"bigbluebutton": self.signed_bbb_a_object}
|
||||||
{"meeting_id": "zulip-1", "password": "a", "checksum": "check"},
|
|
||||||
)
|
)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(isinstance(response, HttpResponseRedirect), True)
|
self.assertEqual(isinstance(response, HttpResponseRedirect), True)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.url,
|
response.url,
|
||||||
"https://bbb.example.com/bigbluebutton/api/join?meetingID=zulip-1&password=a"
|
"https://bbb.example.com/bigbluebutton/api/join?meetingID=a&"
|
||||||
"&fullName=King%20Hamlet&checksum=ca78d6d3c3e04918bfab9d7d6cbc6e50602ab2bdfe1365314570943346a71a00",
|
"password=a&fullName=King%20Hamlet&createTime=0&checksum=47ca959b4ff5c8047a5a56d6e99c07e17eac43dbf792afc0a2a9f6491ec0048b",
|
||||||
)
|
)
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_join_bigbluebutton_redirect_wrong_check(self) -> None:
|
def test_join_bigbluebutton_invalid_signature(self) -> None:
|
||||||
responses.add(
|
responses.add(
|
||||||
responses.GET,
|
responses.GET,
|
||||||
"https://bbb.example.com/bigbluebutton/api/create?meetingID=zulip-1&moderatorPW=a&attendeePW=aa&checksum=check",
|
"https://bbb.example.com/bigbluebutton/api/create?meetingID=a&name=a"
|
||||||
|
"&moderatorPW=a&attendeePW=a&checksum=131bdec35f62fc63d5436e6f791d6d7aed7cf79ef256c03597e51d320d042823",
|
||||||
|
"<response><returncode>SUCCESS</returncode><messageKey/><createTime>0</createTime></response>",
|
||||||
|
)
|
||||||
|
response = self.client_get(
|
||||||
|
"/calls/bigbluebutton/join", {"bigbluebutton": self.signed_bbb_a_object + "zoo"}
|
||||||
|
)
|
||||||
|
self.assert_json_error(response, "Invalid signature.")
|
||||||
|
|
||||||
|
@responses.activate
|
||||||
|
def test_join_bigbluebutton_redirect_wrong_big_blue_button_checksum(self) -> None:
|
||||||
|
responses.add(
|
||||||
|
responses.GET,
|
||||||
|
"https://bbb.example.com/bigbluebutton/api/create?meetingID=a&name=a&moderatorPW=a&attendeePW=a&checksum=131bdec35f62fc63d5436e6f791d6d7aed7cf79ef256c03597e51d320d042823",
|
||||||
"<response><returncode>FAILED</returncode><messageKey>checksumError</messageKey>"
|
"<response><returncode>FAILED</returncode><messageKey>checksumError</messageKey>"
|
||||||
"<message>You did not pass the checksum security check</message></response>",
|
"<message>You did not pass the checksum security check</message></response>",
|
||||||
)
|
)
|
||||||
response = self.client_get(
|
response = self.client_get(
|
||||||
"/calls/bigbluebutton/join",
|
"/calls/bigbluebutton/join",
|
||||||
{"meeting_id": "zulip-1", "password": "a", "checksum": "check"},
|
{"bigbluebutton": self.signed_bbb_a_object},
|
||||||
)
|
)
|
||||||
self.assert_json_error(response, "Error authenticating to the BigBlueButton server.")
|
self.assert_json_error(response, "Error authenticating to the BigBlueButton server.")
|
||||||
|
|
||||||
@@ -216,13 +251,13 @@ class TestVideoCall(ZulipTestCase):
|
|||||||
# Simulate bbb server error
|
# Simulate bbb server error
|
||||||
responses.add(
|
responses.add(
|
||||||
responses.GET,
|
responses.GET,
|
||||||
"https://bbb.example.com/bigbluebutton/api/create?meetingID=zulip-1&moderatorPW=a&attendeePW=aa&checksum=check",
|
"https://bbb.example.com/bigbluebutton/api/create?meetingID=a&name=a&moderatorPW=a&attendeePW=a&checksum=131bdec35f62fc63d5436e6f791d6d7aed7cf79ef256c03597e51d320d042823",
|
||||||
"",
|
"",
|
||||||
status=500,
|
status=500,
|
||||||
)
|
)
|
||||||
response = self.client_get(
|
response = self.client_get(
|
||||||
"/calls/bigbluebutton/join",
|
"/calls/bigbluebutton/join",
|
||||||
{"meeting_id": "zulip-1", "password": "a", "checksum": "check"},
|
{"bigbluebutton": self.signed_bbb_a_object},
|
||||||
)
|
)
|
||||||
self.assert_json_error(response, "Error connecting to the BigBlueButton server.")
|
self.assert_json_error(response, "Error connecting to the BigBlueButton server.")
|
||||||
|
|
||||||
@@ -231,12 +266,12 @@ class TestVideoCall(ZulipTestCase):
|
|||||||
# Simulate bbb server error
|
# Simulate bbb server error
|
||||||
responses.add(
|
responses.add(
|
||||||
responses.GET,
|
responses.GET,
|
||||||
"https://bbb.example.com/bigbluebutton/api/create?meetingID=zulip-1&moderatorPW=a&attendeePW=aa&checksum=check",
|
"https://bbb.example.com/bigbluebutton/api/create?meetingID=a&name=a&moderatorPW=a&attendeePW=a&checksum=131bdec35f62fc63d5436e6f791d6d7aed7cf79ef256c03597e51d320d042823",
|
||||||
"<response><returncode>FAILURE</returncode><messageKey>otherFailure</messageKey></response>",
|
"<response><returncode>FAILURE</returncode><messageKey>otherFailure</messageKey></response>",
|
||||||
)
|
)
|
||||||
response = self.client_get(
|
response = self.client_get(
|
||||||
"/calls/bigbluebutton/join",
|
"/calls/bigbluebutton/join",
|
||||||
{"meeting_id": "zulip-1", "password": "a", "checksum": "check"},
|
{"bigbluebutton": self.signed_bbb_a_object},
|
||||||
)
|
)
|
||||||
self.assert_json_error(response, "BigBlueButton server returned an unexpected error.")
|
self.assert_json_error(response, "BigBlueButton server returned an unexpected error.")
|
||||||
|
|
||||||
@@ -244,6 +279,6 @@ class TestVideoCall(ZulipTestCase):
|
|||||||
with self.settings(BIG_BLUE_BUTTON_SECRET=None, BIG_BLUE_BUTTON_URL=None):
|
with self.settings(BIG_BLUE_BUTTON_SECRET=None, BIG_BLUE_BUTTON_URL=None):
|
||||||
response = self.client_get(
|
response = self.client_get(
|
||||||
"/calls/bigbluebutton/join",
|
"/calls/bigbluebutton/join",
|
||||||
{"meeting_id": "zulip-1", "password": "a", "checksum": "check"},
|
{"bigbluebutton": self.signed_bbb_a_object},
|
||||||
)
|
)
|
||||||
self.assert_json_error(response, "BigBlueButton is not configured.")
|
self.assert_json_error(response, "BigBlueButton is not configured.")
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from urllib.parse import quote, urlencode, urljoin
|
|||||||
import requests
|
import requests
|
||||||
from defusedxml import ElementTree
|
from defusedxml import ElementTree
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.signing import Signer
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.middleware import csrf
|
from django.middleware import csrf
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
@@ -168,35 +169,27 @@ def deauthorize_zoom_user(request: HttpRequest) -> HttpResponse:
|
|||||||
return json_success(request)
|
return json_success(request)
|
||||||
|
|
||||||
|
|
||||||
def get_bigbluebutton_url(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
|
@has_request_variables
|
||||||
|
def get_bigbluebutton_url(
|
||||||
|
request: HttpRequest, user_profile: UserProfile, meeting_name: str = REQ()
|
||||||
|
) -> HttpResponse:
|
||||||
# https://docs.bigbluebutton.org/dev/api.html#create for reference on the API calls
|
# https://docs.bigbluebutton.org/dev/api.html#create for reference on the API calls
|
||||||
# https://docs.bigbluebutton.org/dev/api.html#usage for reference for checksum
|
# https://docs.bigbluebutton.org/dev/api.html#usage for reference for checksum
|
||||||
id = "zulip-" + str(random.randint(100000000000, 999999999999))
|
id = "zulip-" + str(random.randint(100000000000, 999999999999))
|
||||||
password = b32encode(secrets.token_bytes(7))[:10].decode()
|
password = b32encode(secrets.token_bytes(7))[:20].decode()
|
||||||
checksum = hashlib.sha256(
|
|
||||||
(
|
# We sign our data here to ensure a Zulip user can not tamper with
|
||||||
"create"
|
# the join link to gain access to other meetings that are on the
|
||||||
+ "meetingID="
|
# same bigbluebutton server.
|
||||||
+ id
|
signed = Signer().sign_object(
|
||||||
+ "&moderatorPW="
|
{
|
||||||
+ password
|
"meeting_id": id,
|
||||||
+ "&attendeePW="
|
"name": meeting_name,
|
||||||
+ password
|
"password": password,
|
||||||
+ "a"
|
}
|
||||||
+ settings.BIG_BLUE_BUTTON_SECRET
|
|
||||||
).encode()
|
|
||||||
).hexdigest()
|
|
||||||
url = append_url_query_string(
|
|
||||||
"/calls/bigbluebutton/join",
|
|
||||||
urlencode(
|
|
||||||
{
|
|
||||||
"meeting_id": id,
|
|
||||||
"password": password,
|
|
||||||
"checksum": checksum,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
return json_success(request, data={"url": url})
|
url = append_url_query_string("/calls/bigbluebutton/join", "bigbluebutton=" + signed)
|
||||||
|
return json_success(request, {"url": url})
|
||||||
|
|
||||||
|
|
||||||
# We use zulip_login_required here mainly to get access to the user's
|
# We use zulip_login_required here mainly to get access to the user's
|
||||||
@@ -207,55 +200,78 @@ def get_bigbluebutton_url(request: HttpRequest, user_profile: UserProfile) -> Ht
|
|||||||
@zulip_login_required
|
@zulip_login_required
|
||||||
@never_cache
|
@never_cache
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def join_bigbluebutton(
|
def join_bigbluebutton(request: HttpRequest, bigbluebutton: str = REQ()) -> HttpResponse:
|
||||||
request: HttpRequest,
|
|
||||||
meeting_id: str = REQ(),
|
|
||||||
password: str = REQ(),
|
|
||||||
checksum: str = REQ(),
|
|
||||||
) -> HttpResponse:
|
|
||||||
assert request.user.is_authenticated
|
assert request.user.is_authenticated
|
||||||
|
|
||||||
if settings.BIG_BLUE_BUTTON_URL is None or settings.BIG_BLUE_BUTTON_SECRET is None:
|
if settings.BIG_BLUE_BUTTON_URL is None or settings.BIG_BLUE_BUTTON_SECRET is None:
|
||||||
raise JsonableError(_("BigBlueButton is not configured."))
|
raise JsonableError(_("BigBlueButton is not configured."))
|
||||||
else:
|
|
||||||
try:
|
|
||||||
response = VideoCallSession().get(
|
|
||||||
append_url_query_string(
|
|
||||||
settings.BIG_BLUE_BUTTON_URL + "api/create",
|
|
||||||
urlencode(
|
|
||||||
{
|
|
||||||
"meetingID": meeting_id,
|
|
||||||
"moderatorPW": password,
|
|
||||||
"attendeePW": password + "a",
|
|
||||||
"checksum": checksum,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
except requests.RequestException:
|
|
||||||
raise JsonableError(_("Error connecting to the BigBlueButton server."))
|
|
||||||
|
|
||||||
payload = ElementTree.fromstring(response.text)
|
try:
|
||||||
if payload.find("messageKey").text == "checksumError":
|
bigbluebutton_data = Signer().unsign_object(bigbluebutton)
|
||||||
raise JsonableError(_("Error authenticating to the BigBlueButton server."))
|
except Exception:
|
||||||
|
raise JsonableError(_("Invalid signature."))
|
||||||
|
|
||||||
if payload.find("returncode").text != "SUCCESS":
|
create_params = urlencode(
|
||||||
raise JsonableError(_("BigBlueButton server returned an unexpected error."))
|
{
|
||||||
|
"meetingID": bigbluebutton_data["meeting_id"],
|
||||||
|
"name": bigbluebutton_data["name"],
|
||||||
|
"moderatorPW": bigbluebutton_data["password"],
|
||||||
|
# We generate the attendee password from moderatorPW,
|
||||||
|
# because the BigBlueButton API requires a separate
|
||||||
|
# password. This integration is designed to have all users
|
||||||
|
# join as moderators, so we generate attendeePW by
|
||||||
|
# truncating the moderatorPW while keeping it long enough
|
||||||
|
# to not be vulnerable to brute force attacks.
|
||||||
|
"attendeePW": bigbluebutton_data["password"][:16],
|
||||||
|
},
|
||||||
|
quote_via=quote,
|
||||||
|
)
|
||||||
|
|
||||||
join_params = urlencode(
|
checksum = hashlib.sha256(
|
||||||
{
|
("create" + create_params + settings.BIG_BLUE_BUTTON_SECRET).encode()
|
||||||
"meetingID": meeting_id,
|
).hexdigest()
|
||||||
"password": password,
|
|
||||||
"fullName": request.user.full_name,
|
try:
|
||||||
},
|
response = VideoCallSession().get(
|
||||||
quote_via=quote,
|
append_url_query_string(settings.BIG_BLUE_BUTTON_URL + "api/create", create_params)
|
||||||
|
+ "&checksum="
|
||||||
|
+ checksum
|
||||||
)
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
except requests.RequestException:
|
||||||
|
raise JsonableError(_("Error connecting to the BigBlueButton server."))
|
||||||
|
|
||||||
checksum = hashlib.sha256(
|
payload = ElementTree.fromstring(response.text)
|
||||||
("join" + join_params + settings.BIG_BLUE_BUTTON_SECRET).encode()
|
if payload.find("messageKey").text == "checksumError":
|
||||||
).hexdigest()
|
raise JsonableError(_("Error authenticating to the BigBlueButton server."))
|
||||||
redirect_url_base = append_url_query_string(
|
|
||||||
settings.BIG_BLUE_BUTTON_URL + "api/join", join_params
|
if payload.find("returncode").text != "SUCCESS":
|
||||||
)
|
raise JsonableError(_("BigBlueButton server returned an unexpected error."))
|
||||||
return redirect(append_url_query_string(redirect_url_base, "checksum=" + checksum))
|
|
||||||
|
join_params = urlencode(
|
||||||
|
{
|
||||||
|
"meetingID": bigbluebutton_data["meeting_id"],
|
||||||
|
# We use the moderator password here to grant ever user
|
||||||
|
# full moderator permissions to the bigbluebutton session.
|
||||||
|
"password": bigbluebutton_data["password"],
|
||||||
|
"fullName": request.user.full_name,
|
||||||
|
# https://docs.bigbluebutton.org/dev/api.html#create
|
||||||
|
# The createTime option is used to have the user redirected to a link
|
||||||
|
# that is only valid for this meeting.
|
||||||
|
#
|
||||||
|
# Even if the same link in Zulip is used again, a new
|
||||||
|
# createTime parameter will be created, as the meeting on
|
||||||
|
# the BigBlueButton server has to be recreated. (after a
|
||||||
|
# few minutes)
|
||||||
|
"createTime": payload.find("createTime").text,
|
||||||
|
},
|
||||||
|
quote_via=quote,
|
||||||
|
)
|
||||||
|
|
||||||
|
checksum = hashlib.sha256(
|
||||||
|
("join" + join_params + settings.BIG_BLUE_BUTTON_SECRET).encode()
|
||||||
|
).hexdigest()
|
||||||
|
redirect_url_base = append_url_query_string(
|
||||||
|
settings.BIG_BLUE_BUTTON_URL + "api/join", join_params
|
||||||
|
)
|
||||||
|
return redirect(append_url_query_string(redirect_url_base, "checksum=" + checksum))
|
||||||
|
|||||||
Reference in New Issue
Block a user