Commit Graph

147 Commits

Author SHA1 Message Date
lebaudantoine 5bac1668fe ♻️(fullstack) simplify source serialization
Simplify source serialization and validation logic while improving
type safety around room configuration handling.

Introduce a dedicated TypeScript type matching the backend
Pydantic model more precisely.

Also harmonize track source casing between frontend and backend to
remove redundant conversion logic and resolve #1282.
2026-05-17 23:39:53 +02:00
lebaudantoine 5a7a0da923 (backend) add synchronization mechanism for room configuration updates
Introduce synchronization of room configuration changes across
active participants.

When a room configuration is updated through a PUT operation, the
backend now performs an additional LiveKit API call to notify room
participants through a room metadata update event.

This ensures admins and owners quickly see up-to-date settings in
their administration panel. It also prepares the frontend for
automatic updates of unprivileged participants room’s data without
refetching it from the API.

An event-driven design was chosen instead of storing the full room
configuration in LiveKit metadata. While embedding the state
directly in metadata would provide immediate synchronization, it
would also require initializing and maintaining configuration
state during room creation or webhook handling, increasing the
risk of operational failures and regressions.

Instead, the backend emits lightweight synchronization events and
active clients update their React Query cache, which remains the
single source of truth for room configuration data.
2026-05-17 23:39:53 +02:00
lebaudantoine c20daafd81 (fullstack) support everyone_can_mute room configuration
Introduce a new room setting controlling whether all participants,
including non-privileged users, can mute others.

Update API validation accordingly and add the frontend controls
allowing administrators to toggle the option and persist the
configuration through the API.
2026-05-17 23:39:53 +02:00
lebaudantoine 288562cc0e 🛂(backend) allow participants to mute others based on room configuration
Enable any participant to mute others when the room configuration
allows it. This is enabled by default for all meetings unless
explicitly disabled by an administrator.

Privileged users retain the ability to mute any participant
regardless of the room configuration.
2026-05-17 23:39:53 +02:00
Damien Laine 4c5b6de8f3 (backend) make LiveKit Egress recording encoding configurable
Expose RECORDING_ENCODING_* settings to override the default LiveKit
Egress preset (H264_720P_30). When RECORDING_ENCODING_ENABLED is True,
the provided width/height/framerate/bitrate/keyframe values are passed
as advanced EncodingOptions. Lowering framerate and bitrate reduces
recording file size and egress worker CPU load.

Disabled by default, preserving current behaviour.
2026-05-05 18:26:49 +02:00
EpsilonFO a2bccf4f4f 🐛(backend) make start-recording atomic and fault-tolerant
Wrap Recording and RecordingAccess creation in a single transaction so a
partial failure does not leave orphan rows, and return 409 instead of 500
when a recording is already in progress for the room.

When the worker fails to start, transition the Recording to
FAILED_TO_START so the unique partial constraint on (room, status) no
longer blocks future recording attempts on the same room.
2026-05-04 22:15:14 +02:00
leo 0c0ce87947 🔒️(backend) validate Room configuration with Pydantic schema
Room.configuration accepted arbitrary JSON without validation, allowing unsafe
or malformed payloads to be stored and creating a security risk. Define a
Pydantic schema to enforce structure and constraints, and add validation
at the serializer level to reject invalid inputs.
2026-05-04 18:10:44 +02:00
lebaudantoine 6f38d60a27 (backend) support add-ons authentication in external viewset
Integrate the add-ons authentication backend into the externalviewset.

This allows third-party integrations (e.g. calendar add-ins) to
obtain a JWT for a user and use it to call the API (e.g. create a
room) via a Bearer authorization header.
2026-04-29 20:32:27 +02:00
lebaudantoine 012857f8c6 🔧(backend) add setting to toggle application token exchange mechanism
Introduce a configuration flag to enable or disable the
application token exchange (service account) mechanism.

This allows activating alternative authentication backends
without requiring full application token configuration.

Required to support the upcoming add-ons authentication backend.
2026-04-29 20:32:25 +02:00
lebaudantoine e5a804f748 🩹(backend) fix scope typo addons settings
The external viewset expected a plural "rooms:x" scope instead of
the incorrect singular "room:x".

Update it to accept the proper scope and emit tokens accordingly.
2026-04-29 20:32:04 +02:00
lebaudantoine 181b97b310 ♻️(backend) align CSRF token header with Django conventions
Update the CSRF header naming to follow Django standards,
avoiding duplicated client-side logic with inconsistent
header names.
2026-04-29 11:21:10 +02:00
lebaudantoine ecb710688d (backend) introduce add-ons authentication backend
Add a new authentication backend for add-ons, with a core service
managing session state in cache and exposing three API endpoints.

