1
0

Override any missing default power level keys with DINUM's defaults when creating a room (#68)

The createRoom flow in DINUM's Synapse (through the AccessRules module which has hooks for all of this) already rejects a power levels content dict if it doesn't have high enough power levels to satisfy DINUM's [requirements](ac50ed353b/synapse/third_party_rules/access_rules.py (L233-L252)).

This PR ensures that any keys that aren't provided are replaced with the defaults, instead of just assuming the whole dict was correct (and thus those keys were set to mainline Synapse's default instead).
This commit is contained in:
Andrew Morgan
2020-10-29 10:48:29 +00:00
committed by GitHub
parent e8dcadaccd
commit 0ebdde8739
3 changed files with 85 additions and 18 deletions

1
changelog.d/68.misc Normal file
View File

@@ -0,0 +1 @@
Override any missing default power level keys with DINUM's defaults when creating a room.

View File

@@ -189,34 +189,40 @@ class RoomAccessRules(object):
) and access_rule == AccessRules.DIRECT:
raise SynapseError(400, "Invalid access rule")
default_power_levels = self._get_default_power_levels(
requester.user.to_string()
)
# Check if the creator can override values for the power levels.
allowed = self._is_power_level_content_allowed(
config.get("power_level_content_override", {}), access_rule
config.get("power_level_content_override", {}),
access_rule,
default_power_levels,
)
if not allowed:
raise SynapseError(400, "Invalid power levels content override")
use_default_power_levels = True
if config.get("power_level_content_override"):
use_default_power_levels = False
custom_user_power_levels = config.get("power_level_content_override")
# Second loop for events we need to know the current rule to process.
for event in config.get("initial_state", []):
if event["type"] == EventTypes.PowerLevels:
allowed = self._is_power_level_content_allowed(
event["content"], access_rule
event["content"], access_rule, default_power_levels
)
if not allowed:
raise SynapseError(400, "Invalid power levels content")
use_default_power_levels = False
# If power levels were not overridden by the user, override with DINUM's preferred
# defaults instead
if use_default_power_levels:
config["power_level_content_override"] = self._get_default_power_levels(
requester.user.to_string()
)
custom_user_power_levels = event["content"]
if custom_user_power_levels:
# If the user is using their own power levels, but failed to provide an expected
# key in the power levels content dictionary, fill it in from the defaults instead
for key, value in default_power_levels.items():
custom_user_power_levels.setdefault(key, value)
else:
# If power levels were not overridden by the user, completely override with the
# defaults instead
config["power_level_content_override"] = default_power_levels
return True
@@ -710,7 +716,11 @@ class RoomAccessRules(object):
return True
def _is_power_level_content_allowed(
self, content: Dict, access_rule: str, on_room_creation: bool = True
self,
content: Dict,
access_rule: str,
default_power_levels: Optional[Dict] = None,
on_room_creation: bool = True,
) -> bool:
"""Check if a given power levels event is permitted under the given access rule.
@@ -721,6 +731,8 @@ class RoomAccessRules(object):
Args:
content: The content of the m.room.power_levels event to check.
access_rule: The access rule in place in this room.
default_power_levels: The default power levels when a room is created with
the specified access rule. Required if on_room_creation is True.
on_room_creation: True if this call is happening during a room's
creation, False otherwise.
@@ -733,12 +745,19 @@ class RoomAccessRules(object):
# have a special circumstance, but still want to encourage a certain pattern during
# room creation.
if on_room_creation:
# If invite requirements are <PL50
if content.get("invite", 50) < 50:
# We specifically don't fail if "invite" or "state_default" are None, as those
# values should be replaced with our "default" power level values anyways,
# which are compliant
invite = default_power_levels["invite"]
state_default = default_power_levels["state_default"]
# If invite requirements are less than our required defaults
if content.get("invite", invite) < invite:
return False
# If "other" state requirements are <PL100
if content.get("state_default", 100) < 100:
# If "other" state requirements are less than our required defaults
if content.get("state_default", state_default) < state_default:
return False
# Check if we need to apply the restrictions with the current rule.

View File

@@ -185,6 +185,49 @@ class RoomAccessTestCase(unittest.HomeserverTestCase):
expected_code=400,
)
def test_create_room_with_missing_power_levels_use_default_values(self):
"""
Tests that a room created with custom power levels, but without defining invite or state_default
succeeds, but the missing values are replaced with the defaults.
"""
# Attempt to create a room without defining "invite" or "state_default"
modified_power_levels = RoomAccessRules._get_default_power_levels(self.user_id)
del modified_power_levels["invite"]
del modified_power_levels["state_default"]
room_id = self.create_room(
direct=True,
rule=AccessRules.DIRECT,
initial_state=[
{"type": "m.room.power_levels", "content": modified_power_levels}
],
)
# This should succeed, but the defaults should be put in place instead
room_power_levels = self.helper.get_state(
room_id, "m.room.power_levels", self.tok
)
self.assertEqual(room_power_levels["invite"], 50)
self.assertEqual(room_power_levels["state_default"], 100)
# And now the same test, but using power_levels_content_override instead
# of initial_state (which takes a slightly different codepath)
modified_power_levels = RoomAccessRules._get_default_power_levels(self.user_id)
del modified_power_levels["invite"]
del modified_power_levels["state_default"]
room_id = self.create_room(
direct=True,
rule=AccessRules.DIRECT,
power_levels_content_override=modified_power_levels,
)
# This should succeed, but the defaults should be put in place instead
room_power_levels = self.helper.get_state(
room_id, "m.room.power_levels", self.tok
)
self.assertEqual(room_power_levels["invite"], 50)
self.assertEqual(room_power_levels["state_default"], 100)
def test_existing_room_can_change_power_levels(self):
"""Tests that a room created with default power levels can have their power levels
dropped after room creation
@@ -975,6 +1018,7 @@ class RoomAccessTestCase(unittest.HomeserverTestCase):
rule=None,
preset=RoomCreationPreset.TRUSTED_PRIVATE_CHAT,
initial_state=None,
power_levels_content_override=None,
expected_code=200,
):
content = {"is_direct": direct, "preset": preset}
@@ -990,6 +1034,9 @@ class RoomAccessTestCase(unittest.HomeserverTestCase):
content["initial_state"] += initial_state
if power_levels_content_override:
content["power_levels_content_override"] = power_levels_content_override
request, channel = self.make_request(
"POST", "/_matrix/client/r0/createRoom", content, access_token=self.tok,
)