Block attempts to annotate the same event twice

This commit is contained in:
Erik Johnston 2019-05-20 17:39:05 +01:00
parent 24b93b9c76
commit c7ec06e8a6
3 changed files with 86 additions and 5 deletions

View File

@ -22,7 +22,7 @@ from canonicaljson import encode_canonical_json, json
from twisted.internet import defer from twisted.internet import defer
from twisted.internet.defer import succeed from twisted.internet.defer import succeed
from synapse.api.constants import EventTypes, Membership from synapse.api.constants import EventTypes, Membership, RelationTypes
from synapse.api.errors import ( from synapse.api.errors import (
AuthError, AuthError,
Codes, Codes,
@ -601,6 +601,20 @@ class EventCreationHandler(object):
self.validator.validate_new(event) self.validator.validate_new(event)
# We now check that if this event is an annotation that the can't
# annotate the same way twice (e.g. stops users from liking an event
# multiple times).
relation = event.content.get("m.relates_to", {})
if relation.get("rel_type") == RelationTypes.ANNOTATION:
relates_to = relation["event_id"]
aggregation_key = relation["key"]
already_exists = yield self.store.has_user_annotated_event(
relates_to, event.type, aggregation_key, event.sender,
)
if already_exists:
raise SynapseError(400, "Can't send same reaction twice")
logger.debug( logger.debug(
"Created event %s", "Created event %s",
event.event_id, event.event_id,

View File

@ -350,9 +350,7 @@ class RelationsWorkerStore(SQLBaseStore):
""" """
def _get_applicable_edit_txn(txn): def _get_applicable_edit_txn(txn):
txn.execute( txn.execute(sql, (event_id, RelationTypes.REPLACE))
sql, (event_id, RelationTypes.REPLACE,)
)
row = txn.fetchone() row = txn.fetchone()
if row: if row:
return row[0] return row[0]
@ -367,6 +365,50 @@ class RelationsWorkerStore(SQLBaseStore):
edit_event = yield self.get_event(edit_id, allow_none=True) edit_event = yield self.get_event(edit_id, allow_none=True)
defer.returnValue(edit_event) defer.returnValue(edit_event)
def has_user_annotated_event(self, parent_id, event_type, aggregation_key, sender):
"""Check if a user has already annotated an event with the same key
(e.g. already liked an event).
Args:
parent_id (str): The event being annotated
event_type (str): The event type of the annotation
aggregation_key (str): The aggregation key of the annotation
sender (str): The sender of the annotation
Returns:
Deferred[bool]
"""
sql = """
SELECT 1 FROM event_relations
INNER JOIN events USING (event_id)
WHERE
relates_to_id = ?
AND relation_type = ?
AND type = ?
AND sender = ?
AND aggregation_key = ?
LIMIT 1;
"""
def _get_if_user_has_annotated_event(txn):
txn.execute(
sql,
(
parent_id,
RelationTypes.ANNOTATION,
event_type,
sender,
aggregation_key,
),
)
return bool(txn.fetchone())
return self.runInteraction(
"get_if_user_has_annotated_event", _get_if_user_has_annotated_event
)
class RelationsStore(RelationsWorkerStore): class RelationsStore(RelationsWorkerStore):
def _handle_event_relations(self, txn, event): def _handle_event_relations(self, txn, event):

View File

@ -90,6 +90,15 @@ class RelationsTestCase(unittest.HomeserverTestCase):
channel = self._send_relation(RelationTypes.ANNOTATION, EventTypes.Member) channel = self._send_relation(RelationTypes.ANNOTATION, EventTypes.Member)
self.assertEquals(400, channel.code, channel.json_body) self.assertEquals(400, channel.code, channel.json_body)
def test_deny_double_react(self):
"""Test that we deny relations on membership events
"""
channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a")
self.assertEquals(200, channel.code, channel.json_body)
channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a")
self.assertEquals(400, channel.code, channel.json_body)
def test_basic_paginate_relations(self): def test_basic_paginate_relations(self):
"""Tests that calling pagination API corectly the latest relations. """Tests that calling pagination API corectly the latest relations.
""" """
@ -234,14 +243,30 @@ class RelationsTestCase(unittest.HomeserverTestCase):
"""Test that we can paginate within an annotation group. """Test that we can paginate within an annotation group.
""" """
# We need to create ten separate users to send each reaction.
access_tokens = [self.user_token, self.user2_token]
idx = 0
while len(access_tokens) < 10:
user_id, token = self._create_user("test" + str(idx))
idx += 1
self.helper.join(self.room, user=user_id, tok=token)
access_tokens.append(token)
idx = 0
expected_event_ids = [] expected_event_ids = []
for _ in range(10): for _ in range(10):
channel = self._send_relation( channel = self._send_relation(
RelationTypes.ANNOTATION, "m.reaction", key=u"👍" RelationTypes.ANNOTATION,
"m.reaction",
key=u"👍",
access_token=access_tokens[idx],
) )
self.assertEquals(200, channel.code, channel.json_body) self.assertEquals(200, channel.code, channel.json_body)
expected_event_ids.append(channel.json_body["event_id"]) expected_event_ids.append(channel.json_body["event_id"])
idx += 1
# Also send a different type of reaction so that we test we don't see it # Also send a different type of reaction so that we test we don't see it
channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", key="a") channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", key="a")
self.assertEquals(200, channel.code, channel.json_body) self.assertEquals(200, channel.code, channel.json_body)