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

This commit is contained in:
Patrick Cloke 2020-08-26 12:05:36 -04:00 committed by GitHub
parent e0d6244beb
commit 6fe12c9512
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 24 deletions

1
changelog.d/8176.feature Normal file
View file

@ -0,0 +1 @@
Add support for shadow-banning users (ignoring any message send requests).

View file

@ -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
@ -227,9 +228,9 @@ class TypingWriterHandler(FollowerTypingHandler):
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")
@ -237,6 +238,11 @@ class TypingWriterHandler(FollowerTypingHandler):
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)
@ -256,9 +262,9 @@ class TypingWriterHandler(FollowerTypingHandler):
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")
@ -266,6 +272,11 @@ class TypingWriterHandler(FollowerTypingHandler):
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)

View file

@ -868,17 +868,21 @@ class RoomTypingRestServlet(RestServlet):
# 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, {}

View file

@ -21,7 +21,7 @@ from mock import ANY, Mock, call
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
@ -167,7 +167,10 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
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,
)
)
@ -194,7 +197,10 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
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,
)
)
@ -269,7 +275,9 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
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,
)
)
@ -309,7 +317,10 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
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,
)
)
@ -348,7 +359,10 @@ class TypingNotificationsTestCase(unittest.HomeserverTestCase):
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,
)
)

View file

@ -20,7 +20,7 @@ from synapse.api.constants import EventTypes, Membership
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
@ -175,7 +175,7 @@ class FederationSenderTestCase(BaseMultiWorkerStreamTestCase):
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,
)

View file

@ -179,6 +179,54 @@ class RoomTestCase(_ShadowBannedBase):
# 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)