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

Don't set new room alias before potential 403 #10930

Merged
merged 12 commits into from
Oct 25, 2021
1 change: 1 addition & 0 deletions changelog.d/10930.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Newly-created public rooms are now only assigned an alias if the room's creation has not been blocked by permission settings. Contributed by @AndrewFerr.
4 changes: 2 additions & 2 deletions synapse/handlers/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ async def create_association(
if not self.config.roomdirectory.is_alias_creation_allowed(
user_id, room_id, room_alias_str
):
# Lets just return a generic message, as there may be all sorts of
# Let's just return a generic message, as there may be all sorts of
# reasons why we said no. TODO: Allow configurable error messages
# per alias creation rule?
raise SynapseError(403, "Not allowed to create alias")
Expand Down Expand Up @@ -462,7 +462,7 @@ async def edit_published_room_list(
if not self.config.roomdirectory.is_publishing_room_allowed(
user_id, room_id, room_aliases
):
# Lets just return a generic message, as there may be all sorts of
# Let's just return a generic message, as there may be all sorts of
# reasons why we said no. TODO: Allow configurable error messages
# per alias creation rule?
raise SynapseError(403, "Not allowed to publish room")
Expand Down
18 changes: 9 additions & 9 deletions synapse/handlers/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,15 @@ async def create_room(
if not allowed_by_third_party_rules:
raise SynapseError(403, "Room visibility value not allowed.")

if is_public:
if not self.config.roomdirectory.is_publishing_room_allowed(
user_id, room_id, room_alias
):
# Let's just return a generic message, as there may be all sorts of
# reasons why we said no. TODO: Allow configurable error messages
# per alias creation rule?
raise SynapseError(403, "Not allowed to publish room")

directory_handler = self.hs.get_directory_handler()
if room_alias:
await directory_handler.create_association(
Expand All @@ -762,15 +771,6 @@ async def create_room(
check_membership=False,
)

if is_public:
if not self.config.roomdirectory.is_publishing_room_allowed(
user_id, room_id, room_alias
):
# Lets just return a generic message, as there may be all sorts of
# reasons why we said no. TODO: Allow configurable error messages
# per alias creation rule?
raise SynapseError(403, "Not allowed to publish room")

preset_config = config.get(
"preset",
RoomCreationPreset.PRIVATE_CHAT
Expand Down
54 changes: 54 additions & 0 deletions tests/handlers/test_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,60 @@ def test_allowed(self):
self.assertEquals(200, channel.code, channel.result)


class TestCreatePublishedRoomACL(unittest.HomeserverTestCase):
user_id = "@admin:test"
AndrewFerr marked this conversation as resolved.
Show resolved Hide resolved
denied_user_id = "@test:test"
data = {"room_alias_name": "unofficial_test"}

servlets = [directory.register_servlets, room.register_servlets]

def prepare(self, reactor, clock, hs):
# This time we add custom room list publication rules
config = {}
config["alias_creation_rules"] = []
config["room_list_publication_rules"] = [
{"user_id": "*", "alias": "*", "action": "deny"},
{"user_id": "@admin:test", "alias": "*", "action": "allow"},
]

rd_config = RoomDirectoryConfig()
rd_config.read_config(config)

self.hs.config.roomdirectory.is_publishing_room_allowed = (
rd_config.is_publishing_room_allowed
)

return hs

def test_denied(self):
AndrewFerr marked this conversation as resolved.
Show resolved Hide resolved
# NOTE Setting is_public=True isn't enough
AndrewFerr marked this conversation as resolved.
Show resolved Hide resolved
self.data["visibility"] = "public"
self.helper.create_room_as(
self.denied_user_id, extra_content=self.data, expect_code=403
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to mutate self.data, which might affect tests called afterwards. That means that tests can be sensitive to the order they're ran in, or might not run the same in isolation!

For this specific case, I think the other calls to create_room_as explicitly set is_public=False and so each test will behave the same no matter which order they're ran in. But I think we should try to avoid mutating self.data anyway, so the reader doesn't have to reason through this.

How about:

Suggested change
self.data["visibility"] = "public"
self.helper.create_room_as(
self.denied_user_id, extra_content=self.data, expect_code=403
)
data = self.data.copy()
data["visibility"] = "public"
self.helper.create_room_as(
self.denied_user_id, extra_content=data, expect_code=403
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to mutate self.data, which might affect tests called afterwards.

I'm not sure this is correct: I think there will be a new instance of TestCreatePublishedRoomACL for each test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohhh, but each instance will end up pointing at the same dict, I guess, because it's a class-level property rather than an instance-level one. Maybe it'd be better to intialise self.data in prepare?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is correct: I think there will be a new instance of TestCreatePublishedRoomACL for each test.

Looks like it! See https://gist.github.com/DMRobertson/40fe186843a327658cbee31bb381e1b2


def test_allowed_without_publish(self):
self.helper.create_room_as(
self.denied_user_id,
extra_content=self.data,
is_public=False,
expect_code=200,
)

def test_allowed_as_allowed(self):
self.helper.create_room_as(
self.user_id, extra_content=self.data, is_public=False, expect_code=200
)

def test_denied_then_retry_without_publish(self):
self.test_denied()
self.test_allowed_without_publish()
AndrewFerr marked this conversation as resolved.
Show resolved Hide resolved

def test_denied_then_retry_as_allowed(self):
self.test_denied()
self.test_allowed_as_allowed()
AndrewFerr marked this conversation as resolved.
Show resolved Hide resolved


class TestRoomListSearchDisabled(unittest.HomeserverTestCase):
user_id = "@test:test"

Expand Down