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

Commit

Permalink
Don't create an empty room when checking for MAU limits (#12713)
Browse files Browse the repository at this point in the history
  • Loading branch information
babolivier authored May 13, 2022
1 parent aec69d2 commit 9013104
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 56 deletions.
1 change: 1 addition & 0 deletions changelog.d/12713.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a bug introduced in Synapse 1.30.0 where empty rooms could be automatically created if a monthly active users limit is set.
40 changes: 10 additions & 30 deletions synapse/server_notices/resource_limits_server_notices.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
ServerNoticeMsgType,
)
from synapse.api.errors import AuthError, ResourceLimitError, SynapseError
from synapse.server_notices.server_notices_manager import SERVER_NOTICE_ROOM_TAG

if TYPE_CHECKING:
from synapse.server import HomeServer
Expand Down Expand Up @@ -71,18 +70,19 @@ async def maybe_send_server_notice_to_user(self, user_id: str) -> None:
# In practice, not sure we can ever get here
return

room_id = await self._server_notices_manager.get_or_create_notice_room_for_user(
# Check if there's a server notice room for this user.
room_id = await self._server_notices_manager.maybe_get_notice_room_for_user(
user_id
)

if not room_id:
logger.warning("Failed to get server notices room")
return

await self._check_and_set_tags(user_id, room_id)

# Determine current state of room
currently_blocked, ref_events = await self._is_room_currently_blocked(room_id)
if room_id is not None:
# Determine current state of room
currently_blocked, ref_events = await self._is_room_currently_blocked(
room_id
)
else:
currently_blocked = False
ref_events = []

limit_msg = None
limit_type = None
Expand Down Expand Up @@ -161,26 +161,6 @@ async def _apply_limit_block_notification(
user_id, content, EventTypes.Pinned, ""
)

async def _check_and_set_tags(self, user_id: str, room_id: str) -> None:
"""
Since server notices rooms were originally not with tags,
important to check that tags have been set correctly
Args:
user_id(str): the user in question
room_id(str): the server notices room for that user
"""
tags = await self._store.get_tags_for_room(user_id, room_id)
need_to_set_tag = True
if tags:
if SERVER_NOTICE_ROOM_TAG in tags:
# tag already present, nothing to do here
need_to_set_tag = False
if need_to_set_tag:
max_id = await self._account_data_handler.add_tag_to_room(
user_id, room_id, SERVER_NOTICE_ROOM_TAG, {}
)
self._notifier.on_new_event("account_data_key", max_id, users=[user_id])

async def _is_room_currently_blocked(self, room_id: str) -> Tuple[bool, List[str]]:
"""
Determines if the room is currently blocked
Expand Down
70 changes: 45 additions & 25 deletions synapse/server_notices/server_notices_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,35 @@ async def send_notice(
)
return event

@cached()
async def maybe_get_notice_room_for_user(self, user_id: str) -> Optional[str]:
"""Try to look up the server notice room for this user if it exists.
Does not create one if none can be found.
Args:
user_id: the user we want a server notice room for.
Returns:
The room's ID, or None if no room could be found.
"""
rooms = await self._store.get_rooms_for_local_user_where_membership_is(
user_id, [Membership.INVITE, Membership.JOIN]
)
for room in rooms:
# it's worth noting that there is an asymmetry here in that we
# expect the user to be invited or joined, but the system user must
# be joined. This is kinda deliberate, in that if somebody somehow
# manages to invite the system user to a room, that doesn't make it
# the server notices room.
user_ids = await self._store.get_users_in_room(room.room_id)
if len(user_ids) <= 2 and self.server_notices_mxid in user_ids:
# we found a room which our user shares with the system notice
# user
return room.room_id

return None

@cached()
async def get_or_create_notice_room_for_user(self, user_id: str) -> str:
"""Get the room for notices for a given user
Expand All @@ -112,31 +141,20 @@ async def get_or_create_notice_room_for_user(self, user_id: str) -> str:
self.server_notices_mxid, authenticated_entity=self._server_name
)

