1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
From 4b62d4e914d8ff7e21bcfbbc6572f1f2a363e066 Mon Sep 17 00:00:00 2001
From: Rory& <root@rory.gay>
Date: Fri, 25 Jul 2025 08:26:15 +0200
Subject: [PATCH 13/14] RequestRatelimiter: expose can_do_action
---
synapse/api/ratelimiting.py | 75 +++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/synapse/api/ratelimiting.py b/synapse/api/ratelimiting.py
index 509ef6b2c1..5f22089a6b 100644
--- a/synapse/api/ratelimiting.py
+++ b/synapse/api/ratelimiting.py
@@ -435,3 +435,78 @@ class RequestRatelimiter:
update=update,
n_actions=n_actions,
)
+
+ async def can_do_action(
+ self,
+ requester: Optional[Requester],
+ burst_count: Optional[int] = None,
+ update: bool = True,
+ is_admin_redaction: bool = False,
+ n_actions: int = 1,
+ ) -> Tuple[bool, float]:
+ """Can the entity (e.g. user or IP address) perform the action?
+
+ Checks if the user has ratelimiting disabled in the database by looking
+ for null/zero values in the `ratelimit_override` table. (Non-zero
+ values aren't honoured, as they're specific to the event sending
+ ratelimiter, rather than all ratelimiters)
+
+ Args:
+ requester: The requester that is doing the action, if any. Used to check
+ if the user has ratelimits disabled in the database.
+ key: An arbitrary key used to classify an action. Defaults to the
+ requester's user ID.
+ rate_hz: The long term number of actions that can be performed in a second.
+ Overrides the value set during instantiation if set.
+ burst_count: How many actions that can be performed before being limited.
+ Overrides the value set during instantiation if set.
+ update: Whether to count this check as performing the action. If the action
+ cannot be performed, the user's action count is not incremented at all.
+ n_actions: The number of times the user wants to do this action. If the user
+ cannot do all of the actions, the user's action count is not incremented
+ at all.
+ _time_now_s: The current time. Optional, defaults to the current time according
+ to self.clock. Only used by tests.
+
+ Returns:
+ A tuple containing:
+ * A bool indicating if they can perform the action now
+ * The reactor timestamp for when the action can be performed next.
+ -1 if rate_hz is less than or equal to zero
+ """
+ user_id = requester.user.to_string()
+
+ # The AS user itself is never rate limited.
+ app_service = self.store.get_app_service_by_user_id(user_id)
+ if app_service is not None:
+ return True, 0 # do not ratelimit app service senders
+
+ messages_per_second = self._rc_message.per_second
+ burst_count = self._rc_message.burst_count
+
+ # Check if there is a per user override in the DB.
+ override = await self.store.get_ratelimit_for_user(user_id)
+ if override:
+ # If overridden with a null Hz then ratelimiting has been entirely
+ # disabled for the user
+ if not override.messages_per_second:
+ return True, 0
+
+ messages_per_second = override.messages_per_second
+ burst_count = override.burst_count
+
+ if is_admin_redaction and self.admin_redaction_ratelimiter:
+ # If we have separate config for admin redactions, use a separate
+ # ratelimiter as to not have user_ids clash
+ return await self.admin_redaction_ratelimiter.can_do_action(
+ requester, update=update, n_actions=n_actions
+ )
+ else:
+ # Override rate and burst count per-user
+ return await self.request_ratelimiter.can_do_action(
+ requester,
+ rate_hz=messages_per_second,
+ burst_count=burst_count,
+ update=update,
+ n_actions=n_actions,
+ )
--
2.49.0
|