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

Commit

Permalink
Uniformize spam-checker API, part 3: Expand check_event_for_spam with…
Browse files Browse the repository at this point in the history
… the ability to return additional fields.
  • Loading branch information
Yoric committed May 23, 2022
1 parent 92b87d5 commit 9a16801
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 23 deletions.
23 changes: 13 additions & 10 deletions synapse/api/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,13 @@ class SynapseError(CodeMessageException):
errcode: Matrix error code e.g 'M_FORBIDDEN'
"""

def __init__(self, code: int, msg: str, errcode: str = Codes.UNKNOWN):
def __init__(
self,
code: int,
msg: str,
errcode: str = Codes.UNKNOWN,
additional_fields: Optional[Dict] = None,
):
"""Constructs a synapse error.
Args:
Expand All @@ -149,9 +155,13 @@ def __init__(self, code: int, msg: str, errcode: str = Codes.UNKNOWN):
"""
super().__init__(code, msg)
self.errcode = errcode
if additional_fields is None:
self._additional_fields: Dict = {}
else:
self._additional_fields = dict(additional_fields)

def error_dict(self) -> "JsonDict":
return cs_error(self.msg, self.errcode)
return cs_error(self.msg, self.errcode, **self._additional_fields)


class InvalidAPICallError(SynapseError):
Expand All @@ -176,14 +186,7 @@ def __init__(
errcode: str = Codes.UNKNOWN,
additional_fields: Optional[Dict] = None,
):
super().__init__(code, msg, errcode)
if additional_fields is None:
self._additional_fields: Dict = {}
else:
self._additional_fields = dict(additional_fields)

def error_dict(self) -> "JsonDict":
return cs_error(self.msg, self.errcode, **self._additional_fields)
super().__init__(code, msg, errcode, additional_fields)


class ConsentNotGivenError(SynapseError):
Expand Down
36 changes: 23 additions & 13 deletions synapse/events/spamcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
Awaitable,
Callable,
Collection,
Dict,
List,
Optional,
Tuple,
Expand All @@ -42,15 +43,23 @@
logger = logging.getLogger(__name__)


# A boolean returned value, kept for backwards compatibility but deprecated.
DEPRECATED_BOOL = bool

# A string returned value, kept for backwards compatibility but deprecated.
DEPRECATED_STR = str

CHECK_EVENT_FOR_SPAM_CALLBACK = Callable[
["synapse.events.EventBase"],
Awaitable[Union[Allow, Codes, DEPRECATED_BOOL, DEPRECATED_STR]],
Awaitable[
Union[
Allow,
Codes,
# Highly experimental, not officially part of the spamchecker API, may
# disappear without warning depending on the results of ongoing
# experiments.
# Use this to return additional information as part of an error.
Tuple[Codes, Dict],
# Deprecated
bool,
# Deprecated
str,
]
],
]
USER_MAY_JOIN_ROOM_CALLBACK = Callable[[str, str, bool], Awaitable[bool]]
USER_MAY_INVITE_CALLBACK = Callable[[str, str, str], Awaitable[bool]]
Expand Down Expand Up @@ -252,7 +261,7 @@ def register_callbacks(

async def check_event_for_spam(
self, event: "synapse.events.EventBase"
) -> Union[Decision, str]:
) -> Union[Decision, Tuple[Codes, Dict], str]:
"""Checks if a given event is considered "spammy" by this server.
If the server considers an event spammy, then it will be rejected if
Expand All @@ -275,9 +284,9 @@ async def check_event_for_spam(
with Measure(
self.clock, "{}.{}".format(callback.__module__, callback.__qualname__)
):
res: Union[Decision, str, bool] = await delay_cancellation(
callback(event)
)
res: Union[
Decision, Tuple[Codes, Dict], str, bool
] = await delay_cancellation(callback(event))
if res is False or res is Allow.ALLOW:
# This spam-checker accepts the event.
# Other spam-checkers may reject it, though.
Expand All @@ -287,8 +296,9 @@ async def check_event_for_spam(
# return value `True`
return Codes.FORBIDDEN
else:
# This spam-checker rejects the event either with a `str`
# or with a `Codes`. In either case, we stop here.
# This spam-checker rejects the event either with a `str`,
# with a `Codes` or with a `Tuple[Codes, Dict]`. In either
# case, we stop here.
return res

# No spam-checker has rejected the event, let it pass.
Expand Down
8 changes: 8 additions & 0 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,14 @@ async def create_and_send_nonmember_event(

spam_check = await self.spam_checker.check_event_for_spam(event)
if spam_check is not synapse.spam_checker_api.Allow.ALLOW:
if isinstance(spam_check, tuple):
[code, dict] = spam_check
raise SynapseError(
403,
"This message had been rejected as probable spam",
code,
dict,
)
raise SynapseError(
403, "This message had been rejected as probable spam", spam_check
)
Expand Down

0 comments on commit 9a16801

Please sign in to comment.