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

Do not propagate typing notifications from shadow-banned users. #8176

Merged
merged 4 commits into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/8176.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for shadow-banning users (ignoring any message send requests).
21 changes: 16 additions & 5 deletions synapse/handlers/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
# limitations under the License.

import logging
import random
from collections import namedtuple
from typing import TYPE_CHECKING, List, Set, Tuple

from synapse.api.errors import AuthError, SynapseError
from synapse.api.errors import AuthError, ShadowBanError, SynapseError
from synapse.metrics.background_process_metrics import run_as_background_process
from synapse.replication.tcp.streams import TypingStream
from synapse.types import UserID, get_domain_from_id
Expand Down Expand Up @@ -227,16 +228,21 @@ def _handle_timeout_for_member(self, now: int, member: RoomMember):
self._stopped_typing(member)
return

async def started_typing(self, target_user, auth_user, room_id, timeout):
async def started_typing(self, target_user, requester, room_id, timeout):
target_user_id = target_user.to_string()
auth_user_id = auth_user.to_string()
auth_user_id = requester.user.to_string()

if not self.is_mine_id(target_user_id):
raise SynapseError(400, "User is not hosted on this homeserver")

if target_user_id != auth_user_id:
raise AuthError(400, "Cannot set another user's typing state")

if requester.shadow_banned:
# We randomly sleep a bit just to annoy the requester.
await self.clock.sleep(random.randint(1, 10))
raise ShadowBanError()

await self.auth.check_user_in_room(room_id, target_user_id)

logger.debug("%s has started typing in %s", target_user_id, room_id)
Expand All @@ -256,16 +262,21 @@ async def started_typing(self, target_user, auth_user, room_id, timeout):

self._push_update(member=member, typing=True)

async def stopped_typing(self, target_user, auth_user, room_id):
async def stopped_typing(self, target_user, requester, room_id):
target_user_id = target_user.to_string()
auth_user_id = auth_user.to_string()
auth_user_id = requester.user.to_string()

if not self.is_mine_id(target_user_id):
raise SynapseError(400, "User is not hosted on this homeserver")

if target_user_id != auth_user_id:
raise AuthError(400, "Cannot set another user's typing state")

if requester.shadow_banned:
# We randomly sleep a bit just to annoy the requester.
await self.clock.sleep(random.randint(1, 10))
raise ShadowBanError()

await self.auth.check_user_in_room(room_id, target_user_id)

logger.debug("%s has stopped typing in %s", target_user_id, room_id)
Expand Down
26 changes: 15 additions & 11 deletions synapse/rest/client/v1/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -869,17 +869,21 @@ async def on_PUT(self, request, room_id, user_id):
# Limit timeout to stop people from setting silly typing timeouts.
timeout = min(content.get("timeout", 30000), 120000)

if content["typing"]:
await self.typing_handler.started_typing(
target_user=target_user,
auth_user=requester.user,
room_id=room_id,
timeout=timeout,
)
else:
await self.typing_handler.stopped_typing(
target_user=target_user, auth_user=requester.user, room_id=room_id
)
try:
if content["typing"]:
await self.typing_handler.started_typing(
target_user=target_user,
requester=requester,
room_id=room_id,
timeout=timeout,
)
else:
await self.typing_handler.stopped_typing(
target_user=target_user, requester=requester, room_id=room_id
)
except ShadowBanError:
# Pretend this worked without error.
pass

return 200, {}

Expand Down
26 changes: 20 additions & 6 deletions tests/handlers/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from twisted.internet import defer

from synapse.api.errors import AuthError
from synapse.types import UserID
from synapse.types import UserID, create_requester

from tests import unittest
from tests.test_utils import make_awaitable
Expand Down Expand Up @@ -167,7 +167,10 @@ def test_started_typing_local(self):

self.get_success(
self.handler.started_typing(
target_user=U_APPLE, auth_user=U_APPLE, room_id=ROOM_ID, timeout=20000
target_user=U_APPLE,
requester=create_requester(U_APPLE),
room_id=ROOM_ID,
timeout=20000,
)
)