Microsoft Add-ins recommend launching authentication from the
side panel via a dialog, then returning the JWT to the parent
context using postMessage. However, due to Django and SSO security
constraints (window.opener not preserved), this approach is not
viable.

Implement a three-step authentication flow:

- `/init`: create a session, return a short-lived transit token
  and CSRF token. Store session ID in a secure HttpOnly cookie.
- `/poll`: allow the add-on to poll until authentication is
  complete, then consume the session and clear the cookie.
- `/exchange`: exchange the transit token for a JWT, which is
  later retrieved via the `/poll` endpoint.

The add-on opens an authentication dialog, stores the transit
token in sessionStorage, and performs the exchange after login.

This approach works within iframe constraints and provides a
sufficiently secure v0 despite known limitations.
2026-04-24 09:27:18 +02:00
leo 8507cdd2b6 (backend) add metadata collection of VAD, connection and chat events
Introduce MetadataCollector and MetadataCollectorService classes to
centralize the collection and storage of user connections, VAD events,
and chat messages. This creates a structured foundation for future speaker
assignment logic based on voice activity detection. Add tests for this new
feature.
2026-04-17 12:17:27 +02:00
lebaudantoine 3ccb2d4dd8 ♻️(backend) fix Twirp error mocking in tests
Tests were incorrectly mocking Twirp errors using HTTP status
codes instead of the meaningful error codes returned by the
LiveKit SDK.

Update mocks to reflect actual SDK behavior.
2026-04-09 00:37:56 +02:00
lebaudantoine 5d7a54e809 ♻️(backend) use Authorization header for LiveKit token authentication
Replace passing the LiveKit JWT in the request body with the
Authorization header, following standard authentication practices.

Extend the LiveKit authentication backend usage across additional
endpoints.

This also raises questions about how clients should securely
retrieve LiveKit tokens, to be addressed later.
2026-04-09 00:37:43 +02:00
lebaudantoine 07af7a85ff 🥅(backend) refine Twirp error handling for participant operations
Avoid mapping all Twirp errors to generic 500 responses.

Explicitly handle the case where a participant is no longer in
the room, as this may indicate suspicious behavior or a client
state issue.

Improve error discrimination to provide more accurate responses.
2026-04-09 00:37:42 +02:00
lebaudantoine 6180ac4e4f 🔒️(backend) rely on backend to allow participant update their metadata
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>
2026-04-09 00:37:42 +02:00
lebaudantoine 6b656eefd7 (backend) add unit tests for JwtTokenService
Introduce minimal unit test coverage for the JwtTokenService
to ensure its core behavior is validated.
2026-04-02 23:18:39 +02:00
lebaudantoine d915b93caa 🚨(backend) fix InsecureKeyLengthWarning in test suite
Resolve warnings raised in jwt/api_jwt.py:365 by ensuring test
keys meet the required security length.

Align test configuration with expected cryptographic standards.
2026-03-28 16:18:16 +01:00
lebaudantoine d5a614d2b5 🐛(backend) fix regression in update-participant endpoint
Serialization hardening introduced a breaking change between the
frontend and backend. Adjust the Pydantic model to restore
compatibility.

Reinstate support for can_subscribe_metric, which is passed by
default from the frontend.
2026-03-25 12:20:45 +01:00
lebaudantoine e9f90e95b1 🔒️(backend) fix email disclosure in room invitation endpoint
Prevent invited participants from seeing each other's email
addresses when sending room invitations.

Ensure invitations are sent with proper isolation to avoid
mass PII disclosure.

This mitigates risks of email harvesting, spam, and phishing
through the platform.
2026-03-25 09:52:43 +01:00
lebaudantoine dcb788b57b 🔒️(backend) avoid information exposure through exception messages
Sanitize error handling to prevent leaking internal details when
invalid or malicious requests are sent to the API.

Return generic error responses to reduce the risk of information
disclosure during probing attempts.
2026-03-13 17:33:55 +01:00
lebaudantoine f3e90c3999 ♻️(backend) clarify storage hook message for intentionally ignored req
Update the response message to explicitly state that certain
notifications are ignored on purpose, avoiding confusion
during debugging or log inspection.
2026-03-13 15:42:30 +01:00
lebaudantoine cb4ed3c9d7 🩹(backend) ignore non-recording uploads in storage webhook handler
With the introduction of background file uploads, a misconfigured
MinIO webhook could trigger the storage hook endpoint for unrelated
files.

While the dev setup now filters events via the MinIO lifecycle
configuration, add a safeguard at the application level.

Enforce a stricter filepath regex when parsing storage hook events
and ignore files outside the recording output directory.

