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.
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.
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.
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>
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.
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.
Email addresses are PII and should not appear in technical or
error logs.
Sanitize logging to avoid exposing sensitive user data when
invitation sending fails.
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.
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.
The dot before (?P<extension>...) was not escaped and matched any
character instead of a literal period.
Escape it to align with MEDIA_STORAGE_URL_PATTERN, which correctly
uses \. for the file extension separator.
Update the response message to explicitly state that certain
notifications are ignored on purpose, avoiding confusion
during debugging or log inspection.
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.
Avoid duplicating patterns that match the same logical unit by
sharing common parts between the two regexes.
This logical factorization improves maintainability and ensures
consistent parsing behavior across S3 event handlers.
Initially I avoided coupling the recording path regex with the
setting that provides the Django static URL. However the new
file viewset already relies on it, and Drive does as well.
For consistency, update the recording regex to use the same
STATIC_URL-based approach.
While the coupling may not feel ideal, having two different
regex strategies for similar file paths would be worse.
Combine the regex used in Drive with the one from the recording
feature and centralize them in the enums module.
Although the module name suggests only enums, it also hosts
shared constants used across the codebase.
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.
Authenticate the application secret before checking whether the
application is inactive.
This avoids revealing the inactive status of an application when
an incorrect secret is provided, preventing an authentication
state oracle and client_id enumeration.
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.
Add minimal admin support to allow administrators to mark an
application account as inactive if it becomes rogue or compromised.
This capability was missing when the application concept was
initially introduced in the backend.
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.
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.
Making the status read-only improved security and enforced least
privilege, but in production some recordings could not be properly
stopped, leaving them in an active state.
Introduce an admin action to allow support staff to mark recordings
as "failed to stop" or update their status when necessary.
Throttle the request-entry endpoint for authenticated users also
to guard against accidental hammering from buggy clients.
Authenticated users are throttled via RequestEntryUserRateThrottle.
Anonymous users are throttled using the lobby participant cookie
through RequestEntryAnonRateThrottle.
use the lobby participant cookie ID as the throttle cache key
rather than the client IP address.
This avoids penalising multiple users behind the same NAT or proxy
and aligns throttling with how LobbyService identifies participants.
This bug was spotted in production where users from ministries behind
NAT was blocked by throttling while using visio.
If no cookie is present yet, skip throttling for the request. The
cookie will be set on the first response and throttling will apply
from subsequent requests.
This throttle is intended to protect against accidental hammering
from buggy clients, not as a security control against DoS attacks.
This is not a security measure, we should use a WAF.
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.
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.
Use settings directly in migrations to avoid noop
migrations. This might have undisered side effects
if we change the config over time 'invalid' data will be
in the database.
It's a simple quick fix.
Keeping some migrations that are no useless to avoid changing
too much the migration history for users.
Similar to https://github.com/suitenumerique/people/commit/
469014ac415b25be0ceed08b31a87d2d40d743cd
These fields previously triggered a suspicious operation exception
when passed to the API.
Make the list configurable so the serializer behavior can be
adjusted without requiring a new release.
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.
Transcription and summarization results were always generated
using a French text structure (e.g. "Réunion du..."), regardless
of user preference or meeting language. Introduced basic localization
support to adapt generated string languages.
Refactor external API authentication classes to inherit from a
common base authentication backend.
Prepare the introduction of a new authentication class responsible
for verifying tokens provided to calendar integrations.
Move token decoding responsibility to the new token service so it
can both generate and validate tokens.
Encapsulate external exceptions and expose a clear interface by
defining custom Python exceptions raised during token validation.
Taken from #897.
Encapsulate token generation logic for authenticating to the
external API in a well-scoped service.
This service can later be reused in other parts of the codebase,
especially for providing tokens required by calendar integrations.
Commit was cherry picked from #897
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.
Use a mixin, introduced by @lunika in the shared
backend library to monitor throttling behavior.
The mixin tracks when throttling limits are reached, sending errors to Sentry
to trigger alerts when configured. This helps detect misconfigurations,
fine-tune throttling settings, and identify suspicious operations.
This enables safely increasing API throttling limits while ensuring stability,
providing confidence that higher limits won’t break the system.
Extract throttling classes into a dedicated Python module, following the
structure of suitenumerique/docs.
This is a preparatory refactor to ease upcoming changes to the throttling
implementation. No functional behavior change is introduced in this commit.
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.
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.
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.
The previous replace usage was too broad and could remove multiple
occurrences, which was not the original intention.
Replace the replace call with removeprefix, which more accurately
matches the expected behavior by only removing the prefix when present
at the start of the string.
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.
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.
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.
Replace the basic select component that loaded thousands of options into the
DOM with a smarter component supporting dynamic loading and search.
With large user bases, linking users to recording access caused massive option
lists to render, severely impacting performance. This change dramatically
improves page loading speed.
These values should not be updated from the admin interface. Allowing changes
to a recording’s associated room could lead to data leaks (e.g., notifications
being resent to the wrong users after a malicious modification).
Also remove the room select field, which rendered a dropdown with ~150k options,
flooding the DOM and severely degrading page performance.