Better handling of odd PLs during room upgrades

Fixes handling of rooms where we have permission to send the tombstone, but not
other state. We need to (a) fail more gracefully when we can't send the PLs in
the old room, and (b) not set the PLs in the new room until we are done with
the other stuff.
This commit is contained in:
Richard van der Hoff 2018-10-26 23:47:37 +01:00
parent 54bbe71867
commit db24d7f15e
2 changed files with 80 additions and 42 deletions

1
changelog.d/4099.feature Normal file
View File

@ -0,0 +1 @@
Support for replacing rooms with new ones

View File

@ -136,53 +136,91 @@ class RoomCreationHandler(BaseHandler):
requester, tombstone_event, tombstone_context, requester, tombstone_event, tombstone_context,
) )
# and finally, shut down the PLs in the old room, and update them in the new
# room.
old_room_state = yield tombstone_context.get_current_state_ids(self.store) old_room_state = yield tombstone_context.get_current_state_ids(self.store)
old_room_pl_event_id = old_room_state.get((EventTypes.PowerLevels, ""))
if old_room_pl_event_id is None: yield self._update_upgraded_room_pls(
logger.warning( requester, old_room_id, new_room_id, old_room_state,
"Not supported: upgrading a room with no PL event. Not setting PLs " )
"in old room.",
defer.returnValue(new_room_id)
@defer.inlineCallbacks
def _update_upgraded_room_pls(
self, requester, old_room_id, new_room_id, old_room_state,
):
"""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_state (dict[tuple[str, str], str]): the state map for the old room
Returns:
Deferred
"""
old_room_pl_event_id = old_room_state.get((EventTypes.PowerLevels, ""))
if old_room_pl_event_id is None:
logger.warning(
"Not supported: upgrading a room with no PL event. Not setting PLs "
"in old room.",
)
return
old_room_pl_state = yield self.store.get_event(old_room_pl_event_id)
# we try to stop regular users from speaking by setting the PL required
# to send regular events and invites to 'Moderator' level. That's normally
# 50, but if the default PL in a room is 50 or more, then we set the
# required PL above that.
pl_content = dict(old_room_pl_state.content)
users_default = int(pl_content.get("users_default", 0))
restricted_level = max(users_default + 1, 50)
updated = False
for v in ("invite", "events_default"):
current = int(pl_content.get(v, 0))
if current < restricted_level:
logger.info(
"Setting level for %s in %s to %i (was %i)",
v, old_room_id, restricted_level, current,
) )
pl_content[v] = restricted_level
updated = True
else: else:
# we try to stop regular users from speaking by setting the PL required logger.info(
# to send regular events and invites to 'Moderator' level. That's normally "Not setting level for %s (already %i)",
# 50, but if the default PL in a room is 50 or more, then we set the v, current,
# required PL above that. )
old_room_pl_state = yield self.store.get_event(old_room_pl_event_id) if updated:
pl_content = dict(old_room_pl_state.content) try:
users_default = int(pl_content.get("users_default", 0)) yield self.event_creation_handler.create_and_send_nonmember_event(
restricted_level = max(users_default + 1, 50) requester, {
"type": EventTypes.PowerLevels,
"state_key": '',
"room_id": old_room_id,
"sender": requester.user.to_string(),
"content": pl_content,
}, ratelimit=False,
)
except AuthError as e:
logger.warning("Unable to update PLs in old room: %s", e)
updated = False logger.info("Setting correct PLs in new room")
for v in ("invite", "events_default"): yield self.event_creation_handler.create_and_send_nonmember_event(
current = int(pl_content.get(v, 0)) requester, {
if current < restricted_level: "type": EventTypes.PowerLevels,
logger.debug( "state_key": '',
"Setting level for %s in %s to %i (was %i)", "room_id": new_room_id,
v, old_room_id, restricted_level, current, "sender": requester.user.to_string(),
) "content": old_room_pl_state.content,
pl_content[v] = restricted_level }, ratelimit=False,
updated = True )
else:
logger.debug(
"Not setting level for %s (already %i)",
v, current,
)
if updated:
yield self.event_creation_handler.create_and_send_nonmember_event(
requester, {
"type": EventTypes.PowerLevels,
"state_key": '',
"room_id": old_room_id,
"sender": user_id,
"content": pl_content,
}, ratelimit=False,
)
defer.returnValue(new_room_id)
@defer.inlineCallbacks @defer.inlineCallbacks
def clone_exiting_room( def clone_exiting_room(
@ -223,7 +261,6 @@ class RoomCreationHandler(BaseHandler):
initial_state = dict() initial_state = dict()
types_to_copy = ( types_to_copy = (
(EventTypes.PowerLevels, ""),
(EventTypes.JoinRules, ""), (EventTypes.JoinRules, ""),
(EventTypes.Name, ""), (EventTypes.Name, ""),
(EventTypes.Topic, ""), (EventTypes.Topic, ""),