(backend) add 'username' query param when retrieving a room

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.
This commit is contained in:
lebaudantoine
2024-07-17 17:36:25 +02:00
committed by aleb_the_flash
parent faff1c1228
commit ae95a00301
4 changed files with 27 additions and 12 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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()