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

Commit afa17f0

Browse files
authored
Return a 404 from /state for an outlier (#12087)
* Replace `get_state_for_pdu` with `get_state_ids_for_pdu` and `get_events_as_list`. * Return a 404 from `/state` and `/state_ids` for an outlier
1 parent bf9d549 commit afa17f0

File tree

3 files changed

+25
-44
lines changed

3 files changed

+25
-44
lines changed

changelog.d/12087.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug which caused the `/_matrix/federation/v1/state` and `.../state_ids` endpoints to return incorrect or invalid data when called for an event which we have stored as an "outlier".

synapse/federation/federation_server.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
Callable,
2323
Collection,
2424
Dict,
25-
Iterable,
2625
List,
2726
Optional,
2827
Tuple,
@@ -577,10 +576,10 @@ async def _on_state_ids_request_compute(
577576
async def _on_context_state_request_compute(
578577
self, room_id: str, event_id: Optional[str]
579578
) -> Dict[str, list]:
579+
pdus: Collection[EventBase]
580580
if event_id:
581-
pdus: Iterable[EventBase] = await self.handler.get_state_for_pdu(
582-
room_id, event_id
583-
)
581+
event_ids = await self.handler.get_state_ids_for_pdu(room_id, event_id)
582+
pdus = await self.store.get_events_as_list(event_ids)
584583
else:
585584
pdus = (await self.state.get_current_state(room_id)).values()
586585

synapse/handlers/federation.py

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -950,54 +950,35 @@ async def on_make_knock_request(
950950

951951
return event
952952

953-
async def get_state_for_pdu(self, room_id: str, event_id: str) -> List[EventBase]:
954-
"""Returns the state at the event. i.e. not including said event."""
955-
956-
event = await self.store.get_event(event_id, check_room_id=room_id)
957-
958-
state_groups = await self.state_store.get_state_groups(room_id, [event_id])
959-
960-
if state_groups:
961-
_, state = list(state_groups.items()).pop()
962-
results = {(e.type, e.state_key): e for e in state}
963-
964-
if event.is_state():
965-
# Get previous state
966-
if "replaces_state" in event.unsigned:
967-
prev_id = event.unsigned["replaces_state"]
968-
if prev_id != event.event_id:
969-
prev_event = await self.store.get_event(prev_id)
970-
results[(event.type, event.state_key)] = prev_event
971-
else:
972-
del results[(event.type, event.state_key)]
973-
974-
res = list(results.values())
975-
return res
976-
else:
977-
return []
978-
979953
async def get_state_ids_for_pdu(self, room_id: str, event_id: str) -> List[str]:
980954
"""Returns the state at the event. i.e. not including said event."""
981955
event = await self.store.get_event(event_id, check_room_id=room_id)
956+
if event.internal_metadata.outlier:
957+
raise NotFoundError("State not known at event %s" % (event_id,))
982958

983959
state_groups = await self.state_store.get_state_groups_ids(room_id, [event_id])
984960

985-
if state_groups:
986-
_, state = list(state_groups.items()).pop()
987-
results = state
961+
# get_state_groups_ids should return exactly one result
962+
assert len(state_groups) == 1
988963

989-
if event.is_state():
990-
# Get previous state
991-
if "replaces_state" in event.unsigned:
992-
prev_id = event.unsigned["replaces_state"]
993-
if prev_id != event.event_id:
994-
results[(event.type, event.state_key)] = prev_id
995-
else:
996-
results.pop((event.type, event.state_key), None)
964+
state_map = next(iter(state_groups.values()))
997965

998-
return list(results.values())
999-
else:
1000-
return []
966+
state_key = event.get_state_key()
967+
if state_key is not None:
968+
# the event was not rejected (get_event raises a NotFoundError for rejected
969+
# events) so the state at the event should include the event itself.
970+
assert (
971+
state_map.get((event.type, state_key)) == event.event_id
972+
), "State at event did not include event itself"
973+
974+
# ... but we need the state *before* that event
975+
if "replaces_state" in event.unsigned:
976+
prev_id = event.unsigned["replaces_state"]
977+
state_map[(event.type, state_key)] = prev_id
978+
else:
979+
del state_map[(event.type, state_key)]
980+
981+
return list(state_map.values())
1001982

1002983
async def on_backfill_request(
1003984
self, origin: str, room_id: str, pdu_list: List[str], limit: int

0 commit comments

Comments
 (0)