rooms = await self._store.get_rooms_for_local_user_where_membership_is(
user_id, [Membership.INVITE, Membership.JOIN]
)
for room in rooms:
# it's worth noting that there is an asymmetry here in that we
# expect the user to be invited or joined, but the system user must
# be joined. This is kinda deliberate, in that if somebody somehow
# manages to invite the system user to a room, that doesn't make it
# the server notices room.
user_ids = await self._store.get_users_in_room(room.room_id)
if len(user_ids) <= 2 and self.server_notices_mxid in user_ids:
# we found a room which our user shares with the system notice
# user
logger.info(
"Using existing server notices room %s for user %s",
room.room_id,
user_id,
)
await self._update_notice_user_profile_if_changed(
requester,
room.room_id,
self._config.servernotices.server_notices_mxid_display_name,
self._config.servernotices.server_notices_mxid_avatar_url,
)
return room.room_id
room_id = await self.maybe_get_notice_room_for_user(user_id)
if room_id is not None:
logger.info(
"Using existing server notices room %s for user %s",
room_id,
user_id,
)
await self._update_notice_user_profile_if_changed(
requester,
room_id,
self._config.servernotices.server_notices_mxid_display_name,
self._config.servernotices.server_notices_mxid_avatar_url,
)
return room_id

# apparently no existing notice room: create a new one
logger.info("Creating server notices room for %s", user_id)
Expand Down Expand Up @@ -166,6 +184,8 @@ async def get_or_create_notice_room_for_user(self, user_id: str) -> str:
)
room_id = info["room_id"]

self.maybe_get_notice_room_for_user.invalidate((user_id,))

max_id = await self._account_data_handler.add_tag_to_room(
user_id, room_id, SERVER_NOTICE_ROOM_TAG, {}
)
Expand Down
11 changes: 10 additions & 1 deletion tests/server_notices/test_resource_limits_server_notices.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ def prepare(self, reactor, clock, hs):
self._rlsn._server_notices_manager.get_or_create_notice_room_for_user = Mock(
return_value=make_awaitable("!something:localhost")
)
self._rlsn._server_notices_manager.maybe_get_notice_room_for_user = Mock(
return_value=make_awaitable("!something:localhost")
)
self._rlsn._store.add_tag_to_room = Mock(return_value=make_awaitable(None))
self._rlsn._store.get_tags_for_room = Mock(return_value=make_awaitable({}))

Expand Down Expand Up @@ -102,6 +105,7 @@ def test_maybe_send_server_notice_to_user_remove_blocked_notice(self):
)
self.get_success(self._rlsn.maybe_send_server_notice_to_user(self.user_id))
# Would be better to check the content, but once == remove blocking event
self._rlsn._server_notices_manager.maybe_get_notice_room_for_user.assert_called_once()
self._send_notice.assert_called_once()

def test_maybe_send_server_notice_to_user_remove_blocked_notice_noop(self):
Expand Down Expand Up @@ -300,7 +304,10 @@ def test_no_invite_without_notice(self):
hasn't been reached (since it's the only user and the limit is 5), so users
shouldn't receive a server notice.
"""
self.register_user("user", "password")
m = Mock(return_value=make_awaitable(None))
self._rlsn._server_notices_manager.maybe_get_notice_room_for_user = m

user_id = self.register_user("user", "password")
tok = self.login("user", "password")

channel = self.make_request("GET", "/sync?timeout=0", access_token=tok)
Expand All @@ -309,6 +316,8 @@ def test_no_invite_without_notice(self):
"rooms", channel.json_body, "Got invites without server notice"
)

m.assert_called_once_with(user_id)

def test_invite_with_notice(self):
"""Tests that, if the MAU limit is hit, the server notices user invites each user
to a room in which it has sent a notice.
Expand Down

0 comments on commit 9013104

Please sign in to comment.