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

Commit 319d073

Browse files
Override the power levels defaults, enforce mod requirement for invites, admin requirements for unknown state events (#61)
This PR modifies the `RoomAccessRules` module, an implementation of `ThirdPartyEventRules`, to both: * Modify the default power levels when creating a room to set: - `invite` to be minimum PL50 - `state_default` to be minimum PL100 * Enforce this when creating the room.
1 parent 3d1c941 commit 319d073

File tree

3 files changed

+119
-7
lines changed

3 files changed

+119
-7
lines changed

changelog.d/61.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Change the minimum power levels for invites and other state events in new rooms.

synapse/third_party_rules/access_rules.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ def on_create_room(
110110
If yes, make sure the event is correct. Otherwise, append an event with the
111111
default rule to the initial state.
112112
113+
Checks if a m.rooms.power_levels event is being set during room creation.
114+
If yes, make sure the event is allowed. Otherwise, set power_level_content_override
115+
in the config dict to our modified version of the default room power levels.
116+
113117
Args:
114118
requester: The user who is making the createRoom request.
115119
config: The createRoom config dict provided by the user.
@@ -187,6 +191,10 @@ def on_create_room(
187191
if not allowed:
188192
raise SynapseError(400, "Invalid power levels content override")
189193

194+
use_default_power_levels = True
195+
if config.get("power_level_content_override"):
196+
use_default_power_levels = False
197+
190198
# Second loop for events we need to know the current rule to process.
191199
for event in config.get("initial_state", []):
192200
if event["type"] == EventTypes.PowerLevels:
@@ -196,8 +204,44 @@ def on_create_room(
196204
if not allowed:
197205
raise SynapseError(400, "Invalid power levels content")
198206

207+
use_default_power_levels = False
208+
209+
# If power levels were not overridden by the user, override with DINUM's preferred
210+
# defaults instead
211+
if use_default_power_levels:
212+
config["power_level_content_override"] = self._get_default_power_levels(
213+
requester.user.to_string()
214+
)
215+
199216
return True
200217

218+
# If power levels are not overridden by the user during room creation, the following
219+
# rules are used instead. Changes from Synapse's default power levels are noted.
220+
#
221+
# The same power levels are currently applied regardless of room preset.
222+
@staticmethod
223+
def _get_default_power_levels(user_id: str) -> Dict:
224+
return {
225+
"users": {user_id: 100},
226+
"users_default": 0,
227+
"events": {
228+
EventTypes.Name: 50,
229+
EventTypes.PowerLevels: 100,
230+
EventTypes.RoomHistoryVisibility: 100,
231+
EventTypes.CanonicalAlias: 50,
232+
EventTypes.RoomAvatar: 50,
233+
EventTypes.Tombstone: 100,
234+
EventTypes.ServerACL: 100,
235+
EventTypes.RoomEncryption: 100,
236+
},
237+
"events_default": 0,
238+
"state_default": 100, # Admins should be the only ones to perform other tasks
239+
"ban": 50,
240+
"kick": 50,
241+
"redact": 50,
242+
"invite": 50, # All rooms should require mod to invite, even private
243+
}
244+
201245
@defer.inlineCallbacks
202246
def check_threepid_can_be_invited(
203247
self, medium: str, address: str, state_events: StateMap[EventBase],
@@ -272,7 +316,9 @@ def check_event_allowed(
272316
rule = self._get_rule_from_state(state_events)
273317

274318
if event.type == EventTypes.PowerLevels:
275-
return self._is_power_level_content_allowed(event.content, rule)
319+
return self._is_power_level_content_allowed(
320+
event.content, rule, on_room_creation=False
321+
)
276322

277323
if event.type == EventTypes.Member or event.type == EventTypes.ThirdPartyInvite:
278324
return self._on_membership_or_invite(event, rule, state_events)
@@ -468,7 +514,9 @@ def _on_membership_or_invite_direct(
468514

469515
return True
470516

471-
def _is_power_level_content_allowed(self, content: Dict, access_rule: str) -> bool:
517+
def _is_power_level_content_allowed(
518+
self, content: Dict, access_rule: str, on_room_creation: bool = True
519+
) -> bool:
472520
"""Check if a given power levels event is permitted under the given access rule.
473521
474522
It shouldn't be allowed if it either changes the default PL to a non-0 value or
@@ -478,10 +526,26 @@ def _is_power_level_content_allowed(self, content: Dict, access_rule: str) -> bo
478526
Args:
479527
content: The content of the m.room.power_levels event to check.
480528
access_rule: The access rule in place in this room.
529+
on_room_creation: True if this call is happening during a room's
530+
creation, False otherwise.
481531
482532
Returns:
483533
Whether the content of the power levels event is valid.
484534
"""
535+
# Only enforce these rules during room creation
536+
#
537+
# We want to allow admins to modify or fix the power levels in a room if they
538+
# have a special circumstance, but still want to encourage a certain pattern during
539+
# room creation.
540+
if on_room_creation:
541+
# If invite requirements are <PL50
542+
if content.get("invite", 50) < 50:
543+
return False
544+
545+
# If "other" state requirements are <PL100
546+
if content.get("state_default", 100) < 100:
547+
return False
548+
485549
# Check if we need to apply the restrictions with the current rule.
486550
if access_rule not in RULES_WITH_RESTRICTED_POWER_LEVELS:
487551
return True

tests/rest/client/test_room_access_rules.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
from synapse.api.constants import EventTypes, JoinRules, RoomCreationPreset
2424
from synapse.rest import admin
2525
from synapse.rest.client.v1 import login, room
26-
from synapse.third_party_rules.access_rules import ACCESS_RULES_TYPE, AccessRules
26+
from synapse.third_party_rules.access_rules import (
27+
ACCESS_RULES_TYPE,
28+
AccessRules,
29+
RoomAccessRules,
30+
)
2731

2832
from tests import unittest
2933

@@ -149,6 +153,52 @@ def test_create_room_direct_invalid_rule(self):
149153
"""
150154
self.create_room(direct=True, rule=AccessRules.RESTRICTED, expected_code=400)
151155

156+
def test_create_room_default_power_level_rules(self):
157+
"""Tests that a room created with no power level overrides instead uses the dinum
158+
defaults
159+
"""
160+
room_id = self.create_room(direct=True, rule=AccessRules.DIRECT)
161+
power_levels = self.helper.get_state(room_id, "m.room.power_levels", self.tok)
162+
163+
# Inviting another user should require PL50, even in private rooms
164+
self.assertEqual(power_levels["invite"], 50)
165+
# Sending arbitrary state events should require PL100
166+
self.assertEqual(power_levels["state_default"], 100)
167+
168+
def test_create_room_fails_on_incorrect_power_level_rules(self):
169+
"""Tests that a room created with power levels lower than that required are rejected"""
170+
modified_power_levels = RoomAccessRules._get_default_power_levels(self.user_id)
171+
modified_power_levels["invite"] = 0
172+
modified_power_levels["state_default"] = 50
173+
174+
self.create_room(
175+
direct=True,
176+
rule=AccessRules.DIRECT,
177+
initial_state=[
178+
{"type": "m.room.power_levels", "content": modified_power_levels}
179+
],
180+
expected_code=400,
181+
)
182+
183+
def test_existing_room_can_change_power_levels(self):
184+
"""Tests that a room created with default power levels can have their power levels
185+
dropped after room creation
186+
"""
187+
# Creates a room with the default power levels
188+
room_id = self.create_room(
189+
direct=True, rule=AccessRules.DIRECT, expected_code=200,
190+
)
191+
192+
# Attempt to drop invite and state_default power levels after the fact
193+
room_power_levels = self.helper.get_state(
194+
room_id, "m.room.power_levels", self.tok
195+
)
196+
room_power_levels["invite"] = 0
197+
room_power_levels["state_default"] = 50
198+
self.helper.send_state(
199+
room_id, "m.room.power_levels", room_power_levels, self.tok
200+
)
201+
152202
def test_public_room(self):
153203
"""Tests that it's not possible to have a room with the public join rule and an
154204
access rule that's not restricted.
@@ -642,10 +692,7 @@ def create_room(
642692
content["initial_state"] += initial_state
643693

644694
request, channel = self.make_request(
645-
"POST",
646-
"/_matrix/client/r0/createRoom",
647-
json.dumps(content),
648-
access_token=self.tok,
695+
"POST", "/_matrix/client/r0/createRoom", content, access_token=self.tok,
649696
)
650697
self.render(request)
651698

0 commit comments

Comments
 (0)