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

Commit aa8708e

Browse files
Allow events to be created with no prev_events (MSC2716) (#11243)
The event still needs to have `auth_events` defined to be valid. Split out from #11114
1 parent 8391bd6 commit aa8708e

File tree

4 files changed

+124
-7
lines changed

4 files changed

+124
-7
lines changed

changelog.d/11243.misc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow specific, experimental events to be created without `prev_events`. Used by [MSC2716](https://github.com/matrix-org/matrix-doc/pull/2716).

synapse/handlers/message.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ async def create_event(
496496
require_consent: bool = True,
497497
outlier: bool = False,
498498
historical: bool = False,
499+
allow_no_prev_events: bool = False,
499500
depth: Optional[int] = None,
500501
) -> Tuple[EventBase, EventContext]:
501502
"""
@@ -607,6 +608,7 @@ async def create_event(
607608
prev_event_ids=prev_event_ids,
608609
auth_event_ids=auth_event_ids,
609610
depth=depth,
611+
allow_no_prev_events=allow_no_prev_events,
610612
)
611613

612614
# In an ideal world we wouldn't need the second part of this condition. However,
@@ -882,6 +884,7 @@ async def create_new_client_event(
882884
prev_event_ids: Optional[List[str]] = None,
883885
auth_event_ids: Optional[List[str]] = None,
884886
depth: Optional[int] = None,
887+
allow_no_prev_events: bool = False,
885888
) -> Tuple[EventBase, EventContext]:
886889
"""Create a new event for a local client
887890
@@ -912,6 +915,7 @@ async def create_new_client_event(
912915
full_state_ids_at_event = None
913916
if auth_event_ids is not None:
914917
# If auth events are provided, prev events must be also.
918+
# prev_event_ids could be an empty array though.
915919
assert prev_event_ids is not None
916920

917921
# Copy the full auth state before it stripped down
@@ -943,14 +947,22 @@ async def create_new_client_event(
943947
else:
944948
prev_event_ids = await self.store.get_prev_events_for_room(builder.room_id)
945949

946-
# we now ought to have some prev_events (unless it's a create event).
947-
#
948-
# do a quick sanity check here, rather than waiting until we've created the
950+
# Do a quick sanity check here, rather than waiting until we've created the
949951
# event and then try to auth it (which fails with a somewhat confusing "No
950952
# create event in auth events")
951-
assert (
952-
builder.type == EventTypes.Create or len(prev_event_ids) > 0
953-
), "Attempting to create an event with no prev_events"
953+
if allow_no_prev_events:
954+
# We allow events with no `prev_events` but it better have some `auth_events`
955+
assert (
956+
builder.type == EventTypes.Create
957+
# Allow an event to have empty list of prev_event_ids
958+
# only if it has auth_event_ids.
959+
or auth_event_ids
960+
), "Attempting to create a non-m.room.create event with no prev_events or auth_event_ids"
961+
else:
962+
# we now ought to have some prev_events (unless it's a create event).
963+
assert (
964+
builder.type == EventTypes.Create or prev_event_ids
965+
), "Attempting to create a non-m.room.create event with no prev_events"
954966

955967
event = await builder.build(
956968
prev_event_ids=prev_event_ids,

synapse/handlers/room_member.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,8 @@ async def update_membership_locked(
658658
if block_invite:
659659
raise SynapseError(403, "Invites have been disabled on this server")
660660

661-
if prev_event_ids:
661+
# An empty prev_events list is allowed as long as the auth_event_ids are present
662+
if prev_event_ids is not None:
662663
return await self._local_membership_update(
663664
requester=requester,
664665
target=target,

tests/handlers/test_message.py

+103
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from synapse.util.stringutils import random_string
2424

2525
from tests import unittest
26+
from tests.test_utils.event_injection import create_event
2627

2728
logger = logging.getLogger(__name__)
2829

@@ -51,6 +52,24 @@ def prepare(self, reactor, clock, hs):
5152

5253
self.requester = create_requester(self.user_id, access_token_id=self.token_id)
5354

55+
def _create_and_persist_member_event(self) -> Tuple[EventBase, EventContext]:
56+
# Create a member event we can use as an auth_event
57+
memberEvent, memberEventContext = self.get_success(
58+
create_event(
59+
self.hs,
60+
room_id=self.room_id,
61+
type="m.room.member",
62+
sender=self.requester.user.to_string(),
63+
state_key=self.requester.user.to_string(),
64+
content={"membership": "join"},
65+
)
66+
)
67+
self.get_success(
68+
self.persist_event_storage.persist_event(memberEvent, memberEventContext)
69+
)
70+
71+
return memberEvent, memberEventContext
72+
5473
def _create_duplicate_event(self, txn_id: str) -> Tuple[EventBase, EventContext]:
5574
"""Create a new event with the given transaction ID. All events produced
5675
by this method will be considered duplicates.
@@ -156,6 +175,90 @@ def test_duplicated_txn_id_one_call(self):
156175
self.assertEqual(len(events), 2)
157176
self.assertEqual(events[0].event_id, events[1].event_id)
158177

178+
def test_when_empty_prev_events_allowed_create_event_with_empty_prev_events(self):
179+
"""When we set allow_no_prev_events=True, should be able to create a
180+
event without any prev_events (only auth_events).
181+
"""
182+
# Create a member event we can use as an auth_event
183+
memberEvent, _ = self._create_and_persist_member_event()
184+
185+
# Try to create the event with empty prev_events bit with some auth_events
186+
event, _ = self.get_success(
187+
self.handler.create_event(
188+
self.requester,
189+
{
190+
"type": EventTypes.Message,
191+
"room_id": self.room_id,
192+
"sender": self.requester.user.to_string(),
193+
"content": {"msgtype": "m.text", "body": random_string(5)},
194+
},
195+
# Empty prev_events is the key thing we're testing here
196+
prev_event_ids=[],
197+
# But with some auth_events
198+
auth_event_ids=[memberEvent.event_id],
199+
# Allow no prev_events!
200+
allow_no_prev_events=True,
201+
)
202+
)
203+
self.assertIsNotNone(event)
204+
205+
def test_when_empty_prev_events_not_allowed_reject_event_with_empty_prev_events(
206+
self,
207+
):
208+
"""When we set allow_no_prev_events=False, shouldn't be able to create a
209+
event without any prev_events even if it has auth_events. Expect an
210+
exception to be raised.
211+
"""
212+
# Create a member event we can use as an auth_event
213+
memberEvent, _ = self._create_and_persist_member_event()
214+
215+
# Try to create the event with empty prev_events but with some auth_events
216+
self.get_failure(
217+
self.handler.create_event(
218+
self.requester,
219+
{
220+
"type": EventTypes.Message,
221+
"room_id": self.room_id,
222+
"sender": self.requester.user.to_string(),
223+
"content": {"msgtype": "m.text", "body": random_string(5)},
224+
},
225+
# Empty prev_events is the key thing we're testing here
226+
prev_event_ids=[],
227+
# But with some auth_events
228+
auth_event_ids=[memberEvent.event_id],
229+
# We expect the test to fail because empty prev_events are not
230+
# allowed here!
231+
allow_no_prev_events=False,
232+
),
233+
AssertionError,
234+
)
235+
236+
def test_when_empty_prev_events_allowed_reject_event_with_empty_prev_events_and_auth_events(
237+
self,
238+
):
239+
"""When we set allow_no_prev_events=True, should be able to create a
240+
event without any prev_events or auth_events. Expect an exception to be
241+
raised.
242+
"""
243+
# Try to create the event with empty prev_events and empty auth_events
244+
self.get_failure(
245+
self.handler.create_event(
246+
self.requester,
247+
{
248+
"type": EventTypes.Message,
249+
"room_id": self.room_id,
250+
"sender": self.requester.user.to_string(),
251+
"content": {"msgtype": "m.text", "body": random_string(5)},
252+
},
253+
prev_event_ids=[],
254+
# The event should be rejected when there are no auth_events
255+
auth_event_ids=[],
256+
# Allow no prev_events!
257+
allow_no_prev_events=True,
258+
),
259+
AssertionError,
260+
)
261+
159262

160263
class ServerAclValidationTestCase(unittest.HomeserverTestCase):
161264
servlets = [

0 commit comments

Comments
 (0)