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

Commit 0ebdde8

Browse files
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](https://github.com/matrix-org/synapse-dinsic/blob/ac50ed353b5fdbdf9f853be0d94b6fccaf33973e/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).
1 parent e8dcada commit 0ebdde8

File tree

3 files changed

+85
-18
lines changed

3 files changed

+85
-18
lines changed

changelog.d/68.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Override any missing default power level keys with DINUM's defaults when creating a room.

synapse/third_party_rules/access_rules.py

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -189,34 +189,40 @@ async def on_create_room(
189189
) and access_rule == AccessRules.DIRECT:
190190
raise SynapseError(400, "Invalid access rule")
191191

192+
default_power_levels = self._get_default_power_levels(
193+
requester.user.to_string()
194+
)
195+
192196
# Check if the creator can override values for the power levels.
193197
allowed = self._is_power_level_content_allowed(
194-
config.get("power_level_content_override", {}), access_rule
198+
config.get("power_level_content_override", {}),
199+
access_rule,
200+
default_power_levels,
195201
)
196202
if not allowed:
197203
raise SynapseError(400, "Invalid power levels content override")
198204

199-
use_default_power_levels = True
200-
if config.get("power_level_content_override"):
201-
use_default_power_levels = False
205+
custom_user_power_levels = config.get("power_level_content_override")
202206

203207
# Second loop for events we need to know the current rule to process.
204208
for event in config.get("initial_state", []):
205209
if event["type"] == EventTypes.PowerLevels:
206210
allowed = self._is_power_level_content_allowed(
207-
event["content"], access_rule
211+
event["content"], access_rule, default_power_levels
208212
)
209213
if not allowed:
210214
raise SynapseError(400, "Invalid power levels content")
211215

212-
use_default_power_levels = False
213-
214-
# If power levels were not overridden by the user, override with DINUM's preferred
215-
# defaults instead
216-
if use_default_power_levels:
217-
config["power_level_content_override"] = self._get_default_power_levels(
218-
requester.user.to_string()
219-
)
216+
custom_user_power_levels = event["content"]
217+
if custom_user_power_levels:
218+
# If the user is using their own power levels, but failed to provide an expected
219+
# key in the power levels content dictionary, fill it in from the defaults instead
220+
for key, value in default_power_levels.items():
221+
custom_user_power_levels.setdefault(key, value)
222+
else:
223+
# If power levels were not overridden by the user, completely override with the
224+
# defaults instead
225+
config["power_level_content_override"] = default_power_levels
220226

221227
return True
222228

@@ -710,7 +716,11 @@ def _on_membership_or_invite_direct(
710716
return True
711717

712718
def _is_power_level_content_allowed(
713-
self, content: Dict, access_rule: str, on_room_creation: bool = True
719+
self,
720+
content: Dict,
721+
access_rule: str,
722+
default_power_levels: Optional[Dict] = None,
723+
on_room_creation: bool = True,
714724
) -> bool:
715725
"""Check if a given power levels event is permitted under the given access rule.
716726
@@ -721,6 +731,8 @@ def _is_power_level_content_allowed(
721731
Args:
722732
content: The content of the m.room.power_levels event to check.
723733
access_rule: The access rule in place in this room.
734+
default_power_levels: The default power levels when a room is created with
735+
the specified access rule. Required if on_room_creation is True.
724736
on_room_creation: True if this call is happening during a room's
725737
creation, False otherwise.
726738
@@ -733,12 +745,19 @@ def _is_power_level_content_allowed(
733745
# have a special circumstance, but still want to encourage a certain pattern during
734746
# room creation.
735747
if on_room_creation:
736-
# If invite requirements are <PL50
737-
if content.get("invite", 50) < 50:
748+
# We specifically don't fail if "invite" or "state_default" are None, as those
749+
# values should be replaced with our "default" power level values anyways,
750+
# which are compliant
751+
752+
invite = default_power_levels["invite"]
753+
state_default = default_power_levels["state_default"]
754+
755+
# If invite requirements are less than our required defaults
756+
if content.get("invite", invite) < invite:
738757
return False
739758

740-
# If "other" state requirements are <PL100
741-
if content.get("state_default", 100) < 100:
759+
# If "other" state requirements are less than our required defaults
760+
if content.get("state_default", state_default) < state_default:
742761
return False
743762

744763
# Check if we need to apply the restrictions with the current rule.

tests/rest/client/test_room_access_rules.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,49 @@ def test_create_room_fails_on_incorrect_power_level_rules(self):
185185
expected_code=400,
186186
)
187187

188+
def test_create_room_with_missing_power_levels_use_default_values(self):
189+
"""
190+
Tests that a room created with custom power levels, but without defining invite or state_default
191+
succeeds, but the missing values are replaced with the defaults.
192+
"""
193+
194+
# Attempt to create a room without defining "invite" or "state_default"
195+
modified_power_levels = RoomAccessRules._get_default_power_levels(self.user_id)
196+
del modified_power_levels["invite"]
197+
del modified_power_levels["state_default"]
198+
room_id = self.create_room(
199+
direct=True,
200+
rule=AccessRules.DIRECT,
201+
initial_state=[
202+
{"type": "m.room.power_levels", "content": modified_power_levels}
203+
],
204+
)
205+
206+
# This should succeed, but the defaults should be put in place instead
207+
room_power_levels = self.helper.get_state(
208+
room_id, "m.room.power_levels", self.tok
209+
)
210+
self.assertEqual(room_power_levels["invite"], 50)
211+
self.assertEqual(room_power_levels["state_default"], 100)
212+
213+
# And now the same test, but using power_levels_content_override instead
214+
# of initial_state (which takes a slightly different codepath)
215+
modified_power_levels = RoomAccessRules._get_default_power_levels(self.user_id)
216+
del modified_power_levels["invite"]
217+
del modified_power_levels["state_default"]
218+
room_id = self.create_room(
219+
direct=True,
220+
rule=AccessRules.DIRECT,
221+
power_levels_content_override=modified_power_levels,
222+
)
223+
224+
# This should succeed, but the defaults should be put in place instead
225+
room_power_levels = self.helper.get_state(
226+
room_id, "m.room.power_levels", self.tok
227+
)
228+
self.assertEqual(room_power_levels["invite"], 50)
229+
self.assertEqual(room_power_levels["state_default"], 100)
230+
188231
def test_existing_room_can_change_power_levels(self):
189232
"""Tests that a room created with default power levels can have their power levels
190233
dropped after room creation
@@ -975,6 +1018,7 @@ def create_room(
9751018
rule=None,
9761019
preset=RoomCreationPreset.TRUSTED_PRIVATE_CHAT,
9771020
initial_state=None,
1021+
power_levels_content_override=None,
9781022
expected_code=200,
9791023
):
9801024
content = {"is_direct": direct, "preset": preset}
@@ -990,6 +1034,9 @@ def create_room(
9901034

9911035
content["initial_state"] += initial_state
9921036

1037+
if power_levels_content_override:
1038+
content["power_levels_content_override"] = power_levels_content_override
1039+
9931040
request, channel = self.make_request(
9941041
"POST", "/_matrix/client/r0/createRoom", content, access_token=self.tok,
9951042
)

0 commit comments

Comments
 (0)