Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 46ed200

Browse files
committed
media/create: enforce limit on number of pending uploads
Signed-off-by: Sumner Evans <[email protected]>
1 parent 2dcc63f commit 46ed200

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

synapse/media/media_repository.py

+17
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,23 @@ async def create_media_id(self, auth_user: UserID) -> Tuple[str, int]:
197197
)
198198
return f"mxc://{self.server_name}/{media_id}", unused_expires_at
199199

200+
async def reached_pending_media_limit(
201+
self, auth_user: UserID, limit: int
202+
) -> Tuple[bool, int]:
203+
"""Check if the user is over the limit for pending media uploads.
204+
Args:
205+
auth_user: The user_id of the uploader
206+
limit: The maximum number of pending media uploads a user is allowed to have
207+
Returns:
208+
A tuple with a boolean and an integer indicating whether the user has too
209+
many pending media uploads and the timestamp at which the first pending
210+
media will expire, respectively.
211+
"""
212+
pending, first_expiration_ts = await self.store.count_pending_media(
213+
user_id=auth_user
214+
)
215+
return pending >= limit, first_expiration_ts
216+
200217
async def verify_can_upload(self, media_id: str, auth_user: UserID) -> None:
201218
"""Verify that the media ID can be uploaded to by the given user. This
202219
function checks that:

synapse/rest/media/create_resource.py

+14
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(self, hs: "HomeServer", media_repo: "MediaRepository"):
3636
self.media_repo = media_repo
3737
self.clock = hs.get_clock()
3838
self.auth = hs.get_auth()
39+
self.max_pending_media_uploads = hs.config.media.max_pending_media_uploads
3940

4041
# A rate limiter for creating new media IDs.
4142
self._create_media_rate_limiter = Ratelimiter(
@@ -62,6 +63,19 @@ async def _async_render_POST(self, request: SynapseRequest) -> None:
6263
retry_after_ms=int(1000 * (time_allowed - time_now_s))
6364
)
6465

66+
(
67+
reached_pending_limit,
68+
first_expiration_ts,
69+
) = await self.media_repo.reached_pending_media_limit(
70+
requester.user, self.max_pending_media_uploads
71+
)
72+
if reached_pending_limit:
73+
raise LimitExceededError(
74+
msg="You have too many uploads pending. Please finish uploading your "
75+
"existing files.",
76+
retry_after_ms=first_expiration_ts - self.clock.time_msec(),
77+
)
78+
6579
content_uri, unused_expires_at = await self.media_repo.create_media_id(
6680
requester.user
6781
)

synapse/storage/databases/main/media_repository.py

+25
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
)
2828

2929
from synapse.api.constants import Direction
30+
from synapse.api.errors import StoreError
3031
from synapse.storage._base import SQLBaseStore
3132
from synapse.storage.database import (
3233
DatabasePool,
@@ -405,6 +406,30 @@ async def mark_local_media_as_safe(self, media_id: str, safe: bool = True) -> No
405406
desc="mark_local_media_as_safe",
406407
)
407408

409+
async def count_pending_media(self, user_id: UserID) -> Tuple[int, int]:
410+
"""Count the number of pending media for a user.
411+
Returns:
412+
A tuple of two integers: the total pending media requests and the earliest
413+
expiration timestamp.
414+
"""
415+
416+
def get_pending_media_txn(txn: LoggingTransaction) -> Tuple[int, int]:
417+
sql = (
418+
"SELECT COUNT(*), MIN(unused_expires_at)"
419+
" FROM local_media_repository"
420+
" WHERE user_id = ?"
421+
" AND quarantined_by IS NULL"
422+
" AND unused_expires_at > ?"
423+
" AND media_length IS NULL"
424+
)
425+
txn.execute(sql, (user_id.to_string(), self._clock.time_msec()))
426+
row = txn.fetchone()
427+
if not row:
428+
raise StoreError(404, "Failed to count pending media for user")
429+
return row[0], row[1] or 0
430+
431+
return await self.db_pool.runInteraction("get_url_cache", get_pending_media_txn)
432+
408433
async def get_url_cache(self, url: str, ts: int) -> Optional[Dict[str, Any]]:
409434
"""Get the media_id and ts for a cached URL as of the given timestamp
410435
Returns:

0 commit comments

Comments
 (0)