Expand All @@ -194,7 +197,10 @@ def test_started_typing_remote_send(self):

self.get_success(
self.handler.started_typing(
target_user=U_APPLE, auth_user=U_APPLE, room_id=ROOM_ID, timeout=20000
target_user=U_APPLE,
requester=create_requester(U_APPLE),
room_id=ROOM_ID,
timeout=20000,
)
)

Expand Down Expand Up @@ -269,7 +275,9 @@ def test_stopped_typing(self):

self.get_success(
self.handler.stopped_typing(
target_user=U_APPLE, auth_user=U_APPLE, room_id=ROOM_ID
target_user=U_APPLE,
requester=create_requester(U_APPLE),
room_id=ROOM_ID,
)
)

Expand Down Expand Up @@ -309,7 +317,10 @@ def test_typing_timeout(self):

self.get_success(
self.handler.started_typing(
target_user=U_APPLE, auth_user=U_APPLE, room_id=ROOM_ID, timeout=10000
target_user=U_APPLE,
requester=create_requester(U_APPLE),
room_id=ROOM_ID,
timeout=10000,
)
)

Expand Down Expand Up @@ -348,7 +359,10 @@ def test_typing_timeout(self):

self.get_success(
self.handler.started_typing(
target_user=U_APPLE, auth_user=U_APPLE, room_id=ROOM_ID, timeout=10000
target_user=U_APPLE,
requester=create_requester(U_APPLE),
room_id=ROOM_ID,
timeout=10000,
)
)

Expand Down
4 changes: 2 additions & 2 deletions tests/replication/test_federation_sender_shard.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from synapse.events.builder import EventBuilderFactory
from synapse.rest.admin import register_servlets_for_client_rest_resource
from synapse.rest.client.v1 import login, room
from synapse.types import UserID
from synapse.types import UserID, create_requester

from tests.replication._base import BaseMultiWorkerStreamTestCase
from tests.test_utils import make_awaitable
Expand Down Expand Up @@ -175,7 +175,7 @@ def test_send_typing_sharded(self):
self.get_success(
typing_handler.started_typing(
target_user=UserID.from_string(user),
auth_user=UserID.from_string(user),
requester=create_requester(user),
room_id=room,
timeout=20000,
)
Expand Down
48 changes: 48 additions & 0 deletions tests/rest/client/test_shadow_banned.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,54 @@ def test_upgrade(self):
# The summary should be empty since the room doesn't exist.
self.assertEqual(summary, {})

def test_typing(self):
"""Typing notifications should not be propagated into the room."""
# The create works fine.
room_id = self.helper.create_room_as(
self.banned_user_id, tok=self.banned_access_token
)

request, channel = self.make_request(
"PUT",
"/rooms/%s/typing/%s" % (room_id, self.banned_user_id),
{"typing": True, "timeout": 30000},
access_token=self.banned_access_token,
)
self.render(request)
self.assertEquals(200, channel.code)

# There should be no typing events.
event_source = self.hs.get_event_sources().sources["typing"]
self.assertEquals(event_source.get_current_key(), 0)

# The other user can join and send typing events.
self.helper.join(room_id, self.other_user_id, tok=self.other_access_token)

request, channel = self.make_request(
"PUT",
"/rooms/%s/typing/%s" % (room_id, self.other_user_id),
{"typing": True, "timeout": 30000},
access_token=self.other_access_token,
)
self.render(request)
self.assertEquals(200, channel.code)

# These appear in the room.
self.assertEquals(event_source.get_current_key(), 1)
events = self.get_success(
event_source.get_new_events(from_key=0, room_ids=[room_id])
)
self.assertEquals(
events[0],
[
{
"type": "m.typing",
"room_id": room_id,
"content": {"user_ids": [self.other_user_id]},
}
],
)


# To avoid the tests timing out don't add a delay to "annoy the requester".
@patch("random.randint", new=lambda a, b: 0)
Expand Down