|
105 | 105 | ] |
106 | 106 | ], |
107 | 107 | ] |
| 108 | +FEDERATED_USER_MAY_INVITE_CALLBACK = Callable[ |
| 109 | + ["synapse.events.EventBase"], |
| 110 | + Awaitable[ |
| 111 | + Union[ |
| 112 | + Literal["NOT_SPAM"], |
| 113 | + Codes, |
| 114 | + # Highly experimental, not officially part of the spamchecker API, may |
| 115 | + # disappear without warning depending on the results of ongoing |
| 116 | + # experiments. |
| 117 | + # Use this to return additional information as part of an error. |
| 118 | + Tuple[Codes, JsonDict], |
| 119 | + # Deprecated |
| 120 | + bool, |
| 121 | + ] |
| 122 | + ], |
| 123 | +] |
108 | 124 | USER_MAY_SEND_3PID_INVITE_CALLBACK = Callable[ |
109 | 125 | [str, str, str, str], |
110 | 126 | Awaitable[ |
@@ -266,6 +282,7 @@ def load_legacy_spam_checkers(hs: "synapse.server.HomeServer") -> None: |
266 | 282 | spam_checker_methods = { |
267 | 283 | "check_event_for_spam", |
268 | 284 | "user_may_invite", |
| 285 | + "federated_user_may_invite", |
269 | 286 | "user_may_create_room", |
270 | 287 | "user_may_create_room_alias", |
271 | 288 | "user_may_publish_room", |
@@ -347,6 +364,9 @@ def __init__(self, hs: "synapse.server.HomeServer") -> None: |
347 | 364 | ] = [] |
348 | 365 | self._user_may_join_room_callbacks: List[USER_MAY_JOIN_ROOM_CALLBACK] = [] |
349 | 366 | self._user_may_invite_callbacks: List[USER_MAY_INVITE_CALLBACK] = [] |
| 367 | + self._federated_user_may_invite_callbacks: List[ |
| 368 | + FEDERATED_USER_MAY_INVITE_CALLBACK |
| 369 | + ] = [] |
350 | 370 | self._user_may_send_3pid_invite_callbacks: List[ |
351 | 371 | USER_MAY_SEND_3PID_INVITE_CALLBACK |
352 | 372 | ] = [] |
@@ -377,6 +397,7 @@ def register_callbacks( |
377 | 397 | ] = None, |
378 | 398 | user_may_join_room: Optional[USER_MAY_JOIN_ROOM_CALLBACK] = None, |
379 | 399 | user_may_invite: Optional[USER_MAY_INVITE_CALLBACK] = None, |
| 400 | + federated_user_may_invite: Optional[FEDERATED_USER_MAY_INVITE_CALLBACK] = None, |
380 | 401 | user_may_send_3pid_invite: Optional[USER_MAY_SEND_3PID_INVITE_CALLBACK] = None, |
381 | 402 | user_may_create_room: Optional[USER_MAY_CREATE_ROOM_CALLBACK] = None, |
382 | 403 | user_may_create_room_alias: Optional[ |
@@ -406,6 +427,11 @@ def register_callbacks( |
406 | 427 | if user_may_invite is not None: |
407 | 428 | self._user_may_invite_callbacks.append(user_may_invite) |
408 | 429 |
|
| 430 | + if federated_user_may_invite is not None: |
| 431 | + self._federated_user_may_invite_callbacks.append( |
| 432 | + federated_user_may_invite, |
| 433 | + ) |
| 434 | + |
409 | 435 | if user_may_send_3pid_invite is not None: |
410 | 436 | self._user_may_send_3pid_invite_callbacks.append( |
411 | 437 | user_may_send_3pid_invite, |
@@ -605,6 +631,43 @@ async def user_may_invite( |
605 | 631 | # No spam-checker has rejected the request, let it pass. |
606 | 632 | return self.NOT_SPAM |
607 | 633 |
|
| 634 | + async def federated_user_may_invite( |
| 635 | + self, event: "synapse.events.EventBase" |
| 636 | + ) -> Union[Tuple[Codes, dict], Literal["NOT_SPAM"]]: |
| 637 | + """Checks if a given user may send an invite |
| 638 | +
|
| 639 | + Args: |
| 640 | + event: The event to be checked |
| 641 | +
|
| 642 | + Returns: |
| 643 | + NOT_SPAM if the operation is permitted, Codes otherwise. |
| 644 | + """ |
| 645 | + for callback in self._federated_user_may_invite_callbacks: |
| 646 | + with Measure(self.clock, f"{callback.__module__}.{callback.__qualname__}"): |
| 647 | + res = await delay_cancellation(callback(event)) |
| 648 | + # Normalize return values to `Codes` or `"NOT_SPAM"`. |
| 649 | + if res is True or res is self.NOT_SPAM: |
| 650 | + continue |
| 651 | + elif res is False: |
| 652 | + return synapse.api.errors.Codes.FORBIDDEN, {} |
| 653 | + elif isinstance(res, synapse.api.errors.Codes): |
| 654 | + return res, {} |
| 655 | + elif ( |
| 656 | + isinstance(res, tuple) |
| 657 | + and len(res) == 2 |
| 658 | + and isinstance(res[0], synapse.api.errors.Codes) |
| 659 | + and isinstance(res[1], dict) |
| 660 | + ): |
| 661 | + return res |
| 662 | + else: |
| 663 | + logger.warning( |
| 664 | + "Module returned invalid value, rejecting invite as spam" |
| 665 | + ) |
| 666 | + return synapse.api.errors.Codes.FORBIDDEN, {} |
| 667 | + |
| 668 | + # Check the standard user_may_invite callback if no module has rejected the invite yet. |
| 669 | + return await self.user_may_invite(event.sender, event.state_key, event.room_id) |
| 670 | + |
608 | 671 | async def user_may_send_3pid_invite( |
609 | 672 | self, inviter_userid: str, medium: str, address: str, room_id: str |
610 | 673 | ) -> Union[Tuple[Codes, dict], Literal["NOT_SPAM"]]: |
|
0 commit comments