Skip to content

Commit 628351b

Browse files
Half-Shotdevonh
andauthored
Never autojoin deactivated & suspended users. (#18073)
This PR changes the logic so that deactivated users are always ignored. Suspended users were already effectively ignored as Synapse forbids a join while suspended. --------- Co-authored-by: Devon Hudson <devon.dmytro@gmail.com>
1 parent 8f27b3a commit 628351b

3 files changed

Lines changed: 232 additions & 35 deletions

File tree

changelog.d/18073.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deactivated users will no longer automatically accept an invite when `auto_accept_invites` is enabled.

synapse/events/auto_accept_invites.py

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -66,49 +66,66 @@ async def on_new_event(self, event: EventBase, *args: Any) -> None:
6666
event: The incoming event.
6767
"""
6868
# Check if the event is an invite for a local user.
69-
is_invite_for_local_user = (
70-
event.type == EventTypes.Member
71-
and event.is_state()
72-
and event.membership == Membership.INVITE
73-
and self._api.is_mine(event.state_key)
74-
)
69+
if (
70+
event.type != EventTypes.Member
71+
or event.is_state() is False
72+
or event.membership != Membership.INVITE
73+
or self._api.is_mine(event.state_key) is False
74+
):
75+
return
7576

7677
# Only accept invites for direct messages if the configuration mandates it.
7778
is_direct_message = event.content.get("is_direct", False)
78-
is_allowed_by_direct_message_rules = (
79-
not self._config.accept_invites_only_for_direct_messages
80-
or is_direct_message is True
81-
)
79+
if (
80+
self._config.accept_invites_only_for_direct_messages
81+
and is_direct_message is False
82+
):
83+
return
8284

8385
# Only accept invites from remote users if the configuration mandates it.
8486
is_from_local_user = self._api.is_mine(event.sender)
85-
is_allowed_by_local_user_rules = (
86-
not self._config.accept_invites_only_from_local_users
87-
or is_from_local_user is True
88-
)
89-
9087
if (
91-
is_invite_for_local_user
92-
and is_allowed_by_direct_message_rules
93-
and is_allowed_by_local_user_rules
88+
self._config.accept_invites_only_from_local_users
89+
and is_from_local_user is False
9490
):
95-
# Make the user join the room. We run this as a background process to circumvent a race condition
96-
# that occurs when responding to invites over federation (see https://github.com/matrix-org/synapse-auto-accept-invite/issues/12)
97-
run_as_background_process(
98-
"retry_make_join",
99-
self._retry_make_join,
100-
event.state_key,
101-
event.state_key,
102-
event.room_id,
103-
"join",
104-
bg_start_span=False,
105-
)
91+
return
10692

107-
if is_direct_message:
108-
# Mark this room as a direct message!
109-
await self._mark_room_as_direct_message(
110-
event.state_key, event.sender, event.room_id
111-
)
93+
# Check the user is activated.
94+
recipient = await self._api.get_userinfo_by_id(event.state_key)
95+
96+
# Ignore if the user doesn't exist.
97+
if recipient is None:
98+
return
99+
100+
# Never accept invites for deactivated users.
101+
if recipient.is_deactivated:
102+
return
103+
104+
# Never accept invites for suspended users.
105+
if recipient.suspended:
106+
return
107+
108+
# Never accept invites for locked users.
109+
if recipient.locked:
110+
return
111+
112+
# Make the user join the room. We run this as a background process to circumvent a race condition
113+
# that occurs when responding to invites over federation (see https://github.com/matrix-org/synapse-auto-accept-invite/issues/12)
114+
run_as_background_process(
115+
"retry_make_join",
116+
self._retry_make_join,
117+
event.state_key,
118+
event.state_key,
119+
event.room_id,
120+
"join",
121+
bg_start_span=False,
122+
)
123+
124+
if is_direct_message:
125+
# Mark this room as a direct message!
126+
await self._mark_room_as_direct_message(
127+
event.state_key, event.sender, event.room_id
128+
)
112129

113130
async def _mark_room_as_direct_message(
114131
self, user_id: str, dm_user_id: str, room_id: str

tests/events/test_auto_accept_invites.py

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from synapse.rest import admin
4040
from synapse.rest.client import login, room
4141
from synapse.server import HomeServer
42-
from synapse.types import StreamToken, create_requester
42+
from synapse.types import StreamToken, UserID, UserInfo, create_requester
4343
from synapse.util import Clock
4444

4545
from tests.handlers.test_sync import generate_sync_config
@@ -349,6 +349,169 @@ def test_accept_invite_local_user(
349349
join_updates, _ = sync_join(self, invited_user_id)
350350
self.assertEqual(len(join_updates), 0)
351351

352+
@override_config(
353+
{
354+
"auto_accept_invites": {
355+
"enabled": True,
356+
},
357+
}
358+
)
359+
async def test_ignore_invite_for_missing_user(self) -> None:
360+
"""Tests that receiving an invite for a missing user is ignored."""
361+
inviting_user_id = self.register_user("inviter", "pass")
362+
inviting_user_tok = self.login("inviter", "pass")
363+
364+
# A local user who receives an invite
365+
invited_user_id = "@fake:" + self.hs.config.server.server_name
366+
367+
# Create a room and send an invite to the other user
368+
room_id = self.helper.create_room_as(
369+
inviting_user_id,
370+
tok=inviting_user_tok,
371+
)
372+
373+
self.helper.invite(
374+
room_id,
375+
inviting_user_id,
376+
invited_user_id,
377+
tok=inviting_user_tok,
378+
)
379+
380+
join_updates, _ = sync_join(self, inviting_user_id)
381+
# Assert that the last event in the room was not a member event for the target user.
382+
self.assertEqual(
383+
join_updates[0].timeline.events[-1].content["membership"], "invite"
384+
)
385+
386+
@override_config(
387+
{
388+
"auto_accept_invites": {
389+
"enabled": True,
390+
},
391+
}
392+
)
393+
async def test_ignore_invite_for_deactivated_user(self) -> None:
394+
"""Tests that receiving an invite for a deactivated user is ignored."""
395+
inviting_user_id = self.register_user("inviter", "pass", admin=True)
396+
inviting_user_tok = self.login("inviter", "pass")
397+
398+
# A local user who receives an invite
399+
invited_user_id = self.register_user("invitee", "pass")
400+
401+
# Create a room and send an invite to the other user
402+
room_id = self.helper.create_room_as(
403+
inviting_user_id,
404+
tok=inviting_user_tok,
405+
)
406+
407+
channel = self.make_request(
408+
"PUT",
409+
"/_synapse/admin/v2/users/%s" % invited_user_id,
410+
{"deactivated": True},
411+
access_token=inviting_user_tok,
412+
)
413+
414+
assert channel.code == 200
415+
416+
self.helper.invite(
417+
room_id,
418+
inviting_user_id,
419+
invited_user_id,
420+
tok=inviting_user_tok,
421+
)
422+
423+
join_updates, b = sync_join(self, inviting_user_id)
424+
# Assert that the last event in the room was not a member event for the target user.
425+
self.assertEqual(
426+
join_updates[0].timeline.events[-1].content["membership"], "invite"
427+
)
428+
429+
@override_config(
430+
{
431+
"auto_accept_invites": {
432+
"enabled": True,
433+
},
434+
}
435+
)
436+
async def test_ignore_invite_for_suspended_user(self) -> None:
437+
"""Tests that receiving an invite for a suspended user is ignored."""
438+
inviting_user_id = self.register_user("inviter", "pass", admin=True)
439+
inviting_user_tok = self.login("inviter", "pass")
440+
441+
# A local user who receives an invite
442+
invited_user_id = self.register_user("invitee", "pass")
443+
444+
# Create a room and send an invite to the other user
445+
room_id = self.helper.create_room_as(
446+
inviting_user_id,
447+
tok=inviting_user_tok,
448+
)
449+
450+
channel = self.make_request(
451+
"PUT",
452+
f"/_synapse/admin/v1/suspend/{invited_user_id}",
453+
{"suspend": True},
454+
access_token=inviting_user_tok,
455+
)
456+
457+
assert channel.code == 200
458+
459+
self.helper.invite(
460+
room_id,
461+
inviting_user_id,
462+
invited_user_id,
463+
tok=inviting_user_tok,
464+
)
465+
466+
join_updates, b = sync_join(self, inviting_user_id)
467+
# Assert that the last event in the room was not a member event for the target user.
468+
self.assertEqual(
469+
join_updates[0].timeline.events[-1].content["membership"], "invite"
470+
)
471+
472+
@override_config(
473+
{
474+
"auto_accept_invites": {
475+
"enabled": True,
476+
},
477+
}
478+
)
479+
async def test_ignore_invite_for_locked_user(self) -> None:
480+
"""Tests that receiving an invite for a suspended user is ignored."""
481+
inviting_user_id = self.register_user("inviter", "pass", admin=True)
482+
inviting_user_tok = self.login("inviter", "pass")
483+
484+
# A local user who receives an invite
485+
invited_user_id = self.register_user("invitee", "pass")
486+
487+
# Create a room and send an invite to the other user
488+
room_id = self.helper.create_room_as(
489+
inviting_user_id,
490+
tok=inviting_user_tok,
491+
)
492+
493+
channel = self.make_request(
494+
"PUT",
495+
f"/_synapse/admin/v2/users/{invited_user_id}",
496+
{"locked": True},
497+
access_token=inviting_user_tok,
498+
)
499+
500+
assert channel.code == 200
501+
502+
self.helper.invite(
503+
room_id,
504+
inviting_user_id,
505+
invited_user_id,
506+
tok=inviting_user_tok,
507+
)
508+
509+
join_updates, b = sync_join(self, inviting_user_id)
510+
# Assert that the last event in the room was not a member event for the target user.
511+
self.assertEqual(
512+
join_updates[0].timeline.events[-1].content["membership"], "invite"
513+
)
514+
352515

353516
_request_key = 0
354517

@@ -647,6 +810,22 @@ def create_module(
647810
module_api.is_mine.side_effect = lambda a: a.split(":")[1] == "test"
648811
module_api.worker_name = worker_name
649812
module_api.sleep.return_value = make_multiple_awaitable(None)
813+
module_api.get_userinfo_by_id.return_value = UserInfo(
814+
user_id=UserID.from_string("@user:test"),
815+
is_admin=False,
816+
is_guest=False,
817+
consent_server_notice_sent=None,
818+
consent_ts=None,
819+
consent_version=None,
820+
appservice_id=None,
821+
creation_ts=0,
822+
user_type=None,
823+
is_deactivated=False,
824+
locked=False,
825+
is_shadow_banned=False,
826+
approved=True,
827+
suspended=False,
828+
)
650829

651830
if config_override is None:
652831
config_override = {}

0 commit comments

Comments
 (0)