The backend previously passed an abstract is_admin_or_owner boolean
flag in the LiveKit token. That kept the frontend minimalistic and
saved it from having to handle role comparisons.
As we introduce more features that need to distinguish between the
room owner and admins, refactor the token to carry the role
directly. The frontend can then derive the relevant flags from a
richer piece of information.
Add a new setting that controls whether an authenticated user can
rename or modify their display name, or if they must use the one
returned by the SSO.
When the setting is disabled, only anonymous users can set a display
name freely; authenticated users always use their SSO display name.
Requested by many self-hosters.
This commit introduces the compatibility with summary v2.
It tecnically doesn't break the compatibility with v1 as
v1 params are still sent. But we advise people using the
transcribe feature in their own deployments to adapt to the
new v2 API, as this compatibility will be removed in a
future major version.
* RecordingStatusChoices now has
EXTERNAL_PROCESS_SUCCESSFUL
& EXTERNAL_PROCESS_FAILED
Values, which are changed by a new webhook that
can be called by the transcribe service.
This webhook is protected by its own bearer token.
* Title for the document is computed in visio,
* Tests are added / updated accordingly
Before this commit, post a file check, the policy could be
reused to change the verified file.
Now, files are uplaoded to a temporary location, then inside
a transaction that prevents concurrent calls, the file is
copied to its final destination and the checks are run on that one.
A new file can still be updated with the policy but it will never be read, etc.
As part of this change, all files in the new tmp directory
on s3 should have an expiration policy.
Use the Python port of Google's phone number library on the backend
instead of the JS one on the client. Saves 140Kb of unnecessary JS
and avoids a complex dynamic import that would have been required to
optimize loading.
The transformation is static: the phone number lives in the Django
settings, so the backend can format it once and pass the result to
the client. This avoids every client loading the 140Kb library and
re-computing the same information.
Moreover, within a single client, the transformation was previously
re-computed several times during a webapp lifecycle.
Introduce toggle-hand and rename endpoints in RoomViewSet,
secured with LiveKit token authentication.
Remove direct permission for clients to update their own metadata
via LiveKit tokens to prevent spoofing (e.g. faking admin status).
Proxy participant metadata updates through the backend to enforce
proper validation and authorization.
Signed-off-by: lebaudantoine <lebaud.antoine131@gmail.com>
For the coming features we will need to store files on the meet side.
(for instance user backgrounds).
This commits adds a new Model to manage files, and the associated
serializers & viewsets. All are tested.
This work was heavily inspired by the work done by our friends at
https://github.com/suitenumerique/drive
It build on the same architecture design (upload directly to S3 but
download goes through our proxy), but model is much much simplier
(no folders, no file sharing, etc.).
Reused the logic developed by the team working on drive.
This is usefull for our own upload file backend (that will
come in later commits).
Dockefile was updated to add a required system dependency.
We might want to put this shared logic in a lib.
Previously, this was handled manually by the client, sending notifications to
other participants and keeping the recording state only in memory. There was no
shared or persisted state, so leaving and rejoining a meeting lost this
information. Delegating this responsibility solely to the client was a poor
choice.
The backend now owns this responsibility and relies on LiveKit webhooks to keep
room metadata in sync with the egress lifecycle.
This also reveals that the room.isRecording attribute does not update as fast
as the egress stop event, which is unexpected and should be investigated
further.
This will make state management working when several room’s owner will be in
the same meeting, which is expected to arrive any time soon.
From a security perspective, the list endpoint should be limited to return only
rooms created by the external application. Currently, there is a risk of
exposing public rooms through this endpoint.
I will address this in upcoming commits by updating the room model to track
the source of generation. This will also provide useful information
for analytics.
The API viewset was largely copied and adapted. The serializer was heavily
restricted to return a response more appropriate for external applications,
providing ready-to-use information for their users
(for example, a clickable link).
I plan to extend the room information further, potentially aligning it with the
Google Meet API format. This first draft serves as a solid foundation.
Although scopes for delete and update exist, these methods have not yet been
implemented in the viewset. They will be added in future commits.
We need to integrate with external applications. Objective: enable them to
securely generate room links with proper ownership attribution.
Proposed solution: Following the OAuth2 Machine-to-Machine specification,
we expose an endpoint allowing external applications to exchange a client_id
and client_secret pair for a JWT. This JWT is valid only within a well-scoped,
isolated external API, served through a dedicated viewset.
This commit introduces a model to persist application records in the database.
The main challenge lies in generating a secure client_secret and ensuring
it is properly stored.
The restframework-apikey dependency was discarded, as its approach diverges
significantly from OAuth2. Instead, inspiration was taken from oauthlib and
django-oauth-toolkit. However, their implementations proved either too heavy or
not entirely suitable for the intended use case. To avoid pulling in large
dependencies for minimal utility, the necessary components were selectively
copied, adapted, and improved.
A generic SecretField was introduced, designed for reuse and potentially
suitable for upstream contribution to Django.
Secrets are exposed only once at object creation time in the Django admin.
Once the object is saved, the secret is immediately hashed, ensuring it can
never be retrieved again.
One limitation remains: enforcing client_id and client_secret as read-only
during edits. At object creation, marking them read-only excluded them from
the Django form, which unintentionally regenerated new values.
This area requires further refinement.
The design prioritizes configurability while adhering to the principle of least
privilege. By default, new applications are created without any assigned scopes,
preventing them from performing actions on the API until explicitly configured.
If no domain is specified, domain delegation is not applied, allowing tokens
to be issued for any email domain.
Configure LiveKit token to explicitly allow users to subscribe to other
participants' video and audio tracks instead of relying on default
permissions.
Switch from metadata to attributes when generating LiveKit tokens for
more convenient dict-like structure handling during token creation and
client-side reading.
Attributes provide better data structure flexibility compared to
metadata, simplifying both server-side token generation and client-side
data access patterns.
Refactor lobby system to use consistent UUID v4 across lobby
registration and LiveKit token participant identity instead of
generating separate UUIDs.
Maintains synchronized identifiers between lobby cache and LiveKit
participants, simplifying future participant removal operations by
using the same UUID reference across both systems.
Extend LiveKit token creation utility with additional room configuration
and user role parameters to properly adapt room_admin grants and
publish sources based on permission levels.
This creates technical debt in utility function design that should be
refactored into proper service architecture for token
generation operations in future iterations.
Move from lobby service to utils for reuse across services. Method is
generic enough for utility status. Future: create dedicated LiveKit
service to encapsulate all LiveKit-related utilities.
Create dedicated utility function for livekit API client initialization.
Centralizes configuration logic including custom session handling for SSL
verification. Improves code reuse across backend components that interact
with LiveKit.
Implement secure recording file access through authentication instead of
exposing S3 bucket or using temporary signed links with loose permissions.
Inspired by docs and @spaccoud's implementation, with comprehensive
viewset checks to prevent unauthorized recording downloads.
The ingress reserved to media intercept the original request, and thanks to
Nginx annotations, check with the backend if the user is allowed to donwload
this recording file. This might introduce a dependency to Nginx in the project
by the way.
Note: Tests are integration-based rather than unit tests, requiring minio in
the compose stack and CI environment. Implementation includes known botocore
deprecation warnings that per GitHub issues won't be resolved for months.
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.
Extract serialization logic for LiveKit server connection data to make it
reusable across endpoints. Function naming will be improved in future
refactoring when utility functions are moved to a proper service.
'hash' built-in function is randomly seed by Python process.
In staging or production, our backend runs over 3 pods, thus 3
Python processes. For a given identity, it was not prompting
the same hash across all pods.
Why 'hash' is randomly seed? For security reasons, there was
a vulnerability disclosure exploiting key collision. Since Python 3.2,
'hash' is by default randomly seed.
Fixed it! Thx @jonathanperret for your help.
Participants need to be room admin to interact with LiveKit server
api. Until we offer private room, with moderation, all participants
will be considered as room admin.
note: room admin doesn't offer permission to record a room. please,
refer to LiveKit documentation to learn more.
Color is stored in participant's metadata.
Using a hash of some participant's info allow to persist the color
for logged-in participant without storing the color in db.
Please feel free to tune the saturation and lightness range.
Code is inspired by a tutorial found online.
Switched to using query parameters instead of GET requests, enabling the
inclusion of additional parameters when calling the create endpoint via POST.
Simplified the access token generation process by removing redundant calls to
`with_identity` and consolidating token generation steps. This streamlines the
method flow by preparing all necessary data before generating the access token.
Recent updates of dev/ruff and dev/pylint dependencies led
to new linting warnings.
Pylint 3.2.0 introduced a new check `possibly-used-before-assignment`,
which ensures variables are defined regardless of conditional statements.
Some if/else branches were missing defaults. These have been fixed.
The Pylint job was failing due to those TODO items. In our make lint
command sequence, Pylint runs first. If it fails, Ruff won't run,
which is quite inconvenient.
I've extracted those TODOs into an issue for further review.
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.
Introduce a utility function to issue a basic LiveKit access token with the minimal
required video grants for videoconferencing.
/!\ This function is naive, and doesn’t handle properly all cases. It’s under construction.
Testing was conducted using the LiveKit connection test tool https://livekit.io/connection-test,
which allows users to input the address of their local LiveKit server and an access token.
** Upcoming improvements? **
- Unit tests should be added.
- User display name should be their full name instead of their email address.
- Anonymous users should be allowed to provide a full name when requesting access to the room.
- Video grants should be adapted based on the room configuration and the user's role.
These improvements will be addressed in future commits.
Nevertheless, with this draft, we should be able to address various situations, including
public rooms, permanent rooms, temporary rooms, logged-in users, and anonymous users.