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

Test room alias deletion #11327

Merged
merged 7 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/11327.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test that room alias deletion works as intended.
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ disallow_untyped_defs = True
[mypy-tests.storage.test_user_directory]
disallow_untyped_defs = True

[mypy-tests.rest.client.test_directory]
disallow_untyped_defs = True
Comment on lines +285 to +286
Copy link
Member

Choose a reason for hiding this comment

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

I think this is unnecessary with #11322?

Copy link
Member

Choose a reason for hiding this comment

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

OH NO! That had a bug in it....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

missing a .* I think? Ouch!


;; Dependencies without annotations
;; Before ignoring a module, check to see if type stubs are available.
;; The `typeshed` project maintains stubs here:
Expand Down
6 changes: 5 additions & 1 deletion synapse/handlers/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ async def delete_association(
)

room_id = await self._delete_association(room_alias)
if room_id is None:
# It's possible someone else deleted the association after the
# checks above, but before we did the deletion.
raise NotFoundError("Unknown room alias")

try:
await self._update_canonical_alias(requester, user_id, room_id, room_alias)
Expand All @@ -225,7 +229,7 @@ async def delete_appservice_association(
)
await self._delete_association(room_alias)

async def _delete_association(self, room_alias: RoomAlias) -> str:
async def _delete_association(self, room_alias: RoomAlias) -> Optional[str]:
if not self.hs.is_mine(room_alias):
raise SynapseError(400, "Room alias must be local")

Expand Down
7 changes: 5 additions & 2 deletions synapse/storage/databases/main/directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from synapse.api.errors import SynapseError
from synapse.storage._base import SQLBaseStore
from synapse.storage.database import LoggingTransaction
from synapse.types import RoomAlias
from synapse.util.caches.descriptors import cached

Expand Down Expand Up @@ -126,14 +127,16 @@ def alias_txn(txn):


class DirectoryStore(DirectoryWorkerStore):
async def delete_room_alias(self, room_alias: RoomAlias) -> str:
async def delete_room_alias(self, room_alias: RoomAlias) -> Optional[str]:
clokep marked this conversation as resolved.
Show resolved Hide resolved
room_id = await self.db_pool.runInteraction(
"delete_room_alias", self._delete_room_alias_txn, room_alias
)

return room_id

def _delete_room_alias_txn(self, txn, room_alias: RoomAlias) -> str:
def _delete_room_alias_txn(
self, txn: LoggingTransaction, room_alias: RoomAlias
) -> Optional[str]:
txn.execute(
"SELECT room_id FROM room_aliases WHERE room_alias = ?",
(room_alias.to_string(),),
Expand Down
105 changes: 77 additions & 28 deletions tests/rest/client/test_directory.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
from http import HTTPStatus

from twisted.test.proto_helpers import MemoryReactor

from synapse.rest import admin
from synapse.rest.client import directory, login, room
from synapse.server import HomeServer
from synapse.types import RoomAlias
from synapse.util import Clock
from synapse.util.stringutils import random_string

from tests import unittest
Expand All @@ -32,15 +36,19 @@ class DirectoryTestCase(unittest.HomeserverTestCase):
room.register_servlets,
]

def make_homeserver(self, reactor, clock):
def make_homeserver(self, reactor: MemoryReactor, clock: Clock) -> HomeServer:
config = self.default_config()
config["require_membership_for_aliases"] = True

self.hs = self.setup_test_homeserver(config=config)

return self.hs

def prepare(self, reactor, clock, homeserver):
Copy link
Member

Choose a reason for hiding this comment

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

FWIW It might be easy to do a PR that modifies this (and make_homeserver) with sed + black. Not sure if it would make things pass or not though.

def prepare(
self, reactor: MemoryReactor, clock: Clock, homeserver: HomeServer
) -> None:
"""Create two local users and access tokens for them.
One of them creates a room."""
self.room_owner = self.register_user("room_owner", "test")
self.room_owner_tok = self.login("room_owner", "test")

Expand All @@ -51,39 +59,39 @@ def prepare(self, reactor, clock, homeserver):
self.user = self.register_user("user", "test")
self.user_tok = self.login("user", "test")

def test_state_event_not_in_room(self):
def test_state_event_not_in_room(self) -> None:
self.ensure_user_left_room()
self.set_alias_via_state_event(403)
self.set_alias_via_state_event(HTTPStatus.FORBIDDEN)

def test_directory_endpoint_not_in_room(self):
def test_directory_endpoint_not_in_room(self) -> None:
self.ensure_user_left_room()
self.set_alias_via_directory(403)
self.set_alias_via_directory(HTTPStatus.FORBIDDEN)

def test_state_event_in_room_too_long(self):
def test_state_event_in_room_too_long(self) -> None:
self.ensure_user_joined_room()
self.set_alias_via_state_event(400, alias_length=256)
self.set_alias_via_state_event(HTTPStatus.BAD_REQUEST, alias_length=256)

