Implement lobby service using cache as LiveKit doesn't natively support secure lobby functionality. Their teams recommended to create our own system in our app's backend. The lobby system is totally independant of the DRF session IDs, making the request_entry endpoint authentication agnostic. This decoupling prevents future DRF changes from breaking lobby functionality and makes participant tracking more explicit. Security audit is needed as current LiveKit tokens have excessive privileges for unprivileged users. I'll offer more option ASAP for the admin to control participant privileges. Race condition handling also requires improvements, but should not be critical at this point. A great enhancement, would be to add a webhook, notifying the backend when the room is closed, to reset cache. This commit makes redis a prerequesite to run the suite of tests. The readme and CI will be updated in dedicated commits.
113 lines
3.2 KiB
Python
113 lines
3.2 KiB
Python
"""
|
|
Utils functions used in the core app
|
|
"""
|
|
|
|
# ruff: noqa:S311
|
|
|
|
import hashlib
|
|
import json
|
|
import random
|
|
from typing import Optional
|
|
from uuid import uuid4
|
|
|
|
from django.conf import settings
|
|
|
|
from livekit.api import AccessToken, VideoGrants
|
|
|
|
|
|
def generate_color(identity: str) -> str:
|
|
"""Generates a consistent HSL color based on a given identity string.
|
|
|
|
The function seeds the random generator with the identity's hash,
|
|
ensuring consistent color output. The HSL format allows fine-tuned control
|
|
over saturation and lightness, empirically adjusted to produce visually
|
|
appealing and distinct colors. HSL is preferred over hex to constrain the color
|
|
range and ensure predictability.
|
|
"""
|
|
|
|
# ruff: noqa:S324
|
|
identity_hash = hashlib.sha1(identity.encode("utf-8"))
|
|
# Keep only hash's last 16 bits, collisions are not a concern
|
|
seed = int(identity_hash.hexdigest(), 16) & 0xFFFF
|
|
random.seed(seed)
|
|
hue = random.randint(0, 360)
|
|
saturation = random.randint(50, 75)
|
|
lightness = random.randint(25, 60)
|
|
|
|
return f"hsl({hue}, {saturation}%, {lightness}%)"
|
|
|
|
|
|
def generate_token(
|
|
room: str, user, username: Optional[str] = None, color: 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.
|
|
color (Optional[str]): The color to be displayed in the room.
|
|
If none, a value will be generated
|
|
|
|
Returns:
|
|
str: The LiveKit JWT access token.
|
|
"""
|
|
video_grants = VideoGrants(
|
|
room=room,
|
|
room_join=True,
|
|
room_admin=True,
|
|
can_update_own_metadata=True,
|
|
can_publish_sources=[
|
|
"camera",
|
|
"microphone",
|
|
"screen_share",
|
|
"screen_share_audio",
|
|
],
|
|
)
|
|
|
|
if user.is_anonymous:
|
|
identity = str(uuid4())
|
|
default_username = "Anonymous"
|
|
else:
|
|
identity = str(user.sub)
|
|
default_username = str(user)
|
|
|
|
if color is None:
|
|
color = generate_color(identity)
|
|
|
|
token = (
|
|
AccessToken(
|
|
api_key=settings.LIVEKIT_CONFIGURATION["api_key"],
|
|
api_secret=settings.LIVEKIT_CONFIGURATION["api_secret"],
|
|
)
|
|
.with_grants(video_grants)
|
|
.with_identity(identity)
|
|
.with_name(username or default_username)
|
|
.with_metadata(json.dumps({"color": color}))
|
|
)
|
|
|
|
return token.to_jwt()
|
|
|
|
|
|
def generate_livekit_config(
|
|
room_id: str, user, username: str, color: Optional[str] = None
|
|
) -> dict:
|
|
"""Generate LiveKit configuration for room access.
|
|
|
|
Args:
|
|
room_id: Room identifier
|
|
user: User instance requesting access
|
|
username: Display name in room
|
|
|
|
Returns:
|
|
dict: LiveKit configuration with URL, room and access token
|
|
"""
|
|
return {
|
|
"url": settings.LIVEKIT_CONFIGURATION["url"],
|
|
"room": room_id,
|
|
"token": generate_token(
|
|
room=room_id, user=user, username=username, color=color
|
|
),
|
|
}
|