Return a clean 200 response to acknowledge the webhook while
avoiding unnecessary processing.
2026-03-13 15:34:02 +01:00
Florent Chehab 3b719ab9ba (settings) disable file upload by default & max count
Add FILE_UPLOAD_ENABLED setting (default to False, to avoid
a breaking change). Also adds a max_count_by_user sub setting
to restrict the number of uploaded files per user.
2026-03-12 14:55:33 +01:00
lebaudantoine b0af5e7f35 🧪(backend) add failing test for client_id enumeration issue
Currently the inactive status is revealed before verifying the
secret, creating an authentication state oracle.

Introduce a failing test to capture the issue before
applying the fix.
2026-03-12 13:39:44 +01:00
lebaudantoine ca4494c09e ♻️(backend) align Application model field with is_active convention
Most models in the project use `is_active` rather than `active`.
The Application model was not aligned with this convention.

Rename the field and add a migration to standardize the naming.
Update related tests accordingly.

This change should not introduce breaking changes for external
applications.
2026-03-12 13:26:37 +01:00
lebaudantoine f57db0acc8 ♻️(backend) refactor uploaded file's key format
Remove the user-provided filename while preserving the extension,
and switch to the format `uuid.extension`.

This is more natural than the previous `uuid/.extension` format
and avoids confusion.

Update related tests accordingly.
2026-03-12 10:39:59 +01:00
lebaudantoine 051f33bb1e (backend) add tests for request-entry throttling in the lobby system
Cover throttling behavior for both authenticated users and
anonymous participants identified via the lobby cookie.
2026-03-11 18:07:39 +01:00
lebaudantoine c3fd1a89ef 🩹(backend) add page_size to pagination for room endpoints
Align room endpoints with the pagination behavior used across
other API endpoints and resolve issue #1055.
2026-03-11 16:27:02 +01:00
Florent Chehab dc278a6064 (backend) add file upload feature & tests
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.).
2026-03-06 11:31:39 +01:00
Florent Chehab 047da94494 (backend) add mimetype detection logic from drive
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.
2026-03-06 11:31:39 +01:00
lebaudantoine 42647d6d25 🦺(backend) strengthen API validation for recording options
Improve validation of parameters accepted when starting a
recording to prevent unsupported or unexpected values.

Language validation will be further tightened to only accept
languages supported by the transcribe microservice.

Add extensive API validation tests to cover these scenarios.
2026-03-03 19:05:15 +01:00
lebaudantoine bfbf253033 🔒️(backend) enhance API input validation to strengthen security
During the bug bounty, attempts were made to pass unexpected hidden
fields to manipulate room behavior and join as a ghost.

Treat these parameters as suspicious. They are not sent by the
frontend, so their presence likely indicates tampering.

Explicitly allow the parameters but emit warning logs to help detect
and investigate suspicious activity.
2026-03-03 13:30:10 +01:00
lebaudantoine f625df6508 ♻️(backend) refactor external API tests
Refactor tests to avoid duplicating JWT secret key configuration.

Introduce configuration of the JWT audience, which previously had no
default value.
2026-02-24 16:07:23 +01:00
lebaudantoine 69c6e58017 🔒️(backend) add application validation when consuming external JWT
Token generation already verifies that the application is active, but this
guarantee was not enforced when the token was used. This change adds a
runtime check to ensure the client_id claim matches an existing and active
application when evaluating permissions.

This also introduces an emergency revocation mechanism, allowing all previously
issued tokens for a given application to be invalidated if the application is
disabled.
2026-02-09 22:18:09 +01:00
lebaudantoine 3887255e9c ♻️(backend) rework permission to better align with DRF responsibilities
If a viewset action is not implemented, the permission layer no longer returns
a 403. Instead, it lets DRF handle the request and return the appropriate 405
Method Not Allowed response, ensuring cleaner and more standard API error
handling.
2026-02-09 12:16:12 +01:00
lebaudantoine 5d6ad3f3f6 🔒️(backend) enhance scope manipulation
Enhance scope manipulation by normalizing and sanitizing
scope values before processing.

Scopes are now converted to lowercase to ensure consistent behavior,
deduplicated while preserving their original order, and handled in a
deterministic way aligned with the intended authorization model.
2026-02-09 12:16:12 +01:00
lebaudantoine 44d68a9c80 (backend) strengthen external API viewset test coverage
Reinforce the test suite around the external API viewset to better
prevent regressions, permission leaks, and unexpected failures.

Adds additional scenarios covering permission enforcement, edge cases,
and error handling to ensure the external API behavior remains stable
and secure as it evolves.
2026-02-09 12:16:12 +01:00
lebaudantoine f8c6da8021 🔐(backend) enforce object-level permission checks on room endpoint
Apply strict permission validation on the external API room endpoint to
enforce the principle of least privilege. Unlike the default API (which allows
unauthenticated room retrieval and filters access in the serializer), the
external API now only exposes rooms to users with explicit permissions.

