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

Block attempts to annotate the same event twice #5212

Merged
merged 3 commits into from
May 21, 2019
Merged
Show file tree
Hide file tree
Changes from 2 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/5212.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add experimental support for relations (aka reactions and edits).
16 changes: 15 additions & 1 deletion synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from twisted.internet import defer
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 (
AuthError,
Codes,
Expand Down Expand Up @@ -601,6 +601,20 @@ def create_new_client_event(self, builder, requester=None,

self.validator.validate_new(event)

# We now check that if this event is an annotation that the can't
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

words no sense

# 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(
"Created event %s",
event.event_id,
Expand Down
48 changes: 45 additions & 3 deletions synapse/storage/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,7 @@ def get_applicable_edit(self, event_id):
"""

def _get_applicable_edit_txn(txn):
txn.execute(
sql, (event_id, RelationTypes.REPLACE,)
)
txn.execute(sql, (event_id, RelationTypes.REPLACE))
row = txn.fetchone()
if row:
return row[0]
Expand All @@ -367,6 +365,50 @@ def _get_applicable_edit_txn(txn):
edit_event = yield self.get_event(edit_id, allow_none=True)
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):
def _handle_event_relations(self, txn, event):
Expand Down
27 changes: 26 additions & 1 deletion tests/rest/client/v2_alpha/test_relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ def test_deny_membership(self):
channel = self._send_relation(RelationTypes.ANNOTATION, EventTypes.Member)
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):
"""Tests that calling pagination API corectly the latest relations.
"""
Expand Down Expand Up @@ -234,14 +243,30 @@ def test_aggregation_pagination_within_group(self):
"""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 = []
for _ in range(10):
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)
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
channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", key="a")
self.assertEquals(200, channel.code, channel.json_body)
Expand Down