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

Transfer power level state events on room upgrade #6237

Merged
merged 12 commits into from
Dec 2, 2019
Merged
1 change: 1 addition & 0 deletions changelog.d/6237.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Transfer non-standard power levels on room upgrade.
67 changes: 61 additions & 6 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def _upgrade_room(self, requester, old_room_id, new_version):
old_room_version, tombstone_event, tombstone_context
)

yield self.clone_existing_room(
requester_original_power_level = yield self.clone_existing_room(
requester,
old_room_id=old_room_id,
new_room_id=new_room_id,
Expand Down Expand Up @@ -198,22 +198,33 @@ def _upgrade_room(self, requester, old_room_id, new_version):
# finally, shut down the PLs in the old room, and update them in the new
# room.
yield self._update_upgraded_room_pls(
requester, old_room_id, new_room_id, old_room_state
requester,
old_room_id,
new_room_id,
old_room_state,
requester_original_power_level=requester_original_power_level,
)

return new_room_id

@defer.inlineCallbacks
def _update_upgraded_room_pls(
self, requester, old_room_id, new_room_id, old_room_state
self,
requester,
old_room_id,
new_room_id,
old_room_state,
requester_original_power_level=None,
):
"""Send updated power levels in both rooms after an upgrade

Args:
requester (synapse.types.Requester): the user requesting the upgrade
old_room_id (unicode): the id of the room to be replaced
new_room_id (unicode): the id of the replacement room
old_room_id (str): the id of the room to be replaced
new_room_id (str): the id of the replacement room
old_room_state (dict[tuple[str, str], str]): the state map for the old room
requester_original_power_level (int|None): If not None, reduce the requester's
power level to this value in the new room

Returns:
Deferred
Expand Down Expand Up @@ -271,6 +282,13 @@ def _update_upgraded_room_pls(
logger.warning("Unable to update PLs in old room: %s", e)

logger.info("Setting correct PLs in new room")
if requester_original_power_level:
# The requester's power level was temporarily raised earlier in the upgrade
# process in order for them to be allowed to send the room's initial state.
# Return them to their original power level in the old room now
old_room_pl_state.content["users"][
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved
requester.user.to_string()
] = requester_original_power_level
yield self.event_creation_handler.create_and_send_nonmember_event(
requester,
{
Expand Down Expand Up @@ -298,7 +316,11 @@ def clone_existing_room(
tombstone_event_id (unicode|str): the ID of the tombstone event in the old
room.
Returns:
Deferred[None]
Deferred[int|None]: Returns an int if the requester's power level
needed to be raised in the new room to send initial state events.
The returned int is the user's original power level. The calling function
should return the user's powerlevel to this value for consistency
once all other initial state events have been sent. None otherwise.
"""
user_id = requester.user.to_string()

Expand Down Expand Up @@ -333,6 +355,7 @@ def clone_existing_room(
(EventTypes.Encryption, ""),
(EventTypes.ServerACL, ""),
(EventTypes.RelatedGroups, ""),
(EventTypes.PowerLevels, ""),
)

old_room_state_ids = yield self.store.get_filtered_current_state_ids(
Expand All @@ -346,6 +369,35 @@ def clone_existing_room(
if old_event:
initial_state[k] = old_event.content

# Resolve the minimum power level required to send any state event
# We will give the upgrading user this power level temporarily (if necessary) such that
# they are able to copy all of the state events over, then revert them back to their
# original power level afterwards
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

# Copy over user power levels now as this will not be possible with >100PL users once
# the room has been created

power_levels = initial_state[(EventTypes.PowerLevels, "")]

# Calculate the minimum power level needed to clone the room
event_power_levels = power_levels.get("events", {})
state_default = power_levels.get("state_default", 0)
ban = power_levels.get("ban")
needed_power_level = max(state_default, ban, max(event_power_levels.values()))

# Raise the requester's power level in the new room if necessary
current_power_level = power_levels["users"][requester.user.to_string()]
raised_power_level = None
if current_power_level < needed_power_level:
# Save and return this power level
raised_power_level = current_power_level

# Assign this power level to the requester
power_levels["users"][requester.user.to_string()] = needed_power_level

# Transfer the power levels via power_level_content_override instead of initial_state
del initial_state[(EventTypes.PowerLevels, "")]
anoadragon453 marked this conversation as resolved.
Show resolved Hide resolved

yield self._send_events_for_new_room(
requester,
new_room_id,
Expand All @@ -355,6 +407,7 @@ def clone_existing_room(
invite_list=[],
initial_state=initial_state,
creation_content=creation_content,
power_level_content_override=power_levels,
)

# Transfer membership events
Expand All @@ -381,6 +434,8 @@ def clone_existing_room(
content=old_event.content,
)

return raised_power_level

# XXX invites/joins
# XXX 3pid invites

Expand Down