Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c6e994f
Fix structure of initial_state when calling user_may_create_room modu…
hughns Jul 23, 2025
9f2539c
Changelog
hughns Jul 23, 2025
99f5de0
Apply suggestions from code review
hughns Jul 25, 2025
831773d
More suggested changes from review
hughns Jul 25, 2025
137da57
More suggestions from code review
hughns Jul 25, 2025
fa42783
More suggestions from review
hughns Jul 25, 2025
9203016
Pass `room_config` = `None` for a room upgrade and clarify documentation
hughns Jul 28, 2025
005a61f
Suggestions from review
hughns Jul 28, 2025
2e115b7
Revert the logic back to how it was pre adding room_config
hughns Jul 28, 2025
9c13a60
Revert "Revert the logic back to how it was pre adding room_config"
hughns Aug 29, 2025
3d17a88
Revert "Suggestions from review"
hughns Aug 29, 2025
44a0e76
Revert "Pass `room_config` = `None` for a room upgrade and clarify do…
hughns Aug 29, 2025
8693873
Update documentation
hughns Aug 29, 2025
ce47547
Merge branch 'develop' into hughns/user-may-create-room-initial-state…
hughns Aug 29, 2025
ca9d34c
Merge branch 'develop' into hughns/user-may-create-room-initial-state…
hughns Sep 12, 2025
a165f99
Another attempt at line spacing in tests
hughns Sep 17, 2025
dcc5ce3
Update docs/modules/spam_checker_callbacks.md
hughns Sep 17, 2025
91becca
Linewrap
hughns Sep 17, 2025
202aeca
Merge branch 'develop' into hughns/user-may-create-room-initial-state…
hughns Sep 17, 2025
14895d3
Apply suggestions from code review
hughns Sep 24, 2025
1f9b3e3
Improve comments for spam checker logic
hughns Sep 24, 2025
2bb2da4
Merge branch 'develop' into hughns/user-may-create-room-initial-state…
hughns Sep 24, 2025
4c6942c
Add TODO comments regarding preset for spam checker.
hughns Sep 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/18721.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `room_config` `initial_state` being mangled when the `user_may_create_room` spam-checker callback was called for room upgrades.
11 changes: 9 additions & 2 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ async def clone_existing_room(
# If so, mark the new room as non-federatable as well
creation_content[EventContentFields.FEDERATE] = False

initial_state = {}
initial_state: MutableStateMap = {}

# Replicate relevant room events
types_to_copy: List[Tuple[str, Optional[str]]] = [
Expand Down Expand Up @@ -586,7 +586,14 @@ async def clone_existing_room(
user_id,
{
"creation_content": creation_content,
"initial_state": list(initial_state.items()),
"initial_state": [
{
"type": state_key[0],
"state_key": state_key[1],
"content": event_content,
}
for state_key, event_content in initial_state.items()
],
},
)
if spam_check != self._spam_checker_module_callbacks.NOT_SPAM:
Expand Down
66 changes: 52 additions & 14 deletions tests/module_api/test_spamchecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from twisted.test.proto_helpers import MemoryReactor

from synapse.api.constants import EventContentFields, EventTypes
from synapse.config.server import DEFAULT_ROOM_VERSION
from synapse.rest import admin, login, room, room_upgrade_rest_servlet
from synapse.server import HomeServer
Expand Down Expand Up @@ -51,8 +52,8 @@ def create_room(self, content: JsonDict) -> FakeChannel:

return channel

def test_may_user_create_room(self) -> None:
"""Test that the may_user_create_room callback is called when a user
def test_user_may_create_room(self) -> None:
"""Test that the user_may_create_room callback is called when a user
creates a room, and that it receives the correct parameters.
"""

Expand All @@ -67,16 +68,49 @@ async def user_may_create_room(
user_may_create_room=user_may_create_room
)

channel = self.create_room({"foo": "baa"})
expected_room_config = {"foo": "baa"}

channel = self.create_room(expected_room_config)
self.assertEqual(channel.code, 200)
self.assertEqual(self.last_user_id, self.user_id)
self.assertEqual(self.last_room_config, expected_room_config)

def test_user_may_create_room_with_initial_state(self) -> None:
"""Test that the user_may_create_room callback is called when a user
creates a room with some initial state events, and that it receives the correct parameters.
"""

async def user_may_create_room(
user_id: str, room_config: JsonDict
) -> Union[Literal["NOT_SPAM"], Codes]:
self.last_room_config = room_config
self.last_user_id = user_id
return "NOT_SPAM"

self._module_api.register_spam_checker_callbacks(
user_may_create_room=user_may_create_room
)

expected_room_config = {
"foo": "baa",
"initial_state": [
{
"type": EventTypes.Topic,
"content": {EventContentFields.TOPIC: "foo"},
}
],
}

channel = self.create_room(expected_room_config)
self.assertEqual(channel.code, 200)
self.assertEqual(self.last_user_id, self.user_id)
self.assertEqual(self.last_room_config["foo"], "baa")
self.assertEqual(self.last_room_config, expected_room_config)

def test_may_user_create_room_on_upgrade(self) -> None:
"""Test that the may_user_create_room callback is called when a room is upgraded."""
def test_user_may_create_room_on_upgrade(self) -> None:
"""Test that the user_may_create_room callback is called when a room is upgraded."""

# First, create a room to upgrade.
channel = self.create_room({"topic": "foo"})
channel = self.create_room({EventContentFields.TOPIC: "foo"})
self.assertEqual(channel.code, 200)
room_id = channel.json_body["room_id"]

Expand Down Expand Up @@ -107,13 +141,15 @@ async def user_may_create_room(
# Check that the initial state received by callback contains the topic event.
self.assertTrue(
any(
event[0][0] == "m.room.topic" and event[1].get("topic") == "foo"
event.get("type") == EventTypes.Topic
and event.get("state_key") == ""
and event.get("content").get(EventContentFields.TOPIC) == "foo"
for event in self.last_room_config["initial_state"]
)
)

def test_may_user_create_room_disallowed(self) -> None:
"""Test that the codes response from may_user_create_room callback is respected
def test_user_may_create_room_disallowed(self) -> None:
"""Test that the codes response from user_may_create_room callback is respected
and returned via the API.
"""

Expand All @@ -128,14 +164,16 @@ async def user_may_create_room(
user_may_create_room=user_may_create_room
)

channel = self.create_room({"foo": "baa"})
expected_room_config = {"foo": "baa"}

channel = self.create_room(expected_room_config)
self.assertEqual(channel.code, 403)
self.assertEqual(channel.json_body["errcode"], Codes.UNAUTHORIZED)
self.assertEqual(self.last_user_id, self.user_id)
self.assertEqual(self.last_room_config["foo"], "baa")
self.assertEqual(self.last_room_config, expected_room_config)

def test_may_user_create_room_compatibility(self) -> None:
"""Test that the may_user_create_room callback is called when a user
def test_user_may_create_room_compatibility(self) -> None:
"""Test that the user_may_create_room callback is called when a user
creates a room for a module that uses the old callback signature
(without the `room_config` parameter)
"""
Expand Down
Loading