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

Commit a164a46

Browse files
Yoricbabolivier
andauthored
Uniformize spam-checker API, part 4: port other spam-checker callbacks to return Union[Allow, Codes]. (#12857)
Co-authored-by: Brendan Abolivier <[email protected]>
1 parent 53b77b2 commit a164a46

File tree

12 files changed

+604
-182
lines changed

12 files changed

+604
-182
lines changed

changelog.d/12857.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Port spam-checker API callbacks to a new, richer API. This is part of an ongoing change to let spam-checker modules inform users of the reason their event or operation is rejected.

docs/modules/spam_checker_callbacks.md

Lines changed: 134 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -38,62 +38,81 @@ this callback.
3838

3939
_First introduced in Synapse v1.37.0_
4040

41+
_Changed in Synapse v1.61.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
42+
4143
```python
42-
async def user_may_join_room(user: str, room: str, is_invited: bool) -> bool
44+
async def user_may_join_room(user: str, room: str, is_invited: bool) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
4345
```
4446

45-
Called when a user is trying to join a room. The module must return a `bool` to indicate
46-
whether the user can join the room. Return `False` to prevent the user from joining the
47-
room; otherwise return `True` to permit the joining.
48-
49-
The user is represented by their Matrix user ID (e.g.
47+
Called when a user is trying to join a room. The user is represented by their Matrix user ID (e.g.
5048
`@alice:example.com`) and the room is represented by its Matrix ID (e.g.
5149
`!room:example.com`). The module is also given a boolean to indicate whether the user
5250
currently has a pending invite in the room.
5351

5452
This callback isn't called if the join is performed by a server administrator, or in the
5553
context of a room creation.
5654

55+
The callback must return one of:
56+
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
57+
decide to reject it.
58+
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
59+
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
60+
- (deprecated) `False`, which is the same as returning `synapse.module_api.NOT_SPAM`.
61+
- (deprecated) `True`, which is the same as returning `synapse.module_api.errors.Codes.FORBIDDEN`.
62+
5763
If multiple modules implement this callback, they will be considered in order. If a
58-
callback returns `True`, Synapse falls through to the next one. The value of the first
59-
callback that does not return `True` will be used. If this happens, Synapse will not call
60-
any of the subsequent implementations of this callback.
64+
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
65+
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
66+
be used. If this happens, Synapse will not call any of the subsequent implementations of
67+
this callback.
6168

6269
### `user_may_invite`
6370

6471
_First introduced in Synapse v1.37.0_
6572

73+
_Changed in Synapse v1.61.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
74+
6675
```python
67-
async def user_may_invite(inviter: str, invitee: str, room_id: str) -> bool
76+
async def user_may_invite(inviter: str, invitee: str, room_id: str) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
6877
```
6978

70-
Called when processing an invitation. The module must return a `bool` indicating whether
71-
the inviter can invite the invitee to the given room. Both inviter and invitee are
72-
represented by their Matrix user ID (e.g. `@alice:example.com`). Return `False` to prevent
73-
the invitation; otherwise return `True` to permit it.
79+
Called when processing an invitation. Both inviter and invitee are
80+
represented by their Matrix user ID (e.g. `@alice:example.com`).
81+
82+
83+
The callback must return one of:
84+
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
85+
decide to reject it.
86+
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
87+
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
88+
89+
- (deprecated) `False`, which is the same as returning `synapse.module_api.NOT_SPAM`.
90+
- (deprecated) `True`, which is the same as returning `synapse.module_api.errors.Codes.FORBIDDEN`.
7491

7592
If multiple modules implement this callback, they will be considered in order. If a
76-
callback returns `True`, Synapse falls through to the next one. The value of the first
77-
callback that does not return `True` will be used. If this happens, Synapse will not call
78-
any of the subsequent implementations of this callback.
93+
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
94+
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
95+
be used. If this happens, Synapse will not call any of the subsequent implementations of
96+
this callback.
97+
7998

8099
### `user_may_send_3pid_invite`
81100

82101
_First introduced in Synapse v1.45.0_
83102

103+
_Changed in Synapse v1.61.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
104+
84105
```python
85106
async def user_may_send_3pid_invite(
86107
inviter: str,
87108
medium: str,
88109
address: str,
89110
room_id: str,
90-
) -> bool
111+
) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
91112
```
92113

93114
Called when processing an invitation using a third-party identifier (also called a 3PID,
94-
e.g. an email address or a phone number). The module must return a `bool` indicating
95-
whether the inviter can invite the invitee to the given room. Return `False` to prevent
96-
the invitation; otherwise return `True` to permit it.
115+
e.g. an email address or a phone number).
97116

98117
The inviter is represented by their Matrix user ID (e.g. `@alice:example.com`), and the
99118
invitee is represented by its medium (e.g. "email") and its address
@@ -115,63 +134,108 @@ await user_may_send_3pid_invite(
115134
**Note**: If the third-party identifier is already associated with a matrix user ID,
116135
[`user_may_invite`](#user_may_invite) will be used instead.
117136

137+
The callback must return one of:
138+
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
139+
decide to reject it.
140+
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
141+
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
142+
143+
- (deprecated) `False`, which is the same as returning `synapse.module_api.NOT_SPAM`.
144+
- (deprecated) `True`, which is the same as returning `synapse.module_api.errors.Codes.FORBIDDEN`.
145+
118146
If multiple modules implement this callback, they will be considered in order. If a
119-
callback returns `True`, Synapse falls through to the next one. The value of the first
120-
callback that does not return `True` will be used. If this happens, Synapse will not call
121-
any of the subsequent implementations of this callback.
147+
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
148+
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
149+
be used. If this happens, Synapse will not call any of the subsequent implementations of
150+
this callback.
151+
122152

123153
### `user_may_create_room`
124154

125155
_First introduced in Synapse v1.37.0_
126156

157+
_Changed in Synapse v1.61.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
158+
127159
```python
128-
async def user_may_create_room(user: str) -> bool
160+
async def user_may_create_room(user_id: str) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
129161
```
130162

131-
Called when processing a room creation request. The module must return a `bool` indicating
132-
whether the given user (represented by their Matrix user ID) is allowed to create a room.
133-
Return `False` to prevent room creation; otherwise return `True` to permit it.
163+
Called when processing a room creation request.
164+
165+
The callback must return one of:
166+
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
167+
decide to reject it.
168+
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
169+
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
170+
171+
- (deprecated) `False`, which is the same as returning `synapse.module_api.NOT_SPAM`.
172+
- (deprecated) `True`, which is the same as returning `synapse.module_api.errors.Codes.FORBIDDEN`.
134173

135174
If multiple modules implement this callback, they will be considered in order. If a
136-
callback returns `True`, Synapse falls through to the next one. The value of the first
137-
callback that does not return `True` will be used. If this happens, Synapse will not call
138-
any of the subsequent implementations of this callback.
175+
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
176+
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
177+
be used. If this happens, Synapse will not call any of the subsequent implementations of
178+
this callback.
179+
180+
139181

140182
### `user_may_create_room_alias`
141183

142184
_First introduced in Synapse v1.37.0_
143185

186+
_Changed in Synapse v1.61.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
187+
144188
```python
145-
async def user_may_create_room_alias(user: str, room_alias: "synapse.types.RoomAlias") -> bool
189+
async def user_may_create_room_alias(user_id: str, room_alias: "synapse.module_api.RoomAlias") -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
146190
```
147191

148-
Called when trying to associate an alias with an existing room. The module must return a
149-
`bool` indicating whether the given user (represented by their Matrix user ID) is allowed
150-
to set the given alias. Return `False` to prevent the alias creation; otherwise return
151-
`True` to permit it.
192+
Called when trying to associate an alias with an existing room.
193+
194+
The callback must return one of:
195+
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
196+
decide to reject it.
197+
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
198+
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
199+
200+
- (deprecated) `False`, which is the same as returning `synapse.module_api.NOT_SPAM`.
201+
- (deprecated) `True`, which is the same as returning `synapse.module_api.errors.Codes.FORBIDDEN`.
152202

153203
If multiple modules implement this callback, they will be considered in order. If a
154-
callback returns `True`, Synapse falls through to the next one. The value of the first
155-
callback that does not return `True` will be used. If this happens, Synapse will not call
156-
any of the subsequent implementations of this callback.
204+
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
205+
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
206+
be used. If this happens, Synapse will not call any of the subsequent implementations of
207+
this callback.
208+
209+
157210

158211
### `user_may_publish_room`
159212

160213
_First introduced in Synapse v1.37.0_
161214

215+
_Changed in Synapse v1.61.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
216+
162217
```python
163-
async def user_may_publish_room(user: str, room_id: str) -> bool
218+
async def user_may_publish_room(user_id: str, room_id: str) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
164219
```
165220

166-
Called when trying to publish a room to the homeserver's public rooms directory. The
167-
module must return a `bool` indicating whether the given user (represented by their
168-
Matrix user ID) is allowed to publish the given room. Return `False` to prevent the
169-
room from being published; otherwise return `True` to permit its publication.
221+
Called when trying to publish a room to the homeserver's public rooms directory.
222+
223+
The callback must return one of:
224+
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
225+
decide to reject it.
226+
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
227+
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
228+
229+
- (deprecated) `False`, which is the same as returning `synapse.module_api.NOT_SPAM`.
230+
- (deprecated) `True`, which is the same as returning `synapse.module_api.errors.Codes.FORBIDDEN`.
170231

171232
If multiple modules implement this callback, they will be considered in order. If a
172-
callback returns `True`, Synapse falls through to the next one. The value of the first
173-
callback that does not return `True` will be used. If this happens, Synapse will not call
174-
any of the subsequent implementations of this callback.
233+
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
234+
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
235+
be used. If this happens, Synapse will not call any of the subsequent implementations of
236+
this callback.
237+
238+
175239

176240
### `check_username_for_spam`
177241

@@ -239,21 +303,32 @@ this callback.
239303

240304
_First introduced in Synapse v1.37.0_
241305

306+
_Changed in Synapse v1.61.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
307+
242308
```python
243309
async def check_media_file_for_spam(
244310
file_wrapper: "synapse.rest.media.v1.media_storage.ReadableFileWrapper",
245311
file_info: "synapse.rest.media.v1._base.FileInfo",
246-
) -> bool
312+
) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
247313
```
248314

249-
Called when storing a local or remote file. The module must return a `bool` indicating
250-
whether the given file should be excluded from the homeserver's media store. Return
251-
`True` to prevent this file from being stored; otherwise return `False`.
315+
Called when storing a local or remote file.
316+
317+
The callback must return one of:
318+
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
319+
decide to reject it.
320+
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
321+
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
322+
323+
- (deprecated) `False`, which is the same as returning `synapse.module_api.NOT_SPAM`.
324+
- (deprecated) `True`, which is the same as returning `synapse.module_api.errors.Codes.FORBIDDEN`.
252325

253326
If multiple modules implement this callback, they will be considered in order. If a
254-
callback returns `False`, Synapse falls through to the next one. The value of the first
255-
callback that does not return `False` will be used. If this happens, Synapse will not call
256-
any of the subsequent implementations of this callback.
327+
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
328+
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
329+
be used. If this happens, Synapse will not call any of the subsequent implementations of
330+
this callback.
331+
257332

258333
### `should_drop_federated_event`
259334

@@ -316,6 +391,9 @@ class ListSpamChecker:
316391
resource=IsUserEvilResource(config),
317392
)
318393

319-
async def check_event_for_spam(self, event: "synapse.events.EventBase") -> Union[bool, str]:
320-
return event.sender not in self.evil_users
394+
async def check_event_for_spam(self, event: "synapse.events.EventBase") -> Union[Literal["NOT_SPAM"], Codes]:
395+
if event.sender in self.evil_users:
396+
return Codes.FORBIDDEN
397+
else:
398+
return synapse.module_api.NOT_SPAM
321399
```

docs/upgrade.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,47 @@ process, for example:
8989
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
9090
```
9191
92+
# Upgrading to v1.61.0
93+
94+
## New signatures for spam checker callbacks
95+
96+
As a followup to changes in v1.60.0, the following spam-checker callbacks have changed signature:
97+
98+
- `user_may_join_room`
99+
- `user_may_invite`
100+
- `user_may_send_3pid_invite`
101+
- `user_may_create_room`
102+
- `user_may_create_room_alias`
103+
- `user_may_publish_room`
104+
- `check_media_file_for_spam`
105+
106+
For each of these methods, the previous callback signature has been deprecated.
107+
108+
Whereas callbacks used to return `bool`, they should now return `Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes"]`.
109+
110+
For instance, if your module implements `user_may_join_room` as follows:
111+
112+
```python
113+
async def user_may_join_room(self, user_id: str, room_id: str, is_invited: bool)
114+
if ...:
115+
# Request is spam
116+
return False
117+
# Request is not spam
118+
return True
119+
```
120+
121+
you should rewrite it as follows:
122+
123+
```python
124+
async def user_may_join_room(self, user_id: str, room_id: str, is_invited: bool)
125+
if ...:
126+
# Request is spam, mark it as forbidden (you may use some more precise error
127+
# code if it is useful).
128+
return synapse.module_api.errors.Codes.FORBIDDEN
129+
# Request is not spam, mark it as such.
130+
return synapse.module_api.NOT_SPAM
131+
```
132+
92133
# Upgrading to v1.60.0
93134
94135
## Adding a new unique index to `state_group_edges` could fail if your database is corrupted

0 commit comments

Comments
 (0)