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

Create configurable ratelimiter for 3pid invites #11

Merged
merged 9 commits into from
Nov 12, 2019
Merged
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/11.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow server admins to configure a custom global rate-limiting for third party invites.
6 changes: 6 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,8 @@ log_config: "CONFDIR/SERVERNAME.log.config"
# - one for login that ratelimits login requests based on the account the
# client is attempting to log into, based on the amount of failed login
# attempts for this account.
# - one that ratelimits third-party invites requests based on the account
# that's making the requests.
#
# The defaults are as shown below.
#
Expand All @@ -589,6 +591,10 @@ log_config: "CONFDIR/SERVERNAME.log.config"
# failed_attempts:
# per_second: 0.17
# burst_count: 3
#
#rc_third_party_invite:
# per_second: 0.2
# burst_count: 10


# Ratelimiting settings for incoming federation
Expand Down
9 changes: 9 additions & 0 deletions synapse/config/ratelimiting.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def read_config(self, config):
)

self.rc_registration = RateLimitConfig(config.get("rc_registration", {}))
self.rc_third_party_invite = RateLimitConfig(
config.get("rc_third_party_invite", {})
)

rc_login_config = config.get("rc_login", {})
self.rc_login_address = RateLimitConfig(rc_login_config.get("address", {}))
Expand Down Expand Up @@ -102,6 +105,8 @@ def default_config(self, **kwargs):
# - one for login that ratelimits login requests based on the account the
# client is attempting to log into, based on the amount of failed login
# attempts for this account.
# - one that ratelimits third-party invites requests based on the account
# that's making the requests.
#
# The defaults are as shown below.
#
Expand All @@ -123,6 +128,10 @@ def default_config(self, **kwargs):
# failed_attempts:
# per_second: 0.17
# burst_count: 3
#
#rc_third_party_invite:
# per_second: 0.2
# burst_count: 10


# Ratelimiting settings for incoming federation
Expand Down
16 changes: 8 additions & 8 deletions synapse/handlers/room_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@
import synapse.types
from synapse.api.constants import EventTypes, Membership
from synapse.api.errors import AuthError, Codes, ProxiedRequestError, SynapseError
from synapse.api.ratelimiting import Ratelimiter
from synapse.types import RoomID, UserID
from synapse.util.async_helpers import Linearizer
from synapse.util.distributor import user_joined_room, user_left_room

from ._base import BaseHandler

logger = logging.getLogger(__name__)

id_server_scheme = "https://"
Expand Down Expand Up @@ -74,11 +73,7 @@ def __init__(self, hs):
self.rewrite_identity_server_urls = self.config.rewrite_identity_server_urls
self._enable_lookup = hs.config.enable_3pid_lookup
self.allow_per_room_profiles = self.config.allow_per_room_profiles

# This is only used to get at ratelimit function, and
# maybe_kick_guest_users. It's fine there are multiple of these as
# it doesn't store state.
self.base_handler = BaseHandler(hs)
self.ratelimiter = Ratelimiter()

@abc.abstractmethod
def _remote_join(self, requester, remote_room_hosts, room_id, user, content):
Expand Down Expand Up @@ -773,7 +768,12 @@ def do_3pid_invite(

# We need to rate limit *before* we send out any 3PID invites, so we
# can't just rely on the standard ratelimiting of events.
yield self.base_handler.ratelimit(requester)
self.ratelimiter.ratelimit(
requester.user.to_string(), time_now_s=self.hs.clock.time(),
rate_hz=self.hs.config.rc_third_party_invite.per_second,
burst_count=self.hs.config.rc_third_party_invite.burst_count,
update=True,
)

can_invite = yield self.third_party_event_rules.check_threepid_can_be_invited(
medium, address, room_id,
Expand Down
9 changes: 9 additions & 0 deletions tests/rest/client/test_room_access_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ def test_direct(self):
expect_code=200,
)

# Disable the 3pid invite ratelimiter
burst = self.hs.config.rc_third_party_invite.burst_count
per_second = self.hs.config.rc_third_party_invite.per_second
self.hs.config.rc_third_party_invite.burst_count = 10
self.hs.config.rc_third_party_invite.per_second = 0.1

# We can't send a 3PID invite to a room that already has two members.
self.send_threepid_invite(
address="test@allowed_domain",
Expand Down Expand Up @@ -354,6 +360,9 @@ def test_direct(self):
expected_code=403,
)

self.hs.config.rc_third_party_invite.burst_count = burst
self.hs.config.rc_third_party_invite.per_second = per_second

def test_unrestricted(self):
"""Tests that, in unrestricted mode, we can invite whoever we want, but we can
only change the power level of users that wouldn't be forbidden in restricted
Expand Down