Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/18876.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for experimental [MSC4335](https://github.com/matrix-org/matrix-spec-proposals/pull/4335) M_USER_LIMIT_EXCEEDED error code for media upload limits.
15 changes: 15 additions & 0 deletions docs/usage/configuration/config_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2174,13 +2174,28 @@ These settings can be overridden using the `get_media_upload_limits_for_user` mo

Defaults to `[]`.

Options for each entry include:

* `time_period` (duration): The time period over which the limit applies. Required.

* `max_size` (byte size): Amount of data that can be uploaded in the time period by the user. Required.

* `msc4335_info_uri` (string): Experimental MSC4335 URI to where the user can find information about the upload limit. Optional.

* `msc4335_soft_limit` (boolean): Experimental MSC4335 value to say if the limit can be increased. Optional.

* `msc4335_increase_uri` (string): Experimental MSC4335 URI to where the user can increase the upload limit. Required if msc4335_soft_limit is true.

Example configuration:
```yaml
media_upload_limits:
- time_period: 1h
max_size: 100M
- time_period: 1w
max_size: 500M
msc4335_info_uri: https://example.com/quota
msc4335_soft_limit: true
msc4335_increase_uri: https://example.com/increase-quota
```
---
### `max_image_pixels`
Expand Down
38 changes: 29 additions & 9 deletions schema/synapse-config.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2424,20 +2424,40 @@ properties:
module API [callback](../../modules/media_repository_callbacks.md#get_media_upload_limits_for_user).
default: []
items:
time_period:
type: "#/$defs/duration"
description: >-
The time period over which the limit applies. Required.
max_size:
type: "#/$defs/bytes"
description: >-
Amount of data that can be uploaded in the time period by the user.
Required.
type: object
required:
- time_period
- max_size
properties:
time_period:
$ref: "#/$defs/duration"
description: >-
The time period over which the limit applies. Required.
max_size:
$ref: "#/$defs/bytes"
description: >-
Amount of data that can be uploaded in the time period by the user.
Required.
msc4335_info_uri:
type: string
description: >-
Experimental MSC4335 URI to where the user can find information about the upload limit. Optional.
msc4335_soft_limit:
type: boolean
description: >-
Experimental MSC4335 value to say if the limit can be increased. Optional.
msc4335_increase_uri:
type: string
description: >-
Experimental MSC4335 URI to where the user can increase the upload limit. Required if msc4335_soft_limit is true.
examples:
- - time_period: 1h
max_size: 100M
- time_period: 1w
max_size: 500M
msc4335_info_uri: https://example.com/quota
msc4335_soft_limit: true
msc4335_increase_uri: https://example.com/increase-quota
max_image_pixels:
$ref: "#/$defs/bytes"
description: Maximum number of pixels that will be thumbnailed.
Expand Down
33 changes: 33 additions & 0 deletions synapse/api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ class Codes(str, Enum):
# Part of MSC4326
UNKNOWN_DEVICE = "ORG.MATRIX.MSC4326.M_UNKNOWN_DEVICE"

MSC4335_USER_LIMIT_EXCEEDED = "ORG.MATRIX.MSC4335_USER_LIMIT_EXCEEDED"


class CodeMessageException(RuntimeError):
"""An exception with integer code, a message string attributes and optional headers.
Expand Down Expand Up @@ -513,6 +515,37 @@ def error_dict(self, config: Optional["HomeServerConfig"]) -> "JsonDict":
)


class MSC4335UserLimitExceededError(SynapseError):
"""
Experimental implementation of MSC4335 M_USER_LIMIT_EXCEEDED error
"""

def __init__(
self,
code: int,
msg: str,
info_uri: str,
soft_limit: bool,
increase_uri: Optional[str] = None,
):
if soft_limit and increase_uri is None:
raise ValueError("increase_uri must be provided if soft_limit is True")

additional_fields: dict[str, Union[str, bool]] = {
"org.matrix.msc4335.info_uri": info_uri,
"org.matrix.msc4335.soft_limit": soft_limit,
}
if soft_limit and increase_uri is not None:
additional_fields["org.matrix.msc4335.increase_uri"] = increase_uri

super().__init__(
code,
msg,
Codes.MSC4335_USER_LIMIT_EXCEEDED,
additional_fields=additional_fields,
)


class EventSizeError(SynapseError):
"""An error raised when an event is too big."""

Expand Down
3 changes: 3 additions & 0 deletions synapse/config/experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,3 +598,6 @@ def read_config(
# MSC4306: Thread Subscriptions
# (and MSC4308: Thread Subscriptions extension to Sliding Sync)
self.msc4306_enabled: bool = experimental.get("msc4306_enabled", False)

# MSC4335: M_USER_LIMIT_EXCEEDED error
self.msc4335_enabled: bool = experimental.get("msc4335_enabled", False)
39 changes: 37 additions & 2 deletions synapse/config/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import logging
import os
from typing import Any, Dict, List, Tuple
from typing import Any, Dict, List, Optional, Tuple

import attr

Expand Down Expand Up @@ -134,6 +134,15 @@ class MediaUploadLimit:
time_period_ms: int
"""The time period in milliseconds."""

msc4335_info_uri: Optional[str] = None
"""Used for experimental MSC4335 error code feature"""

msc4335_soft_limit: Optional[bool] = None
"""Used for experimental MSC4335 error code feature"""

msc4335_increase_uri: Optional[str] = None
"""Used for experimental MSC4335 error code feature"""


class ContentRepositoryConfig(Config):
section = "media"
Expand Down Expand Up @@ -302,8 +311,34 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
for limit_config in config.get("media_upload_limits", []):
time_period_ms = self.parse_duration(limit_config["time_period"])
max_bytes = self.parse_size(limit_config["max_size"])
msc4335_info_uri = limit_config.get("msc4335_info_uri", None)
msc4335_soft_limit = limit_config.get("msc4335_soft_limit", None)
msc4335_increase_uri = limit_config.get("msc4335_increase_uri", None)

self.media_upload_limits.append(MediaUploadLimit(max_bytes, time_period_ms))
if (
msc4335_info_uri is not None
or msc4335_soft_limit is not None
or msc4335_increase_uri is not None
) and (not (msc4335_info_uri and msc4335_soft_limit is not None)):
raise ConfigError(
"If any of msc4335_info_uri, msc4335_soft_limit or "
"msc4335_increase_uri are set, then both msc4335_info_uri and "
"msc4335_soft_limit must be set."
)
if msc4335_soft_limit and not msc4335_increase_uri:
raise ConfigError(
"msc4335_increase_uri must be set if msc4335_soft_limit is true."
)

self.media_upload_limits.append(
MediaUploadLimit(
max_bytes,
time_period_ms,
msc4335_info_uri,
msc4335_soft_limit,
msc4335_increase_uri,
)
)

def generate_config_section(self, data_dir_path: str, **kwargs: Any) -> str:
assert data_dir_path is not None
Expand Down
21 changes: 21 additions & 0 deletions synapse/media/media_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
Codes,
FederationDeniedError,
HttpResponseException,
MSC4335UserLimitExceededError,
NotFoundError,
RequestSendFailed,
SynapseError,
Expand Down Expand Up @@ -67,6 +68,7 @@
from synapse.media.storage_provider import StorageProviderWrapper
from synapse.media.thumbnailer import Thumbnailer, ThumbnailError
from synapse.media.url_previewer import UrlPreviewer
from synapse.rest.admin.experimental_features import ExperimentalFeature
from synapse.storage.databases.main.media_repository import LocalMedia, RemoteMedia
from synapse.types import UserID
from synapse.util.async_helpers import Linearizer
Expand Down Expand Up @@ -379,6 +381,25 @@ async def create_or_update_content(
sent_bytes=uploaded_media_size,
attempted_bytes=content_length,
)
# If the MSC4335 experimental feature is enabled and the media limit
# has the info_uri configured then we raise the MSC4335 error
msc4335_enabled = await self.store.is_feature_enabled(
auth_user.to_string(), ExperimentalFeature.MSC4335
)
if (
msc4335_enabled
and limit.msc4335_info_uri
and limit.msc4335_soft_limit is not None
):
raise MSC4335UserLimitExceededError(
403,
"Media upload limit exceeded",
limit.msc4335_info_uri,
limit.msc4335_soft_limit,
limit.msc4335_increase_uri,
)
# Otherwise we use the current behaviour albeit not spec compliant
# See: https://github.com/element-hq/synapse/issues/18749
raise SynapseError(
400, "Media upload limit exceeded", Codes.RESOURCE_LIMIT_EXCEEDED
)
Expand Down
3 changes: 3 additions & 0 deletions synapse/rest/admin/experimental_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class ExperimentalFeature(str, Enum):
MSC3881 = "msc3881"
MSC3575 = "msc3575"
MSC4222 = "msc4222"
MSC4335 = "msc4335"

def is_globally_enabled(self, config: "HomeServerConfig") -> bool:
if self is ExperimentalFeature.MSC3881:
Expand All @@ -52,6 +53,8 @@ def is_globally_enabled(self, config: "HomeServerConfig") -> bool:
return config.experimental.msc3575_enabled
if self is ExperimentalFeature.MSC4222:
return config.experimental.msc4222_enabled
if self is ExperimentalFeature.MSC4335:
return config.experimental.msc4335_enabled

assert_never(self)

Expand Down
Loading
Loading