Source code for bigbluebutton.django.views

from typing import Optional

from django.contrib.sites.shortcuts import get_current_site
from django.db.models import QuerySet
from django.http import HttpRequest, HttpResponse
from django.views import View

from defusedxml import minidom
from dicttoxml import dicttoxml

from ..api.bigbluebutton import BigBlueButton as _BigBlueButton
from .models import APIToken


[docs]class APIView(View): # Use a matching HTTP status for each error case # The BBB reference implementation always returns 200, we deliberately violate that # Set to False to mimic the reference implementation more closely _fix_status = True def dispatch(self, request: HttpRequest, method: str) -> HttpResponse: # Find API tokens to allow for this request site = get_current_site(request) api_tokens = APIToken.objects.filter(server_group__site=site) api_token = self._find_token(request, method, api_tokens) if not api_token: # We did not find a token who's salt verifies the request (or none at all) return self._error(498, "checksumError", "You did not pass the checksum security check") # Verify we got a valid HTTP method if request.method not in ("GET", "POST"): return self._error( 405, "xInfraBlueMethodNotAllowed", "The HTTP method {request.method} ist now allowed", ) # Determine token scope and build metadata filter if api_token.scope == "global": filter_meta = {} elif api_token.scope == "user": filter_meta = {"infrablue-user": api_token.user.bbb_user_id} elif api_token.scope == "token": filter_meta = {"infrablue-token": api_token.guid} else: raise TypeError("API token has unexpected scope") # Proprietary extension for test purposes if method == "ping": return self._success( "xInfraBluePong", "The API is working correctly and your request was valid." ) # Create mutable copy of request args without checksum attrs = request.GET.copy() del attrs["checksum"] try: # Hand off handling to python-bigbluebutton2 res = api_token.server_group.api_group.handle_from_data( method, attrs, request.body, filter_meta ) return self._success("", res) except TypeError: return self._error( 404, "xInfraBlueUnknownAPICall", "The API call {method} is not known" ) def _xml_response( self, status: int, return_code: str, message_key: str, message: str, attrs: Optional[dict] = None, ) -> HttpResponse: # Inject mandatory attributes res = attrs.copy() if attrs else {} res.update(dict(reurncode=return_code, messageKey=message_key, message=message)) # Generate XML and prettify xml = dicttoxml(res, attr_type=False, custom_root="response") xml = minidom.parseString(xml).toprettyxml() return HttpResponse(xml, status=status, content_type="text/xml") def _error(self, status: int, message_key: str, message: str) -> HttpResponse: return self._xml_response(status, "FAILED", message_key, message) def _success( self, message_key: str, message: str, attrs: Optional[dict] = None ) -> HttpResponse: return self._xml_response(200, "SUCCESS", message_key, message, attrs) def _find_token( self, request: HttpRequest, method: str, api_tokens: QuerySet ) -> Optional[APIToken]: for api_token in api_tokens: # Try authenticating the request with each token candiate ret = self._auth_request(request, method, api_token) if ret: # We found a valid token return api_token return None def _auth_request(self, request: HttpRequest, method: str, api_token: APIToken) -> bool: # Extract signed part of query string query = request.META["QUERY_STRING"] if query.startswith("checksum="): query = "" elif "&checksum=" in query: query = query[: query.index("&checksum=")] else: # Request is unauthenticated if no checksum present return False # Calculate checksum and compare checksum = _BigBlueButton.request_checksum(method, query, api_token.salt) return checksum == request.GET.get("checksum", "")