def test_directory_in_room_too_long(self):
def test_directory_in_room_too_long(self) -> None:
self.ensure_user_joined_room()
self.set_alias_via_directory(400, alias_length=256)
self.set_alias_via_directory(HTTPStatus.BAD_REQUEST, alias_length=256)

@override_config({"default_room_version": 5})
def test_state_event_user_in_v5_room(self):
def test_state_event_user_in_v5_room(self) -> None:
"""Test that a regular user can add alias events before room v6"""
self.ensure_user_joined_room()
self.set_alias_via_state_event(200)
self.set_alias_via_state_event(HTTPStatus.OK)

@override_config({"default_room_version": 6})
def test_state_event_v6_room(self):
def test_state_event_v6_room(self) -> None:
"""Test that a regular user can *not* add alias events from room v6"""
self.ensure_user_joined_room()
self.set_alias_via_state_event(403)
self.set_alias_via_state_event(HTTPStatus.FORBIDDEN)

def test_directory_in_room(self):
def test_directory_in_room(self) -> None:
self.ensure_user_joined_room()
self.set_alias_via_directory(200)
self.set_alias_via_directory(HTTPStatus.OK)

def test_room_creation_too_long(self):
def test_room_creation_too_long(self) -> None:
url = "/_matrix/client/r0/createRoom"

# We use deliberately a localpart under the length threshold so
Expand All @@ -93,9 +101,9 @@ def test_room_creation_too_long(self):
channel = self.make_request(
"POST", url, request_data, access_token=self.user_tok
)
self.assertEqual(channel.code, 400, channel.result)
self.assertEqual(channel.code, HTTPStatus.BAD_REQUEST, channel.result)

def test_room_creation(self):
def test_room_creation(self) -> None:
url = "/_matrix/client/r0/createRoom"

# Check with an alias of allowed length. There should already be
Expand All @@ -106,9 +114,46 @@ def test_room_creation(self):
channel = self.make_request(
"POST", url, request_data, access_token=self.user_tok
)
self.assertEqual(channel.code, 200, channel.result)
self.assertEqual(channel.code, HTTPStatus.OK, channel.result)

def test_deleting_alias_via_directory(self) -> None:
# Add an alias for the room. We must be joined to do so.
self.ensure_user_joined_room()
alias = self.set_alias_via_directory(HTTPStatus.OK)

# Then try to remove the alias
channel = self.make_request(
"DELETE",
f"/_matrix/client/r0/directory/room/{alias}",
access_token=self.user_tok,
)
self.assertEqual(channel.code, HTTPStatus.OK, channel.result)

def test_deleting_nonexistant_alias(self) -> None:
# Check that no alias exists
alias = "#potato:test"
channel = self.make_request(
"GET",
f"/_matrix/client/r0/directory/room/{alias}",
access_token=self.user_tok,
)
self.assertEqual(channel.code, HTTPStatus.NOT_FOUND, channel.result)
self.assertIn("error", channel.json_body, channel.json_body)
self.assertEqual(channel.json_body["errcode"], "M_NOT_FOUND", channel.json_body)

# Then try to remove the alias
channel = self.make_request(
"DELETE",
f"/_matrix/client/r0/directory/room/{alias}",
access_token=self.user_tok,
)
self.assertEqual(channel.code, HTTPStatus.NOT_FOUND, channel.result)
self.assertIn("error", channel.json_body, channel.json_body)
self.assertEqual(channel.json_body["errcode"], "M_NOT_FOUND", channel.json_body)

def set_alias_via_state_event(self, expected_code, alias_length=5):
def set_alias_via_state_event(
self, expected_code: HTTPStatus, alias_length: int = 5
) -> None:
url = "/_matrix/client/r0/rooms/%s/state/m.room.aliases/%s" % (
self.room_id,
self.hs.hostname,
Expand All @@ -122,26 +167,30 @@ def set_alias_via_state_event(self, expected_code, alias_length=5):
)
self.assertEqual(channel.code, expected_code, channel.result)

def set_alias_via_directory(self, expected_code, alias_length=5):
url = "/_matrix/client/r0/directory/room/%s" % self.random_alias(alias_length)
def set_alias_via_directory(
self, expected_code: HTTPStatus, alias_length: int = 5
) -> str:
alias = self.random_alias(alias_length)
url = "/_matrix/client/r0/directory/room/%s" % alias
data = {"room_id": self.room_id}
request_data = json.dumps(data)

channel = self.make_request(
"PUT", url, request_data, access_token=self.user_tok
)
self.assertEqual(channel.code, expected_code, channel.result)
return alias

def random_alias(self, length):
def random_alias(self, length: int) -> str:
return RoomAlias(random_string(length), self.hs.hostname).to_string()

def ensure_user_left_room(self):
def ensure_user_left_room(self) -> None:
self.ensure_membership("leave")

def ensure_user_joined_room(self):
def ensure_user_joined_room(self) -> None:
self.ensure_membership("join")

def ensure_membership(self, membership):
def ensure_membership(self, membership: str) -> None:
try:
if membership == "leave":
self.helper.leave(room=self.room_id, user=self.user, tok=self.user_tok)
Expand Down