This change fixes a security issue. Slug-based room retrieval, as supported
by the default API, is not introduced here but could be added later if needed.
Retrieving rooms by UUID is retained, as guessing a UUID is significantly harder
than a slug.

A dedicated permission class was created to avoid coupling permissions between
the default and external APIs. The external API enforces stricter access rules.

Access policies may be revisited based on user and integrator feedback. The
external API currently has no production usage.
2026-02-09 12:16:12 +01:00
lebaudantoine 5ba1657e00 🧪(backend) add test exposing rooms permission flaw in external API
Add a failing test demonstrating that a user can retrieve a room they
do not have access to when the room UUID is known.

This highlights an improper object-level permission verification in the
external API. While exploitation requires obtaining the target room
UUID, this still represents a security issue (BOLA / IDOR class
vulnerability) and must be fixed.

The test documents the expected behavior and will pass once proper
access filtering or permission checks are enforced.
2026-02-09 12:16:12 +01:00
lebaudantoine 6962367e18 🐛(backend) fix notification tests broken by renaming env var
SCREEN_RECORDING_BASE_URL was renamed to RECORDING_DOWNLOAD_BASE_URL.

The new variable supersedes the old one, which is temporarily kept for backward
compatibility. This test failure was missed because the local common file was
out of sync with common.dist.

Add the new variable with a default value of None to ensure a smooth
deprecation path when the old variable is removed.
2026-02-07 00:14:49 +01:00
lebaudantoine 99a18b6e90 🩹(backend) use case-insensitive email matching in the external api
Fix a minor issue in the external API where users were matched using
case-sensitive email comparison, while authentication treats emails as
case-insensitive. This caused inconsistencies that are now resolved.

Spotted by T. Lemeur from Centrale.
2026-01-20 20:50:13 +01:00
lebaudantoine f6cdb1125b ♻️(backend) refactor backend recording state management
Instead of relying on the egress_started event—which fires when egress is
starting, not actually started—I now rely on egress_updated for more accurate
status updates. This is especially important for the active status, which
triggers after egress has truly joined the room. Using this avoids prematurely
stopping client-side listening to room.isRecording updates. A further
refactoring may remove reliance on room updates entirely.

The goal is to minimize handling metadata in the mediator class. egress_starting
is still used for simplicity, but egress_started could be considered in the
future.

Note: if the API to start egress hasn’t responded yet, the webhook may fail to
find the recording because it currently matches by worker ID. This is unstable.
A better approach would be to pass the database ID in the egress metadata and
recover the recording from it in the webhook.
2026-01-05 00:14:00 +01:00
lebaudantoine 16badde82d 🚧(backend) update recording metadata alongside recording state changes
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.
2026-01-04 20:22:15 +01:00
lebaudantoine 309c532811 (backend) submit screen recordings to the summary microservice
Screen recording are MP4 files containing video)

The current approach is suboptimal: the microservice will later be updated to
extract audio paths from video, which can be heavy to send to the Whisper
service.

This implementation is straightforward, but the notification service is now
handling many responsibilities through conditional logic. A refactor with a
more configurable approach (mapping attributes to processing steps via
settings) would be cleaner and easier to maintain.
For now, this works; further improvements can come later.

I follow the KISS principle, and try to make this new feature implemented
with the lesser impact on the codebase. This isn’t perfect.
2026-01-04 20:22:15 +01:00
lebaudantoine 0d8c76cd03 (backend) add a flexible JSON field to store recording options
Using a JSON field allows iterating on recording data without running a new
migration each time additional options or metadata need to be tracked.

This comes with trade-offs, notably weaker data validation and less clarity on
which data can be stored alongside a recording.

In the long run, this JSON field can be refactored into dedicated columns once
the feature and data model have stabilized.
2026-01-04 20:22:15 +01:00
lebaudantoine fe83c5fa07 (backend) add unit tests for user provisioning via external API
Add test coverage for provisional user creation through the external API,
including creating users with email-only (no sub)
2025-12-19 13:41:37 +01:00
lebaudantoine b0e27b38e2 🔒️(backend) avoid serializing rooms's pin code when restricted
Prevent anonymous users waiting in the lobby, or attacker
to discover the room pin code, that would allow them to join a room.
2025-12-17 10:05:23 +01:00
lebaudantoine fba879e739 (backend) allow prefixing resource server scopes
When declaring scopes with our OIDC provider, they require us to prefix
each scope with our application name. This is to prevent reserving generic
scopes like rooms:list for only our app, as they manage a large federation.

I’m proposing a workaround where, if a resource server prefix is detected in
the scope, it’s stripped out. This solution is simple and sufficient
in my opinion.

Since the scopes are defined in the database, I don’t want to update
them directly. Additionally, each self-hosted instance may have a different
application name, so the prefix should be configurable via a Django setting.
2025-12-10 19:47:36 +01:00