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

Commit

Permalink
Merge pull request #6015 from matrix-org/erikj/ratelimit_admin_redaction
Browse files Browse the repository at this point in the history
  • Loading branch information
anoadragon453 committed Feb 25, 2020
2 parents df6b120 + cbcbfe6 commit 1dfbad8
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 12 deletions.
1 change: 1 addition & 0 deletions changelog.d/6015.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add config option to increase ratelimits for room admins redacting messages.
7 changes: 7 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,9 @@ log_config: "CONFDIR/SERVERNAME.log.config"
# attempts for this account.
# - one that ratelimits third-party invites requests based on the account
# that's making the requests.
# - one for ratelimiting redactions by room admins. If this is not explicitly
# set then it uses the same ratelimiting as per rc_message. This is useful
# to allow room admins to deal with abuse quickly.
#
# The defaults are as shown below.
#
Expand All @@ -620,6 +623,10 @@ log_config: "CONFDIR/SERVERNAME.log.config"
#rc_third_party_invite:
# per_second: 0.2
# burst_count: 10
#
#rc_admin_redaction:
# per_second: 1
# burst_count: 50


# Ratelimiting settings for incoming federation
Expand Down
13 changes: 13 additions & 0 deletions synapse/config/ratelimiting.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ def read_config(self, config, **kwargs):
"federation_rr_transactions_per_room_per_second", 50
)

rc_admin_redaction = config.get("rc_admin_redaction")
if rc_admin_redaction:
self.rc_admin_redaction = RateLimitConfig(rc_admin_redaction)
else:
self.rc_admin_redaction = None

def generate_config_section(self, **kwargs):
return """\
## Ratelimiting ##
Expand All @@ -107,6 +113,9 @@ def generate_config_section(self, **kwargs):
# attempts for this account.
# - one that ratelimits third-party invites requests based on the account
# that's making the requests.
# - one for ratelimiting redactions by room admins. If this is not explicitly
# set then it uses the same ratelimiting as per rc_message. This is useful
# to allow room admins to deal with abuse quickly.
#
# The defaults are as shown below.
#
Expand All @@ -132,6 +141,10 @@ def generate_config_section(self, **kwargs):
#rc_third_party_invite:
# per_second: 0.2
# burst_count: 10
#
#rc_admin_redaction:
# per_second: 1
# burst_count: 50
# Ratelimiting settings for incoming federation
Expand Down
43 changes: 32 additions & 11 deletions synapse/handlers/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self, hs):
self.state_handler = hs.get_state_handler()
self.distributor = hs.get_distributor()
self.ratelimiter = hs.get_ratelimiter()
self.admin_redaction_ratelimiter = hs.get_admin_redaction_ratelimiter()
self.clock = hs.get_clock()
self.hs = hs

Expand All @@ -53,7 +54,7 @@ def __init__(self, hs):
self.event_builder_factory = hs.get_event_builder_factory()

@defer.inlineCallbacks
def ratelimit(self, requester, update=True):
def ratelimit(self, requester, update=True, is_admin_redaction=False):
"""Ratelimits requests.
Args:
Expand All @@ -62,6 +63,9 @@ def ratelimit(self, requester, update=True):
Set to False when doing multiple checks for one request (e.g.
to check up front if we would reject the request), and set to
True for the last call for a given request.
is_admin_redaction (bool): Whether this is a room admin/moderator
redacting an event. If so then we may apply different
ratelimits depending on config.
Raises:
LimitExceededError if the request should be ratelimited
Expand Down Expand Up @@ -90,16 +94,33 @@ def ratelimit(self, requester, update=True):
messages_per_second = override.messages_per_second
burst_count = override.burst_count
else:
messages_per_second = self.hs.config.rc_message.per_second
burst_count = self.hs.config.rc_message.burst_count

