From ae95a003013a06c6c68fdd0a60c7de40dfed7502 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Wed, 17 Jul 2024 17:36:25 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(backend)=20add=20'username'=20query?= =?UTF-8?q?=20param=20when=20retrieving=20a=20room?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quick and dirty approach. It works, that's essential. Frontend can pass a desired username for the user. This would be the name displayed in the room to other participants. Usernames don't need to be unique, but user identities do If no username is passed, API will fall back to a default username. Why? This serves as a security mechanism. If the API is called incorrectly by a client, it maintains the previous behavior. --- src/backend/core/api/serializers.py | 6 +++++- src/backend/core/api/viewsets.py | 5 ++++- .../core/tests/rooms/test_api_rooms_retrieve.py | 14 +++++++++----- src/backend/core/utils.py | 14 +++++++++----- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/backend/core/api/serializers.py b/src/backend/core/api/serializers.py index 525f2873..5894fa88 100644 --- a/src/backend/core/api/serializers.py +++ b/src/backend/core/api/serializers.py @@ -122,10 +122,14 @@ class RoomSerializer(serializers.ModelSerializer): if role is not None or instance.is_public: slug = f"{instance.id!s}".replace("-", "") + username = request.GET.get("username", None) + output["livekit"] = { "url": settings.LIVEKIT_CONFIGURATION["url"], "room": slug, - "token": utils.generate_token(room=slug, user=request.user), + "token": utils.generate_token( + room=slug, user=request.user, username=username + ), } output["is_administrable"] = is_admin diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index add59be3..e02bc63d 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -188,12 +188,15 @@ class RoomViewSet( if not settings.ALLOW_UNREGISTERED_ROOMS: raise slug = slugify(self.kwargs["pk"]) + username = request.query_params.get("username", None) data = { "id": None, "livekit": { "url": settings.LIVEKIT_CONFIGURATION["url"], "room": slug, - "token": utils.generate_token(room=slug, user=request.user), + "token": utils.generate_token( + room=slug, user=request.user, username=username + ), }, } else: diff --git a/src/backend/core/tests/rooms/test_api_rooms_retrieve.py b/src/backend/core/tests/rooms/test_api_rooms_retrieve.py index 6e21c86d..26c4ac54 100644 --- a/src/backend/core/tests/rooms/test_api_rooms_retrieve.py +++ b/src/backend/core/tests/rooms/test_api_rooms_retrieve.py @@ -111,7 +111,9 @@ def test_api_rooms_retrieve_anonymous_unregistered_allowed(mock_token): }, } - mock_token.assert_called_once_with(room="unregistered-room", user=AnonymousUser()) + mock_token.assert_called_once_with( + room="unregistered-room", user=AnonymousUser(), username=None + ) @override_settings(ALLOW_UNREGISTERED_ROOMS=True) @@ -141,7 +143,9 @@ def test_api_rooms_retrieve_anonymous_unregistered_allowed_not_normalized(mock_t }, } - mock_token.assert_called_once_with(room="reunion", user=AnonymousUser()) + mock_token.assert_called_once_with( + room="reunion", user=AnonymousUser(), username=None + ) @override_settings(ALLOW_UNREGISTERED_ROOMS=False) @@ -229,7 +233,7 @@ def test_api_rooms_retrieve_authenticated_public(mock_token): "slug": room.slug, } - mock_token.assert_called_once_with(room=expected_name, user=user) + mock_token.assert_called_once_with(room=expected_name, user=user, username=None) def test_api_rooms_retrieve_authenticated(): @@ -327,7 +331,7 @@ def test_api_rooms_retrieve_members(mock_token, django_assert_num_queries): "slug": room.slug, } - mock_token.assert_called_once_with(room=expected_name, user=user) + mock_token.assert_called_once_with(room=expected_name, user=user, username=None) @mock.patch("core.utils.generate_token", return_value="foo") @@ -400,4 +404,4 @@ def test_api_rooms_retrieve_administrators(mock_token, django_assert_num_queries "slug": room.slug, } - mock_token.assert_called_once_with(room=expected_name, user=user) + mock_token.assert_called_once_with(room=expected_name, user=user, username=None) diff --git a/src/backend/core/utils.py b/src/backend/core/utils.py index 24921687..7055eae6 100644 --- a/src/backend/core/utils.py +++ b/src/backend/core/utils.py @@ -1,7 +1,7 @@ """ Utils functions used in the core app """ -import string +from typing import Optional from uuid import uuid4 from django.conf import settings @@ -9,12 +9,14 @@ from django.conf import settings from livekit.api import AccessToken, VideoGrants -def generate_token(room: string, user) -> str: +def generate_token(room: str, user, username: Optional[str] = None) -> str: """Generate a Livekit access token for a user in a specific room. Args: room (str): The name of the room. user (User): The user which request the access token. + username (Optional[str]): The username to be displayed in the room. + If none, a default value will be used. Returns: str: The LiveKit JWT access token. @@ -38,10 +40,12 @@ def generate_token(room: string, user) -> str: ).with_grants(video_grants) if user.is_anonymous: - # todo - allow passing a proper name for not logged-in user token.with_identity(str(uuid4())) + default_username = "Anonymous" else: - # todo - use user's fullname instead of its email for the displayed name - token.with_identity(user.sub).with_name(f"{user!s}") + token.with_identity(user.sub) + default_username = str(user) + + token.with_name(username or default_username) return token.to_jwt()