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

Commit ac77cdb

Browse files
authored
Add a shadow-banned flag to users. (#8092)
1 parent b069b78 commit ac77cdb

File tree

12 files changed

+83
-12
lines changed

12 files changed

+83
-12
lines changed

changelog.d/8092.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for shadow-banning users (ignoring any message send requests).

synapse/api/auth.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ async def get_user_by_req(
213213
user = user_info["user"]
214214
token_id = user_info["token_id"]
215215
is_guest = user_info["is_guest"]
216+
shadow_banned = user_info["shadow_banned"]
216217

217218
# Deny the request if the user account has expired.
218219
if self._account_validity.enabled and not allow_expired:
@@ -252,7 +253,12 @@ async def get_user_by_req(
252253
opentracing.set_tag("device_id", device_id)
253254

254255
return synapse.types.create_requester(
255-
user, token_id, is_guest, device_id, app_service=app_service
256+
user,
257+
token_id,
258+
is_guest,
259+
shadow_banned,
260+
device_id,
261+
app_service=app_service,
256262
)
257263
except KeyError:
258264
raise MissingClientTokenError()
@@ -297,6 +303,7 @@ async def get_user_by_access_token(
297303
dict that includes:
298304
`user` (UserID)
299305
`is_guest` (bool)
306+
`shadow_banned` (bool)
300307
`token_id` (int|None): access token id. May be None if guest
301308
`device_id` (str|None): device corresponding to access token
302309
Raises:
@@ -356,6 +363,7 @@ async def get_user_by_access_token(
356363
ret = {
357364
"user": user,
358365
"is_guest": True,
366+
"shadow_banned": False,
359367
"token_id": None,
360368
# all guests get the same device id
361369
"device_id": GUEST_DEVICE_ID,
@@ -365,6 +373,7 @@ async def get_user_by_access_token(
365373
ret = {
366374
"user": user,
367375
"is_guest": False,
376+
"shadow_banned": False,
368377
"token_id": None,
369378
"device_id": None,
370379
}
@@ -488,6 +497,7 @@ async def _look_up_user_by_access_token(self, token):
488497
"user": UserID.from_string(ret.get("name")),
489498
"token_id": ret.get("token_id", None),
490499
"is_guest": False,
500+
"shadow_banned": ret.get("shadow_banned"),
491501
"device_id": ret.get("device_id"),
492502
"valid_until_ms": ret.get("valid_until_ms"),
493503
}

synapse/handlers/register.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ async def register_user(
142142
address=None,
143143
bind_emails=[],
144144
by_admin=False,
145+
shadow_banned=False,
145146
):
146147
"""Registers a new client on the server.
147148
@@ -159,6 +160,7 @@ async def register_user(
159160
bind_emails (List[str]): list of emails to bind to this account.
160161
by_admin (bool): True if this registration is being made via the
161162
admin api, otherwise False.
163+
shadow_banned (bool): Shadow-ban the created user.
162164
Returns:
163165
str: user_id
164166
Raises:
@@ -194,6 +196,7 @@ async def register_user(
194196
admin=admin,
195197
user_type=user_type,
196198
address=address,
199+
shadow_banned=shadow_banned,
197200
)
198201

199202
if self.hs.config.user_directory_search_all_users:
@@ -224,6 +227,7 @@ async def register_user(
224227
make_guest=make_guest,
225228
create_profile_with_displayname=default_display_name,
226229
address=address,
230+
shadow_banned=shadow_banned,
227231
)
228232

229233
# Successfully registered
@@ -529,6 +533,7 @@ def register_with_store(
529533
admin=False,
530534
user_type=None,
531535
address=None,
536+
shadow_banned=False,
532537
):
533538
"""Register user in the datastore.
534539
@@ -546,6 +551,7 @@ def register_with_store(
546551
user_type (str|None): type of user. One of the values from
547552
api.constants.UserTypes, or None for a normal user.
548553
address (str|None): the IP address used to perform the registration.
554+
shadow_banned (bool): Whether to shadow-ban the user
549555
550556
Returns:
551557
Awaitable
@@ -561,6 +567,7 @@ def register_with_store(
561567
admin=admin,
562568
user_type=user_type,
563569
address=address,
570+
shadow_banned=shadow_banned,
564571
)
565572
else:
566573
return self.store.register_user(
@@ -572,6 +579,7 @@ def register_with_store(
572579
create_profile_with_displayname=create_profile_with_displayname,
573580
admin=admin,
574581
user_type=user_type,
582+
shadow_banned=shadow_banned,
575583
)
576584

577585
async def register_device(

synapse/replication/http/register.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ async def _serialize_payload(
4444
admin,
4545
user_type,
4646
address,
47+
shadow_banned,
4748
):
4849
"""
4950
Args:
@@ -60,6 +61,7 @@ async def _serialize_payload(
6061
user_type (str|None): type of user. One of the values from
6162
api.constants.UserTypes, or None for a normal user.
6263
address (str|None): the IP address used to perform the regitration.
64+
shadow_banned (bool): Whether to shadow-ban the user
6365
"""
6466
return {
6567
"password_hash": password_hash,
@@ -70,6 +72,7 @@ async def _serialize_payload(
7072
"admin": admin,
7173
"user_type": user_type,
7274
"address": address,
75+
"shadow_banned": shadow_banned,
7376
}
7477

7578
async def _handle_request(self, request, user_id):
@@ -87,6 +90,7 @@ async def _handle_request(self, request, user_id):
8790
admin=content["admin"],
8891
user_type=content["user_type"],
8992
address=content["address"],
93+
shadow_banned=content["shadow_banned"],
9094
)
9195

9296
return 200, {}

synapse/storage/databases/main/registration.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ def set_server_admin_txn(txn):
304304

305305
def _query_for_auth(self, txn, token):
306306
sql = (
307-
"SELECT users.name, users.is_guest, access_tokens.id as token_id,"
307+
"SELECT users.name, users.is_guest, users.shadow_banned, access_tokens.id as token_id,"
308308
" access_tokens.device_id, access_tokens.valid_until_ms"
309309
" FROM users"
310310
" INNER JOIN access_tokens on users.name = access_tokens.user_id"
@@ -952,6 +952,7 @@ def register_user(
952952
create_profile_with_displayname=None,
953953
admin=False,
954954
user_type=None,
955+
shadow_banned=False,
955956
):
956957
"""Attempts to register an account.
957958
@@ -968,6 +969,8 @@ def register_user(
968969
admin (boolean): is an admin user?
969970
user_type (str|None): type of user. One of the values from
970971
api.constants.UserTypes, or None for a normal user.
972+
shadow_banned (bool): Whether the user is shadow-banned,
973+
i.e. they may be told their requests succeeded but we ignore them.
971974
972975
Raises:
973976
StoreError if the user_id could not be registered.
@@ -986,6 +989,7 @@ def register_user(
986989
create_profile_with_displayname,
987990
admin,
988991
user_type,
992+
shadow_banned,
989993
)
990994

991995
def _register_user(
@@ -999,6 +1003,7 @@ def _register_user(
9991003
create_profile_with_displayname,
10001004
admin,
10011005
user_type,
1006+
shadow_banned,
10021007
):
10031008
user_id_obj = UserID.from_string(user_id)
10041009

@@ -1028,6 +1033,7 @@ def _register_user(
10281033
"appservice_id": appservice_id,
10291034
"admin": 1 if admin else 0,
10301035
"user_type": user_type,
1036+
"shadow_banned": shadow_banned,
10311037
},
10321038
)
10331039
else:
@@ -1042,6 +1048,7 @@ def _register_user(
10421048
"appservice_id": appservice_id,
10431049
"admin": 1 if admin else 0,
10441050
"user_type": user_type,
1051+
"shadow_banned": shadow_banned,
10451052
},
10461053
)
10471054

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* Copyright 2020 The Matrix.org Foundation C.I.C
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
-- A shadow-banned user may be told that their requests succeeded when they were
17+
-- actually ignored.
18+
ALTER TABLE users ADD COLUMN shadow_banned BOOLEAN;

synapse/types.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,15 @@ class Collection(Iterable[T_co], Container[T_co], Sized): # type: ignore
5151

5252
class Requester(
5353
namedtuple(
54-
"Requester", ["user", "access_token_id", "is_guest", "device_id", "app_service"]
54+
"Requester",
55+
[
56+
"user",
57+
"access_token_id",
58+
"is_guest",
59+
"shadow_banned",
60+
"device_id",
61+
"app_service",
62+
],
5563
)
5664
):
5765
"""
@@ -62,6 +70,7 @@ class Requester(
6270
access_token_id (int|None): *ID* of the access token used for this
6371
request, or None if it came via the appservice API or similar
6472
is_guest (bool): True if the user making this request is a guest user
73+
shadow_banned (bool): True if the user making this request has been shadow-banned.
6574
device_id (str|None): device_id which was set at authentication time
6675
app_service (ApplicationService|None): the AS requesting on behalf of the user
6776
"""
@@ -77,6 +86,7 @@ def serialize(self):
7786
"user_id": self.user.to_string(),
7887
"access_token_id": self.access_token_id,
7988
"is_guest": self.is_guest,
89+
"shadow_banned": self.shadow_banned,
8090
"device_id": self.device_id,
8191
"app_server_id": self.app_service.id if self.app_service else None,
8292
}
@@ -101,13 +111,19 @@ def deserialize(store, input):
101111
user=UserID.from_string(input["user_id"]),
102112
access_token_id=input["access_token_id"],
103113
is_guest=input["is_guest"],
114+
shadow_banned=input["shadow_banned"],
104115
device_id=input["device_id"],
105116
app_service=appservice,
106117
)
107118

108119

109120
def create_requester(
110-
user_id, access_token_id=None, is_guest=False, device_id=None, app_service=None
121+
user_id,
122+
access_token_id=None,
123+
is_guest=False,
124+
shadow_banned=False,
125+
device_id=None,
126+
app_service=None,
111127
):
112128
"""
113129
Create a new ``Requester`` object
@@ -117,6 +133,7 @@ def create_requester(
117133
access_token_id (int|None): *ID* of the access token used for this
118134
request, or None if it came via the appservice API or similar
119135
is_guest (bool): True if the user making this request is a guest user
136+
shadow_banned (bool): True if the user making this request is shadow-banned.
120137
device_id (str|None): device_id which was set at authentication time
121138
app_service (ApplicationService|None): the AS requesting on behalf of the user
122139
@@ -125,7 +142,9 @@ def create_requester(
125142
"""
126143
if not isinstance(user_id, UserID):
127144
user_id = UserID.from_string(user_id)
128-
return Requester(user_id, access_token_id, is_guest, device_id, app_service)
145+
return Requester(
146+
user_id, access_token_id, is_guest, shadow_banned, device_id, app_service
147+
)
129148

130149

131150
def get_domain_from_id(string):

tests/storage/test_cleanup_extrems.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def prepare(self, reactor, clock, homeserver):
3838

3939
# Create a test user and room
4040
self.user = UserID("alice", "test")
41-
self.requester = Requester(self.user, None, False, None, None)
41+
self.requester = Requester(self.user, None, False, False, None, None)
4242
info, _ = self.get_success(self.room_creator.create_room(self.requester, {}))
4343
self.room_id = info["room_id"]
4444

@@ -260,7 +260,7 @@ def prepare(self, reactor, clock, homeserver):
260260
# Create a test user and room
261261
self.user = UserID.from_string(self.register_user("user1", "password"))
262262
self.token1 = self.login("user1", "password")
263-
self.requester = Requester(self.user, None, False, None, None)
263+
self.requester = Requester(self.user, None, False, False, None, None)
264264
info, _ = self.get_success(self.room_creator.create_room(self.requester, {}))
265265
self.room_id = info["room_id"]
266266
self.event_creator = homeserver.get_event_creation_handler()

tests/storage/test_event_metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_exposed_to_prometheus(self):
2727
room_creator = self.hs.get_room_creation_handler()
2828

2929
user = UserID("alice", "test")
30-
requester = Requester(user, None, False, None, None)
30+
requester = Requester(user, None, False, False, None, None)
3131

3232
# Real events, forward extremities
3333
events = [(3, 2), (6, 2), (4, 6)]

tests/storage/test_roommember.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def test_can_rerun_update(self):
187187

188188
# Now let's create a room, which will insert a membership
189189
user = UserID("alice", "test")
190-
requester = Requester(user, None, False, None, None)
190+
requester = Requester(user, None, False, False, None, None)
191191
self.get_success(self.room_creator.create_room(requester, {}))
192192

193193
# Register the background update to run again.

0 commit comments

Comments
 (0)