transfer local canonical aliases to new room on upgrade

When upgrading rooms, we reassign any local aliases from the old room to
the new room. This commit updates the m.room.canonical_alias events in
the old and new rooms to reflect which aliases were moved. The spec is
unclear on whether the server should do this[1], but it's consistent
with synapse's behavior.

I went with putting the canonical alias update logic inline, rather than
something like add_canonical_alias and remove_canonical_alias helper
functions to the alias service, because it's desirable to have the alias
updates be sent as a single event than a separate event for each change.

[1]: https://github.com/matrix-org/matrix-spec/issues/2142
This commit is contained in:
Olivia Lee 2025-05-18 12:29:32 -07:00
parent 07fb57be06
commit a34bca3986
No known key found for this signature in database
GPG key ID: 54D568A15B9CD1F9
2 changed files with 77 additions and 0 deletions

View file

@ -327,3 +327,5 @@ This will be the first release of Grapevine since it was forked from Conduit
([!124](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/124))
26. Allow adding canonical aliases from remote servers.
([!158](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/158))
27. Transfer local canonical aliases to the new room when upgrading a room.
([!186](https://gitlab.computer.surgery/matrix/grapevine/-/merge_requests/186))

View file

@ -777,6 +777,20 @@ pub(crate) async fn upgrade_room_route(
}
// Moves any local aliases to the new room
let mut old_canonical_alias: RoomCanonicalAliasEventContent = services()
.rooms
.state_accessor
.room_state_get(&body.room_id, &StateEventType::RoomCanonicalAlias, "")?
.and_then(|event| {
serde_json::from_str(event.content.get())
.inspect_err(|_| {
Error::bad_database("invalid m.room.canonical_alias event");
})
.ok()
})
.unwrap_or_default();
let mut new_canonical_alias = RoomCanonicalAliasEventContent::default();
for alias in services()
.rooms
.alias
@ -788,6 +802,67 @@ pub(crate) async fn upgrade_room_route(
&replacement_room,
sender_user,
)?;
if old_canonical_alias.alias.as_ref() == Some(&alias) {
new_canonical_alias.alias = Some(alias.clone());
old_canonical_alias.alias = None;
}
let mut is_alt_alias = false;
old_canonical_alias.alt_aliases.retain(|a| {
let equal = a == &alias;
is_alt_alias |= equal;
!equal
});
if is_alt_alias {
new_canonical_alias.alt_aliases.push(alias);
}
}
// Send updated events to both rooms if we moved any canonical aliases
let new_canonical_alias_empty = new_canonical_alias.alias.is_none()
&& new_canonical_alias.alt_aliases.is_empty();
if !new_canonical_alias_empty {
if let Err(error) = services()
.rooms
.timeline
.build_and_append_pdu(
PduBuilder {
event_type: TimelineEventType::RoomCanonicalAlias,
content: to_raw_value(&old_canonical_alias)
.expect("event is valid"),
unsigned: None,
state_key: Some(String::new()),
redacts: None,
},
sender_user,
&original_room_token,
)
.await
{
// The sender may not have had permission to send
// m.room.canonical_alias in the old room. Don't treat
// this as fatal.
warn!(
%error,
"Failed to remove local canonical aliases from old room"
);
}
services()
.rooms
.timeline
.build_and_append_pdu(
PduBuilder {
event_type: TimelineEventType::RoomCanonicalAlias,
content: to_raw_value(&new_canonical_alias)
.expect("event is valid"),
unsigned: None,
state_key: Some(String::new()),
redacts: None,
},
sender_user,
&replacement_room_token,
)
.await?;
}
// Get the old room power levels