allowed, time_allowed = self.ratelimiter.can_do_action(
user_id,
time_now,
rate_hz=messages_per_second,
burst_count=burst_count,
update=update,
)
# We default to different values if this is an admin redaction and
# the config is set
if is_admin_redaction and self.hs.config.rc_admin_redaction:
messages_per_second = self.hs.config.rc_admin_redaction.per_second
burst_count = self.hs.config.rc_admin_redaction.burst_count
else:
messages_per_second = self.hs.config.rc_message.per_second
burst_count = self.hs.config.rc_message.burst_count

if is_admin_redaction and self.hs.config.rc_admin_redaction:
# If we have separate config for admin redactions we use a separate
# ratelimiter
allowed, time_allowed = self.admin_redaction_ratelimiter.can_do_action(
user_id,
time_now,
rate_hz=messages_per_second,
burst_count=burst_count,
update=update,
)
else:
allowed, time_allowed = self.ratelimiter.can_do_action(
user_id,
time_now,
rate_hz=messages_per_second,
burst_count=burst_count,
update=update,
)
if not allowed:
raise LimitExceededError(
retry_after_ms=int(1000 * (time_allowed - time_now))
Expand Down
22 changes: 21 additions & 1 deletion synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,27 @@ def persist_and_notify_client_event(
assert not self.config.worker_app

if ratelimit:
yield self.base_handler.ratelimit(requester)
# We check if this is a room admin redacting an event so that we
# can apply different ratelimiting. We do this by simply checking
# it's not a self-redaction (to avoid having to look up whether the
# user is actually admin or not).
is_admin_redaction = False
if event.type == EventTypes.Redaction:
original_event = yield self.store.get_event(
event.redacts,
check_redacted=False,
get_prev_content=False,
allow_rejected=False,
allow_none=True,
)

is_admin_redaction = (
original_event and event.sender != original_event.sender
)

yield self.base_handler.ratelimit(
requester, is_admin_redaction=is_admin_redaction
)

yield self.base_handler.maybe_kick_guest_users(event, context)

Expand Down
4 changes: 4 additions & 0 deletions synapse/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ def __init__(self, hostname, reactor=None, **kwargs):
self.clock = Clock(reactor)
self.distributor = Distributor()
self.ratelimiter = Ratelimiter()
self.admin_redaction_ratelimiter = Ratelimiter()
self.registration_ratelimiter = Ratelimiter()

self.datastore = None
Expand Down Expand Up @@ -283,6 +284,9 @@ def get_ratelimiter(self):
def get_registration_ratelimiter(self):
return self.registration_ratelimiter

def get_admin_redaction_ratelimiter(self):
return self.admin_redaction_ratelimiter

def build_federation_client(self):
return FederationClient(self)

Expand Down
25 changes: 25 additions & 0 deletions tests/rest/client/test_redactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ class RedactionsTestCase(HomeserverTestCase):
sync.register_servlets,
]

def make_homeserver(self, reactor, clock):
config = self.default_config()

config["rc_message"] = {"per_second": 0.2, "burst_count": 10}
config["rc_admin_redaction"] = {"per_second": 1, "burst_count": 100}

return self.setup_test_homeserver(config=config)

def prepare(self, reactor, clock, hs):
# register a couple of users
self.mod_user_id = self.register_user("user1", "pass")
Expand Down Expand Up @@ -177,3 +185,20 @@ def test_redact_create_event(self):
self._redact_event(
self.other_access_token, self.room_id, create_event_id, expect_code=403
)

def test_redact_event_as_moderator_ratelimit(self):
"""Tests that the correct ratelimiting is applied to redactions
"""

message_ids = []
# as a regular user, send messages to redact
for _ in range(20):
b = self.helper.send(room_id=self.room_id, tok=self.other_access_token)
message_ids.append(b["event_id"])
self.reactor.advance(10) # To get around ratelimits

# as the moderator, send a bunch of redactions
for msg_id in message_ids:
# These should all succeed, even though this would be denied by
# the standard message ratelimiter
self._redact_event(self.mod_access_token, self.room_id, msg_id)

0 comments on commit 1dfbad8

Please sign in to comment.