diff options
author | David Robertson <davidr@element.io> | 2021-08-20 17:50:44 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-20 17:50:44 +0100 |
commit | ecd823d766fecdd7e1c7163073c097a0084122e2 (patch) | |
tree | 3e6810436695937eab90d966d95011f41d990436 /tests/rest/client/v2_alpha | |
parent | Simplify tests for the device admin rest API. (#10664) (diff) | |
download | synapse-ecd823d766fecdd7e1c7163073c097a0084122e2.tar.xz |
Flatten tests/rest/client/{v1,v2_alpha} too (#10667)
Diffstat (limited to 'tests/rest/client/v2_alpha')
-rw-r--r-- | tests/rest/client/v2_alpha/__init__.py | 0 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_account.py | 1000 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_auth.py | 717 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_capabilities.py | 212 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_filter.py | 111 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_keys.py | 91 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_password_policy.py | 177 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_register.py | 746 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_relations.py | 724 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_report_event.py | 82 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_sendtodevice.py | 200 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_shared_rooms.py | 142 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_sync.py | 686 | ||||
-rw-r--r-- | tests/rest/client/v2_alpha/test_upgrade_room.py | 159 |
14 files changed, 0 insertions, 5047 deletions
diff --git a/tests/rest/client/v2_alpha/__init__.py b/tests/rest/client/v2_alpha/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/tests/rest/client/v2_alpha/__init__.py +++ /dev/null diff --git a/tests/rest/client/v2_alpha/test_account.py b/tests/rest/client/v2_alpha/test_account.py deleted file mode 100644 index b946fca8b3..0000000000 --- a/tests/rest/client/v2_alpha/test_account.py +++ /dev/null @@ -1,1000 +0,0 @@ -# Copyright 2015-2016 OpenMarket Ltd -# Copyright 2017-2018 New Vector Ltd -# Copyright 2019 The Matrix.org Foundation C.I.C. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 -import os -import re -from email.parser import Parser -from typing import Optional - -import pkg_resources - -import synapse.rest.admin -from synapse.api.constants import LoginType, Membership -from synapse.api.errors import Codes, HttpResponseException -from synapse.appservice import ApplicationService -from synapse.rest.client import account, login, register, room -from synapse.rest.synapse.client.password_reset import PasswordResetSubmitTokenResource - -from tests import unittest -from tests.server import FakeSite, make_request -from tests.unittest import override_config - - -class PasswordResetTestCase(unittest.HomeserverTestCase): - - servlets = [ - account.register_servlets, - synapse.rest.admin.register_servlets_for_client_rest_resource, - register.register_servlets, - login.register_servlets, - ] - - def make_homeserver(self, reactor, clock): - config = self.default_config() - - # Email config. - config["email"] = { - "enable_notifs": False, - "template_dir": os.path.abspath( - pkg_resources.resource_filename("synapse", "res/templates") - ), - "smtp_host": "127.0.0.1", - "smtp_port": 20, - "require_transport_security": False, - "smtp_user": None, - "smtp_pass": None, - "notif_from": "test@example.com", - } - config["public_baseurl"] = "https://example.com" - - hs = self.setup_test_homeserver(config=config) - - async def sendmail( - reactor, smtphost, smtpport, from_addr, to_addrs, msg, **kwargs - ): - self.email_attempts.append(msg) - - self.email_attempts = [] - hs.get_send_email_handler()._sendmail = sendmail - - return hs - - def prepare(self, reactor, clock, hs): - self.store = hs.get_datastore() - self.submit_token_resource = PasswordResetSubmitTokenResource(hs) - - def test_basic_password_reset(self): - """Test basic password reset flow""" - old_password = "monkey" - new_password = "kangeroo" - - user_id = self.register_user("kermit", old_password) - self.login("kermit", old_password) - - email = "test@example.com" - - # Add a threepid - self.get_success( - self.store.user_add_threepid( - user_id=user_id, - medium="email", - address=email, - validated_at=0, - added_at=0, - ) - ) - - client_secret = "foobar" - session_id = self._request_token(email, client_secret) - - self.assertEquals(len(self.email_attempts), 1) - link = self._get_link_from_email() - - self._validate_token(link) - - self._reset_password(new_password, session_id, client_secret) - - # Assert we can log in with the new password - self.login("kermit", new_password) - - # Assert we can't log in with the old password - self.attempt_wrong_password_login("kermit", old_password) - - @override_config({"rc_3pid_validation": {"burst_count": 3}}) - def test_ratelimit_by_email(self): - """Test that we ratelimit /requestToken for the same email.""" - old_password = "monkey" - new_password = "kangeroo" - - user_id = self.register_user("kermit", old_password) - self.login("kermit", old_password) - - email = "test1@example.com" - - # Add a threepid - self.get_success( - self.store.user_add_threepid( - user_id=user_id, - medium="email", - address=email, - validated_at=0, - added_at=0, - ) - ) - - def reset(ip): - client_secret = "foobar" - session_id = self._request_token(email, client_secret, ip) - - self.assertEquals(len(self.email_attempts), 1) - link = self._get_link_from_email() - - self._validate_token(link) - - self._reset_password(new_password, session_id, client_secret) - - self.email_attempts.clear() - - # We expect to be able to make three requests before getting rate - # limited. - # - # We change IPs to ensure that we're not being ratelimited due to the - # same IP - reset("127.0.0.1") - reset("127.0.0.2") - reset("127.0.0.3") - - with self.assertRaises(HttpResponseException) as cm: - reset("127.0.0.4") - - self.assertEqual(cm.exception.code, 429) - - def test_basic_password_reset_canonicalise_email(self): - """Test basic password reset flow - Request password reset with different spelling - """ - old_password = "monkey" - new_password = "kangeroo" - - user_id = self.register_user("kermit", old_password) - self.login("kermit", old_password) - - email_profile = "test@example.com" - email_passwort_reset = "TEST@EXAMPLE.COM" - - # Add a threepid - self.get_success( - self.store.user_add_threepid( - user_id=user_id, - medium="email", - address=email_profile, - validated_at=0, - added_at=0, - ) - ) - - client_secret = "foobar" - session_id = self._request_token(email_passwort_reset, client_secret) - - self.assertEquals(len(self.email_attempts), 1) - link = self._get_link_from_email() - - self._validate_token(link) - - self._reset_password(new_password, session_id, client_secret) - - # Assert we can log in with the new password - self.login("kermit", new_password) - - # Assert we can't log in with the old password - self.attempt_wrong_password_login("kermit", old_password) - - def test_cant_reset_password_without_clicking_link(self): - """Test that we do actually need to click the link in the email""" - old_password = "monkey" - new_password = "kangeroo" - - user_id = self.register_user("kermit", old_password) - self.login("kermit", old_password) - - email = "test@example.com" - - # Add a threepid - self.get_success( - self.store.user_add_threepid( - user_id=user_id, - medium="email", - address=email, - validated_at=0, - added_at=0, - ) - ) - - client_secret = "foobar" - session_id = self._request_token(email, client_secret) - - self.assertEquals(len(self.email_attempts), 1) - - # Attempt to reset password without clicking the link - self._reset_password(new_password, session_id, client_secret, expected_code=401) - - # Assert we can log in with the old password - self.login("kermit", old_password) - - # Assert we can't log in with the new password - self.attempt_wrong_password_login("kermit", new_password) - - def test_no_valid_token(self): - """Test that we do actually need to request a token and can't just - make a session up. - """ - old_password = "monkey" - new_password = "kangeroo" - - user_id = self.register_user("kermit", old_password) - self.login("kermit", old_password) - - email = "test@example.com" - - # Add a threepid - self.get_success( - self.store.user_add_threepid( - user_id=user_id, - medium="email", - address=email, - validated_at=0, - added_at=0, - ) - ) - - client_secret = "foobar" - session_id = "weasle" - - # Attempt to reset password without even requesting an email - self._reset_password(new_password, session_id, client_secret, expected_code=401) - - # Assert we can log in with the old password - self.login("kermit", old_password) - - # Assert we can't log in with the new password - self.attempt_wrong_password_login("kermit", new_password) - - @unittest.override_config({"request_token_inhibit_3pid_errors": True}) - def test_password_reset_bad_email_inhibit_error(self): - """Test that triggering a password reset with an email address that isn't bound - to an account doesn't leak the lack of binding for that address if configured - that way. - """ - self.register_user("kermit", "monkey") - self.login("kermit", "monkey") - - email = "test@example.com" - - client_secret = "foobar" - session_id = self._request_token(email, client_secret) - - self.assertIsNotNone(session_id) - - def _request_token(self, email, client_secret, ip="127.0.0.1"): - channel = self.make_request( - "POST", - b"account/password/email/requestToken", - {"client_secret": client_secret, "email": email, "send_attempt": 1}, - client_ip=ip, - ) - - if channel.code != 200: - raise HttpResponseException( - channel.code, - channel.result["reason"], - channel.result["body"], - ) - - return channel.json_body["sid"] - - def _validate_token(self, link): - # Remove the host - path = link.replace("https://example.com", "") - - # Load the password reset confirmation page - channel = make_request( - self.reactor, - FakeSite(self.submit_token_resource), - "GET", - path, - shorthand=False, - ) - - self.assertEquals(200, channel.code, channel.result) - - # Now POST to the same endpoint, mimicking the same behaviour as clicking the - # password reset confirm button - - # Confirm the password reset - channel = make_request( - self.reactor, - FakeSite(self.submit_token_resource), - "POST", - path, - content=b"", - shorthand=False, - content_is_form=True, - ) - self.assertEquals(200, channel.code, channel.result) - - def _get_link_from_email(self): - assert self.email_attempts, "No emails have been sent" - - raw_msg = self.email_attempts[-1].decode("UTF-8") - mail = Parser().parsestr(raw_msg) - - text = None - for part in mail.walk(): - if part.get_content_type() == "text/plain": - text = part.get_payload(decode=True).decode("UTF-8") - break - - if not text: - self.fail("Could not find text portion of email to parse") - - match = re.search(r"https://example.com\S+", text) - assert match, "Could not find link in email" - - return match.group(0) - - def _reset_password( - self, new_password, session_id, client_secret, expected_code=200 - ): - channel = self.make_request( - "POST", - b"account/password", - { - "new_password": new_password, - "auth": { - "type": LoginType.EMAIL_IDENTITY, - "threepid_creds": { - "client_secret": client_secret, - "sid": session_id, - }, - }, - }, - ) - self.assertEquals(expected_code, channel.code, channel.result) - - -class DeactivateTestCase(unittest.HomeserverTestCase): - - servlets = [ - synapse.rest.admin.register_servlets_for_client_rest_resource, - login.register_servlets, - account.register_servlets, - room.register_servlets, - ] - - def make_homeserver(self, reactor, clock): - self.hs = self.setup_test_homeserver() - return self.hs - - def test_deactivate_account(self): - user_id = self.register_user("kermit", "test") - tok = self.login("kermit", "test") - - self.deactivate(user_id, tok) - - store = self.hs.get_datastore() - - # Check that the user has been marked as deactivated. - self.assertTrue(self.get_success(store.get_user_deactivated_status(user_id))) - - # Check that this access token has been invalidated. - channel = self.make_request("GET", "account/whoami", access_token=tok) - self.assertEqual(channel.code, 401) - - def test_pending_invites(self): - """Tests that deactivating a user rejects every pending invite for them.""" - store = self.hs.get_datastore() - - inviter_id = self.register_user("inviter", "test") - inviter_tok = self.login("inviter", "test") - - invitee_id = self.register_user("invitee", "test") - invitee_tok = self.login("invitee", "test") - - # Make @inviter:test invite @invitee:test in a new room. - room_id = self.helper.create_room_as(inviter_id, tok=inviter_tok) - self.helper.invite( - room=room_id, src=inviter_id, targ=invitee_id, tok=inviter_tok - ) - - # Make sure the invite is here. - pending_invites = self.get_success( - store.get_invited_rooms_for_local_user(invitee_id) - ) - self.assertEqual(len(pending_invites), 1, pending_invites) - self.assertEqual(pending_invites[0].room_id, room_id, pending_invites) - - # Deactivate @invitee:test. - self.deactivate(invitee_id, invitee_tok) - - # Check that the invite isn't there anymore. - pending_invites = self.get_success( - store.get_invited_rooms_for_local_user(invitee_id) - ) - self.assertEqual(len(pending_invites), 0, pending_invites) - - # Check that the membership of @invitee:test in the room is now "leave". - memberships = self.get_success( - store.get_rooms_for_local_user_where_membership_is( - invitee_id, [Membership.LEAVE] - ) - ) - self.assertEqual(len(memberships), 1, memberships) - self.assertEqual(memberships[0].room_id, room_id, memberships) - - def deactivate(self, user_id, tok): - request_data = json.dumps( - { - "auth": { - "type": "m.login.password", - "user": user_id, - "password": "test", - }, - "erase": False, - } - ) - channel = self.make_request( - "POST", "account/deactivate", request_data, access_token=tok - ) - self.assertEqual(channel.code, 200) - - -class WhoamiTestCase(unittest.HomeserverTestCase): - - servlets = [ - synapse.rest.admin.register_servlets_for_client_rest_resource, - login.register_servlets, - account.register_servlets, - register.register_servlets, - ] - - def test_GET_whoami(self): - device_id = "wouldgohere" - user_id = self.register_user("kermit", "test") - tok = self.login("kermit", "test", device_id=device_id) - - whoami = self.whoami(tok) - self.assertEqual(whoami, {"user_id": user_id, "device_id": device_id}) - - def test_GET_whoami_appservices(self): - user_id = "@as:test" - as_token = "i_am_an_app_service" - - appservice = ApplicationService( - as_token, - self.hs.config.server_name, - id="1234", - namespaces={"users": [{"regex": user_id, "exclusive": True}]}, - sender=user_id, - ) - self.hs.get_datastore().services_cache.append(appservice) - - whoami = self.whoami(as_token) - self.assertEqual(whoami, {"user_id": user_id}) - self.assertFalse(hasattr(whoami, "device_id")) - - def whoami(self, tok): - channel = self.make_request("GET", "account/whoami", {}, access_token=tok) - self.assertEqual(channel.code, 200) - return channel.json_body - - -class ThreepidEmailRestTestCase(unittest.HomeserverTestCase): - - servlets = [ - account.register_servlets, - login.register_servlets, - synapse.rest.admin.register_servlets_for_client_rest_resource, - ] - - def make_homeserver(self, reactor, clock): - config = self.default_config() - - # Email config. - config["email"] = { - "enable_notifs": False, - "template_dir": os.path.abspath( - pkg_resources.resource_filename("synapse", "res/templates") - ), - "smtp_host": "127.0.0.1", - "smtp_port": 20, - "require_transport_security": False, - "smtp_user": None, - "smtp_pass": None, - "notif_from": "test@example.com", - } - config["public_baseurl"] = "https://example.com" - - self.hs = self.setup_test_homeserver(config=config) - - async def sendmail( - reactor, smtphost, smtpport, from_addr, to_addrs, msg, **kwargs - ): - self.email_attempts.append(msg) - - self.email_attempts = [] - self.hs.get_send_email_handler()._sendmail = sendmail - - return self.hs - - def prepare(self, reactor, clock, hs): - self.store = hs.get_datastore() - - self.user_id = self.register_user("kermit", "test") - self.user_id_tok = self.login("kermit", "test") - self.email = "test@example.com" - self.url_3pid = b"account/3pid" - - def test_add_valid_email(self): - self.get_success(self._add_email(self.email, self.email)) - - def test_add_valid_email_second_time(self): - self.get_success(self._add_email(self.email, self.email)) - self.get_success( - self._request_token_invalid_email( - self.email, - expected_errcode=Codes.THREEPID_IN_USE, - expected_error="Email is already in use", - ) - ) - - def test_add_valid_email_second_time_canonicalise(self): - self.get_success(self._add_email(self.email, self.email)) - self.get_success( - self._request_token_invalid_email( - "TEST@EXAMPLE.COM", - expected_errcode=Codes.THREEPID_IN_USE, - expected_error="Email is already in use", - ) - ) - - def test_add_email_no_at(self): - self.get_success( - self._request_token_invalid_email( - "address-without-at.bar", - expected_errcode=Codes.UNKNOWN, - expected_error="Unable to parse email address", - ) - ) - - def test_add_email_two_at(self): - self.get_success( - self._request_token_invalid_email( - "foo@foo@test.bar", - expected_errcode=Codes.UNKNOWN, - expected_error="Unable to parse email address", - ) - ) - - def test_add_email_bad_format(self): - self.get_success( - self._request_token_invalid_email( - "user@bad.example.net@good.example.com", - expected_errcode=Codes.UNKNOWN, - expected_error="Unable to parse email address", - ) - ) - - def test_add_email_domain_to_lower(self): - self.get_success(self._add_email("foo@TEST.BAR", "foo@test.bar")) - - def test_add_email_domain_with_umlaut(self): - self.get_success(self._add_email("foo@Öumlaut.com", "foo@öumlaut.com")) - - def test_add_email_address_casefold(self): - self.get_success(self._add_email("Strauß@Example.com", "strauss@example.com")) - - def test_address_trim(self): - self.get_success(self._add_email(" foo@test.bar ", "foo@test.bar")) - - @override_config({"rc_3pid_validation": {"burst_count": 3}}) - def test_ratelimit_by_ip(self): - """Tests that adding emails is ratelimited by IP""" - - # We expect to be able to set three emails before getting ratelimited. - self.get_success(self._add_email("foo1@test.bar", "foo1@test.bar")) - self.get_success(self._add_email("foo2@test.bar", "foo2@test.bar")) - self.get_success(self._add_email("foo3@test.bar", "foo3@test.bar")) - - with self.assertRaises(HttpResponseException) as cm: - self.get_success(self._add_email("foo4@test.bar", "foo4@test.bar")) - - self.assertEqual(cm.exception.code, 429) - - def test_add_email_if_disabled(self): - """Test adding email to profile when doing so is disallowed""" - self.hs.config.enable_3pid_changes = False - - client_secret = "foobar" - session_id = self._request_token(self.email, client_secret) - - self.assertEquals(len(self.email_attempts), 1) - link = self._get_link_from_email() - - self._validate_token(link) - - channel = self.make_request( - "POST", - b"/_matrix/client/unstable/account/3pid/add", - { - "client_secret": client_secret, - "sid": session_id, - "auth": { - "type": "m.login.password", - "user": self.user_id, - "password": "test", - }, - }, - access_token=self.user_id_tok, - ) - self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"]) - - # Get user - channel = self.make_request( - "GET", - self.url_3pid, - access_token=self.user_id_tok, - ) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - self.assertFalse(channel.json_body["threepids"]) - - def test_delete_email(self): - """Test deleting an email from profile""" - # Add a threepid - self.get_success( - self.store.user_add_threepid( - user_id=self.user_id, - medium="email", - address=self.email, - validated_at=0, - added_at=0, - ) - ) - - channel = self.make_request( - "POST", - b"account/3pid/delete", - {"medium": "email", "address": self.email}, - access_token=self.user_id_tok, - ) - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - - # Get user - channel = self.make_request( - "GET", - self.url_3pid, - access_token=self.user_id_tok, - ) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - self.assertFalse(channel.json_body["threepids"]) - - def test_delete_email_if_disabled(self): - """Test deleting an email from profile when disallowed""" - self.hs.config.enable_3pid_changes = False - - # Add a threepid - self.get_success( - self.store.user_add_threepid( - user_id=self.user_id, - medium="email", - address=self.email, - validated_at=0, - added_at=0, - ) - ) - - channel = self.make_request( - "POST", - b"account/3pid/delete", - {"medium": "email", "address": self.email}, - access_token=self.user_id_tok, - ) - - self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"]) - - # Get user - channel = self.make_request( - "GET", - self.url_3pid, - access_token=self.user_id_tok, - ) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual("email", channel.json_body["threepids"][0]["medium"]) - self.assertEqual(self.email, channel.json_body["threepids"][0]["address"]) - - def test_cant_add_email_without_clicking_link(self): - """Test that we do actually need to click the link in the email""" - client_secret = "foobar" - session_id = self._request_token(self.email, client_secret) - - self.assertEquals(len(self.email_attempts), 1) - - # Attempt to add email without clicking the link - channel = self.make_request( - "POST", - b"/_matrix/client/unstable/account/3pid/add", - { - "client_secret": client_secret, - "sid": session_id, - "auth": { - "type": "m.login.password", - "user": self.user_id, - "password": "test", - }, - }, - access_token=self.user_id_tok, - ) - self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual(Codes.THREEPID_AUTH_FAILED, channel.json_body["errcode"]) - - # Get user - channel = self.make_request( - "GET", - self.url_3pid, - access_token=self.user_id_tok, - ) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - self.assertFalse(channel.json_body["threepids"]) - - def test_no_valid_token(self): - """Test that we do actually need to request a token and can't just - make a session up. - """ - client_secret = "foobar" - session_id = "weasle" - - # Attempt to add email without even requesting an email - channel = self.make_request( - "POST", - b"/_matrix/client/unstable/account/3pid/add", - { - "client_secret": client_secret, - "sid": session_id, - "auth": { - "type": "m.login.password", - "user": self.user_id, - "password": "test", - }, - }, - access_token=self.user_id_tok, - ) - self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual(Codes.THREEPID_AUTH_FAILED, channel.json_body["errcode"]) - - # Get user - channel = self.make_request( - "GET", - self.url_3pid, - access_token=self.user_id_tok, - ) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - self.assertFalse(channel.json_body["threepids"]) - - @override_config({"next_link_domain_whitelist": None}) - def test_next_link(self): - """Tests a valid next_link parameter value with no whitelist (good case)""" - self._request_token( - "something@example.com", - "some_secret", - next_link="https://example.com/a/good/site", - expect_code=200, - ) - - @override_config({"next_link_domain_whitelist": None}) - def test_next_link_exotic_protocol(self): - """Tests using a esoteric protocol as a next_link parameter value. - Someone may be hosting a client on IPFS etc. - """ - self._request_token( - "something@example.com", - "some_secret", - next_link="some-protocol://abcdefghijklmopqrstuvwxyz", - expect_code=200, - ) - - @override_config({"next_link_domain_whitelist": None}) - def test_next_link_file_uri(self): - """Tests next_link parameters cannot be file URI""" - # Attempt to use a next_link value that points to the local disk - self._request_token( - "something@example.com", - "some_secret", - next_link="file:///host/path", - expect_code=400, - ) - - @override_config({"next_link_domain_whitelist": ["example.com", "example.org"]}) - def test_next_link_domain_whitelist(self): - """Tests next_link parameters must fit the whitelist if provided""" - - # Ensure not providing a next_link parameter still works - self._request_token( - "something@example.com", - "some_secret", - next_link=None, - expect_code=200, - ) - - self._request_token( - "something@example.com", - "some_secret", - next_link="https://example.com/some/good/page", - expect_code=200, - ) - - self._request_token( - "something@example.com", - "some_secret", - next_link="https://example.org/some/also/good/page", - expect_code=200, - ) - - self._request_token( - "something@example.com", - "some_secret", - next_link="https://bad.example.org/some/bad/page", - expect_code=400, - ) - - @override_config({"next_link_domain_whitelist": []}) - def test_empty_next_link_domain_whitelist(self): - """Tests an empty next_lint_domain_whitelist value, meaning next_link is essentially - disallowed - """ - self._request_token( - "something@example.com", - "some_secret", - next_link="https://example.com/a/page", - expect_code=400, - ) - - def _request_token( - self, - email: str, - client_secret: str, - next_link: Optional[str] = None, - expect_code: int = 200, - ) -> str: - """Request a validation token to add an email address to a user's account - - Args: - email: The email address to validate - client_secret: A secret string - next_link: A link to redirect the user to after validation - expect_code: Expected return code of the call - - Returns: - The ID of the new threepid validation session - """ - body = {"client_secret": client_secret, "email": email, "send_attempt": 1} - if next_link: - body["next_link"] = next_link - - channel = self.make_request( - "POST", - b"account/3pid/email/requestToken", - body, - ) - - if channel.code != expect_code: - raise HttpResponseException( - channel.code, - channel.result["reason"], - channel.result["body"], - ) - - return channel.json_body.get("sid") - - def _request_token_invalid_email( - self, - email, - expected_errcode, - expected_error, - client_secret="foobar", - ): - channel = self.make_request( - "POST", - b"account/3pid/email/requestToken", - {"client_secret": client_secret, "email": email, "send_attempt": 1}, - ) - self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual(expected_errcode, channel.json_body["errcode"]) - self.assertEqual(expected_error, channel.json_body["error"]) - - def _validate_token(self, link): - # Remove the host - path = link.replace("https://example.com", "") - - channel = self.make_request("GET", path, shorthand=False) - self.assertEquals(200, channel.code, channel.result) - - def _get_link_from_email(self): - assert self.email_attempts, "No emails have been sent" - - raw_msg = self.email_attempts[-1].decode("UTF-8") - mail = Parser().parsestr(raw_msg) - - text = None - for part in mail.walk(): - if part.get_content_type() == "text/plain": - text = part.get_payload(decode=True).decode("UTF-8") - break - - if not text: - self.fail("Could not find text portion of email to parse") - - match = re.search(r"https://example.com\S+", text) - assert match, "Could not find link in email" - - return match.group(0) - - def _add_email(self, request_email, expected_email): - """Test adding an email to profile""" - previous_email_attempts = len(self.email_attempts) - - client_secret = "foobar" - session_id = self._request_token(request_email, client_secret) - - self.assertEquals(len(self.email_attempts) - previous_email_attempts, 1) - link = self._get_link_from_email() - - self._validate_token(link) - - channel = self.make_request( - "POST", - b"/_matrix/client/unstable/account/3pid/add", - { - "client_secret": client_secret, - "sid": session_id, - "auth": { - "type": "m.login.password", - "user": self.user_id, - "password": "test", - }, - }, - access_token=self.user_id_tok, - ) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - - # Get user - channel = self.make_request( - "GET", - self.url_3pid, - access_token=self.user_id_tok, - ) - - self.assertEqual(200, int(channel.result["code"]), msg=channel.result["body"]) - self.assertEqual("email", channel.json_body["threepids"][0]["medium"]) - - threepids = {threepid["address"] for threepid in channel.json_body["threepids"]} - self.assertIn(expected_email, threepids) diff --git a/tests/rest/client/v2_alpha/test_auth.py b/tests/rest/client/v2_alpha/test_auth.py deleted file mode 100644 index cf5cfb910c..0000000000 --- a/tests/rest/client/v2_alpha/test_auth.py +++ /dev/null @@ -1,717 +0,0 @@ -# Copyright 2018 New Vector -# Copyright 2020-2021 The Matrix.org Foundation C.I.C -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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. -from typing import Optional, Union - -from twisted.internet.defer import succeed - -import synapse.rest.admin -from synapse.api.constants import LoginType -from synapse.handlers.ui_auth.checkers import UserInteractiveAuthChecker -from synapse.rest.client import account, auth, devices, login, register -from synapse.rest.synapse.client import build_synapse_client_resource_tree -from synapse.types import JsonDict, UserID - -from tests import unittest -from tests.handlers.test_oidc import HAS_OIDC -from tests.rest.client.v1.utils import TEST_OIDC_CONFIG -from tests.server import FakeChannel -from tests.unittest import override_config, skip_unless - - -class DummyRecaptchaChecker(UserInteractiveAuthChecker): - def __init__(self, hs): - super().__init__(hs) - self.recaptcha_attempts = [] - - def check_auth(self, authdict, clientip): - self.recaptcha_attempts.append((authdict, clientip)) - return succeed(True) - - -class FallbackAuthTests(unittest.HomeserverTestCase): - - servlets = [ - auth.register_servlets, - register.register_servlets, - ] - hijack_auth = False - - def make_homeserver(self, reactor, clock): - - config = self.default_config() - - config["enable_registration_captcha"] = True - config["recaptcha_public_key"] = "brokencake" - config["registrations_require_3pid"] = [] - - hs = self.setup_test_homeserver(config=config) - return hs - - def prepare(self, reactor, clock, hs): - self.recaptcha_checker = DummyRecaptchaChecker(hs) - auth_handler = hs.get_auth_handler() - auth_handler.checkers[LoginType.RECAPTCHA] = self.recaptcha_checker - - def register(self, expected_response: int, body: JsonDict) -> FakeChannel: - """Make a register request.""" - channel = self.make_request("POST", "register", body) - - self.assertEqual(channel.code, expected_response) - return channel - - def recaptcha( - self, - session: str, - expected_post_response: int, - post_session: Optional[str] = None, - ) -> None: - """Get and respond to a fallback recaptcha. Returns the second request.""" - if post_session is None: - post_session = session - - channel = self.make_request( - "GET", "auth/m.login.recaptcha/fallback/web?session=" + session - ) - self.assertEqual(channel.code, 200) - - channel = self.make_request( - "POST", - "auth/m.login.recaptcha/fallback/web?session=" - + post_session - + "&g-recaptcha-response=a", - ) - self.assertEqual(channel.code, expected_post_response) - - # The recaptcha handler is called with the response given - attempts = self.recaptcha_checker.recaptcha_attempts - self.assertEqual(len(attempts), 1) - self.assertEqual(attempts[0][0]["response"], "a") - - def test_fallback_captcha(self): - """Ensure that fallback auth via a captcha works.""" - # Returns a 401 as per the spec - channel = self.register( - 401, - {"username": "user", "type": "m.login.password", "password": "bar"}, - ) - - # Grab the session - session = channel.json_body["session"] - # Assert our configured public key is being given - self.assertEqual( - channel.json_body["params"]["m.login.recaptcha"]["public_key"], "brokencake" - ) - - # Complete the recaptcha step. - self.recaptcha(session, 200) - - # also complete the dummy auth - self.register(200, {"auth": {"session": session, "type": "m.login.dummy"}}) - - # Now we should have fulfilled a complete auth flow, including - # the recaptcha fallback step, we can then send a - # request to the register API with the session in the authdict. - channel = self.register(200, {"auth": {"session": session}}) - - # We're given a registered user. - self.assertEqual(channel.json_body["user_id"], "@user:test") - - def test_complete_operation_unknown_session(self): - """ - Attempting to mark an invalid session as complete should error. - """ - # Make the initial request to register. (Later on a different password - # will be used.) - # Returns a 401 as per the spec - channel = self.register( - 401, {"username": "user", "type": "m.login.password", "password": "bar"} - ) - - # Grab the session - session = channel.json_body["session"] - # Assert our configured public key is being given - self.assertEqual( - channel.json_body["params"]["m.login.recaptcha"]["public_key"], "brokencake" - ) - - # Attempt to complete the recaptcha step with an unknown session. - # This results in an error. - self.recaptcha(session, 400, session + "unknown") - - -class UIAuthTests(unittest.HomeserverTestCase): - servlets = [ - auth.register_servlets, - devices.register_servlets, - login.register_servlets, - synapse.rest.admin.register_servlets_for_client_rest_resource, - register.register_servlets, - ] - - def default_config(self): - config = super().default_config() - - # public_baseurl uses an http:// scheme because FakeChannel.isSecure() returns - # False, so synapse will see the requested uri as http://..., so using http in - # the public_baseurl stops Synapse trying to redirect to https. - config["public_baseurl"] = "http://synapse.test" - - if HAS_OIDC: - # we enable OIDC as a way of testing SSO flows - oidc_config = {} - oidc_config.update(TEST_OIDC_CONFIG) - oidc_config["allow_existing_users"] = True - config["oidc_config"] = oidc_config - - return config - - def create_resource_dict(self): - resource_dict = super().create_resource_dict() - resource_dict.update(build_synapse_client_resource_tree(self.hs)) - return resource_dict - - def prepare(self, reactor, clock, hs): - self.user_pass = "pass" - self.user = self.register_user("test", self.user_pass) - self.device_id = "dev1" - self.user_tok = self.login("test", self.user_pass, self.device_id) - - def delete_device( - self, - access_token: str, - device: str, - expected_response: int, - body: Union[bytes, JsonDict] = b"", - ) -> FakeChannel: - """Delete an individual device.""" - channel = self.make_request( - "DELETE", - "devices/" + device, - body, - access_token=access_token, - ) - - # Ensure the response is sane. - self.assertEqual(channel.code, expected_response) - - return channel - - def delete_devices(self, expected_response: int, body: JsonDict) -> FakeChannel: - """Delete 1 or more devices.""" - # Note that this uses the delete_devices endpoint so that we can modify - # the payload half-way through some tests. - channel = self.make_request( - "POST", - "delete_devices", - body, - access_token=self.user_tok, - ) - - # Ensure the response is sane. - self.assertEqual(channel.code, expected_response) - - return channel - - def test_ui_auth(self): - """ - Test user interactive authentication outside of registration. - """ - # Attempt to delete this device. - # Returns a 401 as per the spec - channel = self.delete_device(self.user_tok, self.device_id, 401) - - # Grab the session - session = channel.json_body["session"] - # Ensure that flows are what is expected. - self.assertIn({"stages": ["m.login.password"]}, channel.json_body["flows"]) - - # Make another request providing the UI auth flow. - self.delete_device( - self.user_tok, - self.device_id, - 200, - { - "auth": { - "type": "m.login.password", - "identifier": {"type": "m.id.user", "user": self.user}, - "password": self.user_pass, - "session": session, - }, - }, - ) - - def test_grandfathered_identifier(self): - """Check behaviour without "identifier" dict - - Synapse used to require clients to submit a "user" field for m.login.password - UIA - check that still works. - """ - - channel = self.delete_device(self.user_tok, self.device_id, 401) - session = channel.json_body["session"] - - # Make another request providing the UI auth flow. - self.delete_device( - self.user_tok, - self.device_id, - 200, - { - "auth": { - "type": "m.login.password", - "user": self.user, - "password": self.user_pass, - "session": session, - }, - }, - ) - - def test_can_change_body(self): - """ - The client dict can be modified during the user interactive authentication session. - - Note that it is not spec compliant to modify the client dict during a - user interactive authentication session, but many clients currently do. - - When Synapse is updated to be spec compliant, the call to re-use the - session ID should be rejected. - """ - # Create a second login. - self.login("test", self.user_pass, "dev2") - - # Attempt to delete the first device. - # Returns a 401 as per the spec - channel = self.delete_devices(401, {"devices": [self.device_id]}) - - # Grab the session - session = channel.json_body["session"] - # Ensure that flows are what is expected. - self.assertIn({"stages": ["m.login.password"]}, channel.json_body["flows"]) - - # Make another request providing the UI auth flow, but try to delete the - # second device. - self.delete_devices( - 200, - { - "devices": ["dev2"], - "auth": { - "type": "m.login.password", - "identifier": {"type": "m.id.user", "user": self.user}, - "password": self.user_pass, - "session": session, - }, - }, - ) - - def test_cannot_change_uri(self): - """ - The initial requested URI cannot be modified during the user interactive authentication session. - """ - # Create a second login. - self.login("test", self.user_pass, "dev2") - - # Attempt to delete the first device. - # Returns a 401 as per the spec - channel = self.delete_device(self.user_tok, self.device_id, 401) - - # Grab the session - session = channel.json_body["session"] - # Ensure that flows are what is expected. - self.assertIn({"stages": ["m.login.password"]}, channel.json_body["flows"]) - - # Make another request providing the UI auth flow, but try to delete the - # second device. This results in an error. - # - # This makes use of the fact that the device ID is embedded into the URL. - self.delete_device( - self.user_tok, - "dev2", - 403, - { - "auth": { - "type": "m.login.password", - "identifier": {"type": "m.id.user", "user": self.user}, - "password": self.user_pass, - "session": session, - }, - }, - ) - - @unittest.override_config({"ui_auth": {"session_timeout": "5s"}}) - def test_can_reuse_session(self): - """ - The session can be reused if configured. - - Compare to test_cannot_change_uri. - """ - # Create a second and third login. - self.login("test", self.user_pass, "dev2") - self.login("test", self.user_pass, "dev3") - - # Attempt to delete a device. This works since the user just logged in. - self.delete_device(self.user_tok, "dev2", 200) - - # Move the clock forward past the validation timeout. - self.reactor.advance(6) - - # Deleting another devices throws the user into UI auth. - channel = self.delete_device(self.user_tok, "dev3", 401) - - # Grab the session - session = channel.json_body["session"] - # Ensure that flows are what is expected. - self.assertIn({"stages": ["m.login.password"]}, channel.json_body["flows"]) - - # Make another request providing the UI auth flow. - self.delete_device( - self.user_tok, - "dev3", - 200, - { - "auth": { - "type": "m.login.password", - "identifier": {"type": "m.id.user", "user": self.user}, - "password": self.user_pass, - "session": session, - }, - }, - ) - - # Make another request, but try to delete the first device. This works - # due to re-using the previous session. - # - # Note that *no auth* information is provided, not even a session iD! - self.delete_device(self.user_tok, self.device_id, 200) - - @skip_unless(HAS_OIDC, "requires OIDC") - @override_config({"oidc_config": TEST_OIDC_CONFIG}) - def test_ui_auth_via_sso(self): - """Test a successful UI Auth flow via SSO - - This includes: - * hitting the UIA SSO redirect endpoint - * checking it serves a confirmation page which links to the OIDC provider - * calling back to the synapse oidc callback - * checking that the original operation succeeds - """ - - # log the user in - remote_user_id = UserID.from_string(self.user).localpart - login_resp = self.helper.login_via_oidc(remote_user_id) - self.assertEqual(login_resp["user_id"], self.user) - - # initiate a UI Auth process by attempting to delete the device - channel = self.delete_device(self.user_tok, self.device_id, 401) - - # check that SSO is offered - flows = channel.json_body["flows"] - self.assertIn({"stages": ["m.login.sso"]}, flows) - - # run the UIA-via-SSO flow - session_id = channel.json_body["session"] - channel = self.helper.auth_via_oidc( - {"sub": remote_user_id}, ui_auth_session_id=session_id - ) - - # that should serve a confirmation page - self.assertEqual(channel.code, 200, channel.result) - - # and now the delete request should succeed. - self.delete_device( - self.user_tok, - self.device_id, - 200, - body={"auth": {"session": session_id}}, - ) - - @skip_unless(HAS_OIDC, "requires OIDC") - @override_config({"oidc_config": TEST_OIDC_CONFIG}) - def test_does_not_offer_password_for_sso_user(self): - login_resp = self.helper.login_via_oidc("username") - user_tok = login_resp["access_token"] - device_id = login_resp["device_id"] - - # now call the device deletion API: we should get the option to auth with SSO - # and not password. - channel = self.delete_device(user_tok, device_id, 401) - - flows = channel.json_body["flows"] - self.assertEqual(flows, [{"stages": ["m.login.sso"]}]) - - def test_does_not_offer_sso_for_password_user(self): - channel = self.delete_device(self.user_tok, self.device_id, 401) - - flows = channel.json_body["flows"] - self.assertEqual(flows, [{"stages": ["m.login.password"]}]) - - @skip_unless(HAS_OIDC, "requires OIDC") - @override_config({"oidc_config": TEST_OIDC_CONFIG}) - def test_offers_both_flows_for_upgraded_user(self): - """A user that had a password and then logged in with SSO should get both flows""" - login_resp = self.helper.login_via_oidc(UserID.from_string(self.user).localpart) - self.assertEqual(login_resp["user_id"], self.user) - - channel = self.delete_device(self.user_tok, self.device_id, 401) - - flows = channel.json_body["flows"] - # we have no particular expectations of ordering here - self.assertIn({"stages": ["m.login.password"]}, flows) - self.assertIn({"stages": ["m.login.sso"]}, flows) - self.assertEqual(len(flows), 2) - - @skip_unless(HAS_OIDC, "requires OIDC") - @override_config({"oidc_config": TEST_OIDC_CONFIG}) - def test_ui_auth_fails_for_incorrect_sso_user(self): - """If the user tries to authenticate with the wrong SSO user, they get an error""" - # log the user in - login_resp = self.helper.login_via_oidc(UserID.from_string(self.user).localpart) - self.assertEqual(login_resp["user_id"], self.user) - - # start a UI Auth flow by attempting to delete a device - channel = self.delete_device(self.user_tok, self.device_id, 401) - - flows = channel.json_body["flows"] - self.assertIn({"stages": ["m.login.sso"]}, flows) - session_id = channel.json_body["session"] - - # do the OIDC auth, but auth as the wrong user - channel = self.helper.auth_via_oidc( - {"sub": "wrong_user"}, ui_auth_session_id=session_id - ) - - # that should return a failure message - self.assertSubstring("We were unable to validate", channel.text_body) - - # ... and the delete op should now fail with a 403 - self.delete_device( - self.user_tok, self.device_id, 403, body={"auth": {"session": session_id}} - ) - - -class RefreshAuthTests(unittest.HomeserverTestCase): - servlets = [ - auth.register_servlets, - account.register_servlets, - login.register_servlets, - synapse.rest.admin.register_servlets_for_client_rest_resource, - register.register_servlets, - ] - hijack_auth = False - - def prepare(self, reactor, clock, hs): - self.user_pass = "pass" - self.user = self.register_user("test", self.user_pass) - - def test_login_issue_refresh_token(self): - """ - A login response should include a refresh_token only if asked. - """ - # Test login - body = {"type": "m.login.password", "user": "test", "password": self.user_pass} - - login_without_refresh = self.make_request( - "POST", "/_matrix/client/r0/login", body - ) - self.assertEqual(login_without_refresh.code, 200, login_without_refresh.result) - self.assertNotIn("refresh_token", login_without_refresh.json_body) - - login_with_refresh = self.make_request( - "POST", - "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true", - body, - ) - self.assertEqual(login_with_refresh.code, 200, login_with_refresh.result) - self.assertIn("refresh_token", login_with_refresh.json_body) - self.assertIn("expires_in_ms", login_with_refresh.json_body) - - def test_register_issue_refresh_token(self): - """ - A register response should include a refresh_token only if asked. - """ - register_without_refresh = self.make_request( - "POST", - "/_matrix/client/r0/register", - { - "username": "test2", - "password": self.user_pass, - "auth": {"type": LoginType.DUMMY}, - }, - ) - self.assertEqual( - register_without_refresh.code, 200, register_without_refresh.result - ) - self.assertNotIn("refresh_token", register_without_refresh.json_body) - - register_with_refresh = self.make_request( - "POST", - "/_matrix/client/r0/register?org.matrix.msc2918.refresh_token=true", - { - "username": "test3", - "password": self.user_pass, - "auth": {"type": LoginType.DUMMY}, - }, - ) - self.assertEqual(register_with_refresh.code, 200, register_with_refresh.result) - self.assertIn("refresh_token", register_with_refresh.json_body) - self.assertIn("expires_in_ms", register_with_refresh.json_body) - - def test_token_refresh(self): - """ - A refresh token can be used to issue a new access token. - """ - body = {"type": "m.login.password", "user": "test", "password": self.user_pass} - login_response = self.make_request( - "POST", - "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true", - body, - ) - self.assertEqual(login_response.code, 200, login_response.result) - - refresh_response = self.make_request( - "POST", - "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", - {"refresh_token": login_response.json_body["refresh_token"]}, - ) - self.assertEqual(refresh_response.code, 200, refresh_response.result) - self.assertIn("access_token", refresh_response.json_body) - self.assertIn("refresh_token", refresh_response.json_body) - self.assertIn("expires_in_ms", refresh_response.json_body) - - # The access and refresh tokens should be different from the original ones after refresh - self.assertNotEqual( - login_response.json_body["access_token"], - refresh_response.json_body["access_token"], - ) - self.assertNotEqual( - login_response.json_body["refresh_token"], - refresh_response.json_body["refresh_token"], - ) - - @override_config({"access_token_lifetime": "1m"}) - def test_refresh_token_expiration(self): - """ - The access token should have some time as specified in the config. - """ - body = {"type": "m.login.password", "user": "test", "password": self.user_pass} - login_response = self.make_request( - "POST", - "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true", - body, - ) - self.assertEqual(login_response.code, 200, login_response.result) - self.assertApproximates( - login_response.json_body["expires_in_ms"], 60 * 1000, 100 - ) - - refresh_response = self.make_request( - "POST", - "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", - {"refresh_token": login_response.json_body["refresh_token"]}, - ) - self.assertEqual(refresh_response.code, 200, refresh_response.result) - self.assertApproximates( - refresh_response.json_body["expires_in_ms"], 60 * 1000, 100 - ) - - def test_refresh_token_invalidation(self): - """Refresh tokens are invalidated after first use of the next token. - - A refresh token is considered invalid if: - - it was already used at least once - - and either - - the next access token was used - - the next refresh token was used - - The chain of tokens goes like this: - - login -|-> first_refresh -> third_refresh (fails) - |-> second_refresh -> fifth_refresh - |-> fourth_refresh (fails) - """ - - body = {"type": "m.login.password", "user": "test", "password": self.user_pass} - login_response = self.make_request( - "POST", - "/_matrix/client/r0/login?org.matrix.msc2918.refresh_token=true", - body, - ) - self.assertEqual(login_response.code, 200, login_response.result) - - # This first refresh should work properly - first_refresh_response = self.make_request( - "POST", - "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", - {"refresh_token": login_response.json_body["refresh_token"]}, - ) - self.assertEqual( - first_refresh_response.code, 200, first_refresh_response.result - ) - - # This one as well, since the token in the first one was never used - second_refresh_response = self.make_request( - "POST", - "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", - {"refresh_token": login_response.json_body["refresh_token"]}, - ) - self.assertEqual( - second_refresh_response.code, 200, second_refresh_response.result - ) - - # This one should not, since the token from the first refresh is not valid anymore - third_refresh_response = self.make_request( - "POST", - "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", - {"refresh_token": first_refresh_response.json_body["refresh_token"]}, - ) - self.assertEqual( - third_refresh_response.code, 401, third_refresh_response.result - ) - - # The associated access token should also be invalid - whoami_response = self.make_request( - "GET", - "/_matrix/client/r0/account/whoami", - access_token=first_refresh_response.json_body["access_token"], - ) - self.assertEqual(whoami_response.code, 401, whoami_response.result) - - # But all other tokens should work (they will expire after some time) - for access_token in [ - second_refresh_response.json_body["access_token"], - login_response.json_body["access_token"], - ]: - whoami_response = self.make_request( - "GET", "/_matrix/client/r0/account/whoami", access_token=access_token - ) - self.assertEqual(whoami_response.code, 200, whoami_response.result) - - # Now that the access token from the last valid refresh was used once, refreshing with the N-1 token should fail - fourth_refresh_response = self.make_request( - "POST", - "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", - {"refresh_token": login_response.json_body["refresh_token"]}, - ) - self.assertEqual( - fourth_refresh_response.code, 403, fourth_refresh_response.result - ) - - # But refreshing from the last valid refresh token still works - fifth_refresh_response = self.make_request( - "POST", - "/_matrix/client/unstable/org.matrix.msc2918.refresh_token/refresh", - {"refresh_token": second_refresh_response.json_body["refresh_token"]}, - ) - self.assertEqual( - fifth_refresh_response.code, 200, fifth_refresh_response.result - ) diff --git a/tests/rest/client/v2_alpha/test_capabilities.py b/tests/rest/client/v2_alpha/test_capabilities.py deleted file mode 100644 index ac31e5ceaf..0000000000 --- a/tests/rest/client/v2_alpha/test_capabilities.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright 2019 New Vector Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 synapse.rest.admin -from synapse.api.room_versions import KNOWN_ROOM_VERSIONS -from synapse.rest.client import capabilities, login - -from tests import unittest -from tests.unittest import override_config - - -class CapabilitiesTestCase(unittest.HomeserverTestCase): - - servlets = [ - synapse.rest.admin.register_servlets_for_client_rest_resource, - capabilities.register_servlets, - login.register_servlets, - ] - - def make_homeserver(self, reactor, clock): - self.url = b"/_matrix/client/r0/capabilities" - hs = self.setup_test_homeserver() - self.config = hs.config - self.auth_handler = hs.get_auth_handler() - return hs - - def prepare(self, reactor, clock, hs): - self.localpart = "user" - self.password = "pass" - self.user = self.register_user(self.localpart, self.password) - - def test_check_auth_required(self): - channel = self.make_request("GET", self.url) - - self.assertEqual(channel.code, 401) - - def test_get_room_version_capabilities(self): - access_token = self.login(self.localpart, self.password) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - for room_version in capabilities["m.room_versions"]["available"].keys(): - self.assertTrue(room_version in KNOWN_ROOM_VERSIONS, "" + room_version) - - self.assertEqual( - self.config.default_room_version.identifier, - capabilities["m.room_versions"]["default"], - ) - - def test_get_change_password_capabilities_password_login(self): - access_token = self.login(self.localpart, self.password) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertTrue(capabilities["m.change_password"]["enabled"]) - - @override_config({"password_config": {"localdb_enabled": False}}) - def test_get_change_password_capabilities_localdb_disabled(self): - access_token = self.get_success( - self.auth_handler.get_access_token_for_user_id( - self.user, device_id=None, valid_until_ms=None - ) - ) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertFalse(capabilities["m.change_password"]["enabled"]) - - @override_config({"password_config": {"enabled": False}}) - def test_get_change_password_capabilities_password_disabled(self): - access_token = self.get_success( - self.auth_handler.get_access_token_for_user_id( - self.user, device_id=None, valid_until_ms=None - ) - ) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertFalse(capabilities["m.change_password"]["enabled"]) - - def test_get_change_users_attributes_capabilities_when_msc3283_disabled(self): - """Test that per default msc3283 is disabled server returns `m.change_password`.""" - access_token = self.login(self.localpart, self.password) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertTrue(capabilities["m.change_password"]["enabled"]) - self.assertNotIn("org.matrix.msc3283.set_displayname", capabilities) - self.assertNotIn("org.matrix.msc3283.set_avatar_url", capabilities) - self.assertNotIn("org.matrix.msc3283.3pid_changes", capabilities) - - @override_config({"experimental_features": {"msc3283_enabled": True}}) - def test_get_change_users_attributes_capabilities_when_msc3283_enabled(self): - """Test if msc3283 is enabled server returns capabilities.""" - access_token = self.login(self.localpart, self.password) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertTrue(capabilities["m.change_password"]["enabled"]) - self.assertTrue(capabilities["org.matrix.msc3283.set_displayname"]["enabled"]) - self.assertTrue(capabilities["org.matrix.msc3283.set_avatar_url"]["enabled"]) - self.assertTrue(capabilities["org.matrix.msc3283.3pid_changes"]["enabled"]) - - @override_config( - { - "enable_set_displayname": False, - "experimental_features": {"msc3283_enabled": True}, - } - ) - def test_get_set_displayname_capabilities_displayname_disabled(self): - """Test if set displayname is disabled that the server responds it.""" - access_token = self.login(self.localpart, self.password) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertFalse(capabilities["org.matrix.msc3283.set_displayname"]["enabled"]) - - @override_config( - { - "enable_set_avatar_url": False, - "experimental_features": {"msc3283_enabled": True}, - } - ) - def test_get_set_avatar_url_capabilities_avatar_url_disabled(self): - """Test if set avatar_url is disabled that the server responds it.""" - access_token = self.login(self.localpart, self.password) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertFalse(capabilities["org.matrix.msc3283.set_avatar_url"]["enabled"]) - - @override_config( - { - "enable_3pid_changes": False, - "experimental_features": {"msc3283_enabled": True}, - } - ) - def test_change_3pid_capabilities_3pid_disabled(self): - """Test if change 3pid is disabled that the server responds it.""" - access_token = self.login(self.localpart, self.password) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertFalse(capabilities["org.matrix.msc3283.3pid_changes"]["enabled"]) - - def test_get_does_not_include_msc3244_fields_by_default(self): - access_token = self.get_success( - self.auth_handler.get_access_token_for_user_id( - self.user, device_id=None, valid_until_ms=None - ) - ) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - self.assertNotIn( - "org.matrix.msc3244.room_capabilities", capabilities["m.room_versions"] - ) - - @override_config({"experimental_features": {"msc3244_enabled": True}}) - def test_get_does_include_msc3244_fields_when_enabled(self): - access_token = self.get_success( - self.auth_handler.get_access_token_for_user_id( - self.user, device_id=None, valid_until_ms=None - ) - ) - - channel = self.make_request("GET", self.url, access_token=access_token) - capabilities = channel.json_body["capabilities"] - - self.assertEqual(channel.code, 200) - for details in capabilities["m.room_versions"][ - "org.matrix.msc3244.room_capabilities" - ].values(): - if details["preferred"] is not None: - self.assertTrue( - details["preferred"] in KNOWN_ROOM_VERSIONS, - str(details["preferred"]), - ) - - self.assertGreater(len(details["support"]), 0) - for room_version in details["support"]: - self.assertTrue(room_version in KNOWN_ROOM_VERSIONS, str(room_version)) diff --git a/tests/rest/client/v2_alpha/test_filter.py b/tests/rest/client/v2_alpha/test_filter.py deleted file mode 100644 index 475c6bed3d..0000000000 --- a/tests/rest/client/v2_alpha/test_filter.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright 2015, 2016 OpenMarket Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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. - -from twisted.internet import defer - -from synapse.api.errors import Codes -from synapse.rest.client import filter - -from tests import unittest - -PATH_PREFIX = "/_matrix/client/v2_alpha" - - -class FilterTestCase(unittest.HomeserverTestCase): - - user_id = "@apple:test" - hijack_auth = True - EXAMPLE_FILTER = {"room": {"timeline": {"types": ["m.room.message"]}}} - EXAMPLE_FILTER_JSON = b'{"room": {"timeline": {"types": ["m.room.message"]}}}' - servlets = [filter.register_servlets] - - def prepare(self, reactor, clock, hs): - self.filtering = hs.get_filtering() - self.store = hs.get_datastore() - - def test_add_filter(self): - channel = self.make_request( - "POST", - "/_matrix/client/r0/user/%s/filter" % (self.user_id), - self.EXAMPLE_FILTER_JSON, - ) - - self.assertEqual(channel.result["code"], b"200") - self.assertEqual(channel.json_body, {"filter_id": "0"}) - filter = self.store.get_user_filter(user_localpart="apple", filter_id=0) - self.pump() - self.assertEquals(filter.result, self.EXAMPLE_FILTER) - - def test_add_filter_for_other_user(self): - channel = self.make_request( - "POST", - "/_matrix/client/r0/user/%s/filter" % ("@watermelon:test"), - self.EXAMPLE_FILTER_JSON, - ) - - self.assertEqual(channel.result["code"], b"403") - self.assertEquals(channel.json_body["errcode"], Codes.FORBIDDEN) - - def test_add_filter_non_local_user(self): - _is_mine = self.hs.is_mine - self.hs.is_mine = lambda target_user: False - channel = self.make_request( - "POST", - "/_matrix/client/r0/user/%s/filter" % (self.user_id), - self.EXAMPLE_FILTER_JSON, - ) - - self.hs.is_mine = _is_mine - self.assertEqual(channel.result["code"], b"403") - self.assertEquals(channel.json_body["errcode"], Codes.FORBIDDEN) - - def test_get_filter(self): - filter_id = defer.ensureDeferred( - self.filtering.add_user_filter( - user_localpart="apple", user_filter=self.EXAMPLE_FILTER - ) - ) - self.reactor.advance(1) - filter_id = filter_id.result - channel = self.make_request( - "GET", "/_matrix/client/r0/user/%s/filter/%s" % (self.user_id, filter_id) - ) - - self.assertEqual(channel.result["code"], b"200") - self.assertEquals(channel.json_body, self.EXAMPLE_FILTER) - - def test_get_filter_non_existant(self): - channel = self.make_request( - "GET", "/_matrix/client/r0/user/%s/filter/12382148321" % (self.user_id) - ) - - self.assertEqual(channel.result["code"], b"404") - self.assertEquals(channel.json_body["errcode"], Codes.NOT_FOUND) - - # Currently invalid params do not have an appropriate errcode - # in errors.py - def test_get_filter_invalid_id(self): - channel = self.make_request( - "GET", "/_matrix/client/r0/user/%s/filter/foobar" % (self.user_id) - ) - - self.assertEqual(channel.result["code"], b"400") - - # No ID also returns an invalid_id error - def test_get_filter_no_id(self): - channel = self.make_request( - "GET", "/_matrix/client/r0/user/%s/filter/" % (self.user_id) - ) - - self.assertEqual(channel.result["code"], b"400") diff --git a/tests/rest/client/v2_alpha/test_keys.py b/tests/rest/client/v2_alpha/test_keys.py deleted file mode 100644 index d7fa635eae..0000000000 --- a/tests/rest/client/v2_alpha/test_keys.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright 2021 The Matrix.org Foundation C.I.C. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 - -from http import HTTPStatus - -from synapse.api.errors import Codes -from synapse.rest import admin -from synapse.rest.client import keys, login - -from tests import unittest - - -class KeyQueryTestCase(unittest.HomeserverTestCase): - servlets = [ - keys.register_servlets, - admin.register_servlets_for_client_rest_resource, - login.register_servlets, - ] - - def test_rejects_device_id_ice_key_outside_of_list(self): - self.register_user("alice", "wonderland") - alice_token = self.login("alice", "wonderland") - bob = self.register_user("bob", "uncle") - channel = self.make_request( - "POST", - "/_matrix/client/r0/keys/query", - { - "device_keys": { - bob: "device_id1", - }, - }, - alice_token, - ) - self.assertEqual(channel.code, HTTPStatus.BAD_REQUEST, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.BAD_JSON, - channel.result, - ) - - def test_rejects_device_key_given_as_map_to_bool(self): - self.register_user("alice", "wonderland") - alice_token = self.login("alice", "wonderland") - bob = self.register_user("bob", "uncle") - channel = self.make_request( - "POST", - "/_matrix/client/r0/keys/query", - { - "device_keys": { - bob: { - "device_id1": True, - }, - }, - }, - alice_token, - ) - - self.assertEqual(channel.code, HTTPStatus.BAD_REQUEST, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.BAD_JSON, - channel.result, - ) - - def test_requires_device_key(self): - """`device_keys` is required. We should complain if it's missing.""" - self.register_user("alice", "wonderland") - alice_token = self.login("alice", "wonderland") - channel = self.make_request( - "POST", - "/_matrix/client/r0/keys/query", - {}, - alice_token, - ) - self.assertEqual(channel.code, HTTPStatus.BAD_REQUEST, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.BAD_JSON, - channel.result, - ) diff --git a/tests/rest/client/v2_alpha/test_password_policy.py b/tests/rest/client/v2_alpha/test_password_policy.py deleted file mode 100644 index 3cf5871899..0000000000 --- a/tests/rest/client/v2_alpha/test_password_policy.py +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright 2019 The Matrix.org Foundation C.I.C. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 synapse.api.constants import LoginType -from synapse.api.errors import Codes -from synapse.rest import admin -from synapse.rest.client import account, login, password_policy, register - -from tests import unittest - - -class PasswordPolicyTestCase(unittest.HomeserverTestCase): - """Tests the password policy feature and its compliance with MSC2000. - - When validating a password, Synapse does the necessary checks in this order: - - 1. Password is long enough - 2. Password contains digit(s) - 3. Password contains symbol(s) - 4. Password contains uppercase letter(s) - 5. Password contains lowercase letter(s) - - For each test below that checks whether a password triggers the right error code, - that test provides a password good enough to pass the previous tests, but not the - one it is currently testing (nor any test that comes afterward). - """ - - servlets = [ - admin.register_servlets_for_client_rest_resource, - login.register_servlets, - register.register_servlets, - password_policy.register_servlets, - account.register_servlets, - ] - - def make_homeserver(self, reactor, clock): - self.register_url = "/_matrix/client/r0/register" - self.policy = { - "enabled": True, - "minimum_length": 10, - "require_digit": True, - "require_symbol": True, - "require_lowercase": True, - "require_uppercase": True, - } - - config = self.default_config() - config["password_config"] = { - "policy": self.policy, - } - - hs = self.setup_test_homeserver(config=config) - return hs - - def test_get_policy(self): - """Tests if the /password_policy endpoint returns the configured policy.""" - - channel = self.make_request("GET", "/_matrix/client/r0/password_policy") - - self.assertEqual(channel.code, 200, channel.result) - self.assertEqual( - channel.json_body, - { - "m.minimum_length": 10, - "m.require_digit": True, - "m.require_symbol": True, - "m.require_lowercase": True, - "m.require_uppercase": True, - }, - channel.result, - ) - - def test_password_too_short(self): - request_data = json.dumps({"username": "kermit", "password": "shorty"}) - channel = self.make_request("POST", self.register_url, request_data) - - self.assertEqual(channel.code, 400, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.PASSWORD_TOO_SHORT, - channel.result, - ) - - def test_password_no_digit(self): - request_data = json.dumps({"username": "kermit", "password": "longerpassword"}) - channel = self.make_request("POST", self.register_url, request_data) - - self.assertEqual(channel.code, 400, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.PASSWORD_NO_DIGIT, - channel.result, - ) - - def test_password_no_symbol(self): - request_data = json.dumps({"username": "kermit", "password": "l0ngerpassword"}) - channel = self.make_request("POST", self.register_url, request_data) - - self.assertEqual(channel.code, 400, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.PASSWORD_NO_SYMBOL, - channel.result, - ) - - def test_password_no_uppercase(self): - request_data = json.dumps({"username": "kermit", "password": "l0ngerpassword!"}) - channel = self.make_request("POST", self.register_url, request_data) - - self.assertEqual(channel.code, 400, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.PASSWORD_NO_UPPERCASE, - channel.result, - ) - - def test_password_no_lowercase(self): - request_data = json.dumps({"username": "kermit", "password": "L0NGERPASSWORD!"}) - channel = self.make_request("POST", self.register_url, request_data) - - self.assertEqual(channel.code, 400, channel.result) - self.assertEqual( - channel.json_body["errcode"], - Codes.PASSWORD_NO_LOWERCASE, - channel.result, - ) - - def test_password_compliant(self): - request_data = json.dumps({"username": "kermit", "password": "L0ngerpassword!"}) - channel = self.make_request("POST", self.register_url, request_data) - - # Getting a 401 here means the password has passed validation and the server has - # responded with a list of registration flows. - self.assertEqual(channel.code, 401, channel.result) - - def test_password_change(self): - """This doesn't test every possible use case, only that hitting /account/password - triggers the password validation code. - """ - compliant_password = "C0mpl!antpassword" - not_compliant_password = "notcompliantpassword" - - user_id = self.register_user("kermit", compliant_password) - tok = self.login("kermit", compliant_password) - - request_data = json.dumps( - { - "new_password": not_compliant_password, - "auth": { - "password": compliant_password, - "type": LoginType.PASSWORD, - "user": user_id, - }, - } - ) - channel = self.make_request( - "POST", - "/_matrix/client/r0/account/password", - request_data, - access_token=tok, - ) - - self.assertEqual(channel.code, 400, channel.result) - self.assertEqual(channel.json_body["errcode"], Codes.PASSWORD_NO_DIGIT) diff --git a/tests/rest/client/v2_alpha/test_register.py b/tests/rest/client/v2_alpha/test_register.py deleted file mode 100644 index fecda037a5..0000000000 --- a/tests/rest/client/v2_alpha/test_register.py +++ /dev/null @@ -1,746 +0,0 @@ -# Copyright 2014-2016 OpenMarket Ltd -# Copyright 2017-2018 New Vector Ltd -# Copyright 2019 The Matrix.org Foundation C.I.C. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 datetime -import json -import os - -import pkg_resources - -import synapse.rest.admin -from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType -from synapse.api.errors import Codes -from synapse.appservice import ApplicationService -from synapse.rest.client import account, account_validity, login, logout, register, sync - -from tests import unittest -from tests.unittest import override_config - - -class RegisterRestServletTestCase(unittest.HomeserverTestCase): - - servlets = [ - login.register_servlets, - register.register_servlets, - synapse.rest.admin.register_servlets, - ] - url = b"/_matrix/client/r0/register" - - def default_config(self): - config = super().default_config() - config["allow_guest_access"] = True - return config - - def test_POST_appservice_registration_valid(self): - user_id = "@as_user_kermit:test" - as_token = "i_am_an_app_service" - - appservice = ApplicationService( - as_token, - self.hs.config.server_name, - id="1234", - namespaces={"users": [{"regex": r"@as_user.*", "exclusive": True}]}, - sender="@as:test", - ) - - self.hs.get_datastore().services_cache.append(appservice) - request_data = json.dumps( - {"username": "as_user_kermit", "type": APP_SERVICE_REGISTRATION_TYPE} - ) - - channel = self.make_request( - b"POST", self.url + b"?access_token=i_am_an_app_service", request_data - ) - - self.assertEquals(channel.result["code"], b"200", channel.result) - det_data = {"user_id": user_id, "home_server": self.hs.hostname} - self.assertDictContainsSubset(det_data, channel.json_body) - - def test_POST_appservice_registration_no_type(self): - as_token = "i_am_an_app_service" - - appservice = ApplicationService( - as_token, - self.hs.config.server_name, - id="1234", - namespaces={"users": [{"regex": r"@as_user.*", "exclusive": True}]}, - sender="@as:test", - ) - - self.hs.get_datastore().services_cache.append(appservice) - request_data = json.dumps({"username": "as_user_kermit"}) - - channel = self.make_request( - b"POST", self.url + b"?access_token=i_am_an_app_service", request_data - ) - - self.assertEquals(channel.result["code"], b"400", channel.result) - - def test_POST_appservice_registration_invalid(self): - self.appservice = None # no application service exists - request_data = json.dumps( - {"username": "kermit", "type": APP_SERVICE_REGISTRATION_TYPE} - ) - channel = self.make_request( - b"POST", self.url + b"?access_token=i_am_an_app_service", request_data - ) - - self.assertEquals(channel.result["code"], b"401", channel.result) - - def test_POST_bad_password(self): - request_data = json.dumps({"username": "kermit", "password": 666}) - channel = self.make_request(b"POST", self.url, request_data) - - self.assertEquals(channel.result["code"], b"400", channel.result) - self.assertEquals(channel.json_body["error"], "Invalid password") - - def test_POST_bad_username(self): - request_data = json.dumps({"username": 777, "password": "monkey"}) - channel = self.make_request(b"POST", self.url, request_data) - - self.assertEquals(channel.result["code"], b"400", channel.result) - self.assertEquals(channel.json_body["error"], "Invalid username") - - def test_POST_user_valid(self): - user_id = "@kermit:test" - device_id = "frogfone" - params = { - "username": "kermit", - "password": "monkey", - "device_id": device_id, - "auth": {"type": LoginType.DUMMY}, - } - request_data = json.dumps(params) - channel = self.make_request(b"POST", self.url, request_data) - - det_data = { - "user_id": user_id, - "home_server": self.hs.hostname, - "device_id": device_id, - } - self.assertEquals(channel.result["code"], b"200", channel.result) - self.assertDictContainsSubset(det_data, channel.json_body) - - @override_config({"enable_registration": False}) - def test_POST_disabled_registration(self): - request_data = json.dumps({"username": "kermit", "password": "monkey"}) - self.auth_result = (None, {"username": "kermit", "password": "monkey"}, None) - - channel = self.make_request(b"POST", self.url, request_data) - - self.assertEquals(channel.result["code"], b"403", channel.result) - self.assertEquals(channel.json_body["error"], "Registration has been disabled") - self.assertEquals(channel.json_body["errcode"], "M_FORBIDDEN") - - def test_POST_guest_registration(self): - self.hs.config.macaroon_secret_key = "test" - self.hs.config.allow_guest_access = True - - channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}") - - det_data = {"home_server": self.hs.hostname, "device_id": "guest_device"} - self.assertEquals(channel.result["code"], b"200", channel.result) - self.assertDictContainsSubset(det_data, channel.json_body) - - def test_POST_disabled_guest_registration(self): - self.hs.config.allow_guest_access = False - - channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}") - - self.assertEquals(channel.result["code"], b"403", channel.result) - self.assertEquals(channel.json_body["error"], "Guest access is disabled") - - @override_config({"rc_registration": {"per_second": 0.17, "burst_count": 5}}) - def test_POST_ratelimiting_guest(self): - for i in range(0, 6): - url = self.url + b"?kind=guest" - channel = self.make_request(b"POST", url, b"{}") - - if i == 5: - self.assertEquals(channel.result["code"], b"429", channel.result) - retry_after_ms = int(channel.json_body["retry_after_ms"]) - else: - self.assertEquals(channel.result["code"], b"200", channel.result) - - self.reactor.advance(retry_after_ms / 1000.0 + 1.0) - - channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}") - - self.assertEquals(channel.result["code"], b"200", channel.result) - - @override_config({"rc_registration": {"per_second": 0.17, "burst_count": 5}}) - def test_POST_ratelimiting(self): - for i in range(0, 6): - params = { - "username": "kermit" + str(i), - "password": "monkey", - "device_id": "frogfone", - "auth": {"type": LoginType.DUMMY}, - } - request_data = json.dumps(params) - channel = self.make_request(b"POST", self.url, request_data) - - if i == 5: - self.assertEquals(channel.result["code"], b"429", channel.result) - retry_after_ms = int(channel.json_body["retry_after_ms"]) - else: - self.assertEquals(channel.result["code"], b"200", channel.result) - - self.reactor.advance(retry_after_ms / 1000.0 + 1.0) - - channel = self.make_request(b"POST", self.url + b"?kind=guest", b"{}") - - self.assertEquals(channel.result["code"], b"200", channel.result) - - def test_advertised_flows(self): - channel = self.make_request(b"POST", self.url, b"{}") - self.assertEquals(channel.result["code"], b"401", channel.result) - flows = channel.json_body["flows"] - - # with the stock config, we only expect the dummy flow - self.assertCountEqual([["m.login.dummy"]], (f["stages"] for f in flows)) - - @unittest.override_config( - { - "public_baseurl": "https://test_server", - "enable_registration_captcha": True, - "user_consent": { - "version": "1", - "template_dir": "/", - "require_at_registration": True, - }, - "account_threepid_delegates": { - "email": "https://id_server", - "msisdn": "https://id_server", - }, - } - ) - def test_advertised_flows_captcha_and_terms_and_3pids(self): - channel = self.make_request(b"POST", self.url, b"{}") - self.assertEquals(channel.result["code"], b"401", channel.result) - flows = channel.json_body["flows"] - - self.assertCountEqual( - [ - ["m.login.recaptcha", "m.login.terms", "m.login.dummy"], - ["m.login.recaptcha", "m.login.terms", "m.login.email.identity"], - ["m.login.recaptcha", "m.login.terms", "m.login.msisdn"], - [ - "m.login.recaptcha", - "m.login.terms", - "m.login.msisdn", - "m.login.email.identity", - ], - ], - (f["stages"] for f in flows), - ) - - @unittest.override_config( - { - "public_baseurl": "https://test_server", - "registrations_require_3pid": ["email"], - "disable_msisdn_registration": True, - "email": { - "smtp_host": "mail_server", - "smtp_port": 2525, - "notif_from": "sender@host", - }, - } - ) - def test_advertised_flows_no_msisdn_email_required(self): - channel = self.make_request(b"POST", self.url, b"{}") - self.assertEquals(channel.result["code"], b"401", channel.result) - flows = channel.json_body["flows"] - - # with the stock config, we expect all four combinations of 3pid - self.assertCountEqual( - [["m.login.email.identity"]], (f["stages"] for f in flows) - ) - - @unittest.override_config( - { - "request_token_inhibit_3pid_errors": True, - "public_baseurl": "https://test_server", - "email": { - "smtp_host": "mail_server", - "smtp_port": 2525, - "notif_from": "sender@host", - }, - } - ) - def test_request_token_existing_email_inhibit_error(self): - """Test that requesting a token via this endpoint doesn't leak existing - associations if configured that way. - """ - user_id = self.register_user("kermit", "monkey") - self.login("kermit", "monkey") - - email = "test@example.com" - - # Add a threepid - self.get_success( - self.hs.get_datastore().user_add_threepid( - user_id=user_id, - medium="email", - address=email, - validated_at=0, - added_at=0, - ) - ) - - channel = self.make_request( - "POST", - b"register/email/requestToken", - {"client_secret": "foobar", "email": email, "send_attempt": 1}, - ) - self.assertEquals(200, channel.code, channel.result) - - self.assertIsNotNone(channel.json_body.get("sid")) - - @unittest.override_config( - { - "public_baseurl": "https://test_server", - "email": { - "smtp_host": "mail_server", - "smtp_port": 2525, - "notif_from": "sender@host", - }, - } - ) - def test_reject_invalid_email(self): - """Check that bad emails are rejected""" - - # Test for email with multiple @ - channel = self.make_request( - "POST", - b"register/email/requestToken", - {"client_secret": "foobar", "email": "email@@email", "send_attempt": 1}, - ) - self.assertEquals(400, channel.code, channel.result) - # Check error to ensure that we're not erroring due to a bug in the test. - self.assertEquals( - channel.json_body, - {"errcode": "M_UNKNOWN", "error": "Unable to parse email address"}, - ) - - # Test for email with no @ - channel = self.make_request( - "POST", - b"register/email/requestToken", - {"client_secret": "foobar", "email": "email", "send_attempt": 1}, - ) - self.assertEquals(400, channel.code, channel.result) - self.assertEquals( - channel.json_body, - {"errcode": "M_UNKNOWN", "error": "Unable to parse email address"}, - ) - - # Test for super long email - email = "a@" + "a" * 1000 - channel = self.make_request( - "POST", - b"register/email/requestToken", - {"client_secret": "foobar", "email": email, "send_attempt": 1}, - ) - self.assertEquals(400, channel.code, channel.result) - self.assertEquals( - channel.json_body, - {"errcode": "M_UNKNOWN", "error": "Unable to parse email address"}, - ) - - -class AccountValidityTestCase(unittest.HomeserverTestCase): - - servlets = [ - register.register_servlets, - synapse.rest.admin.register_servlets_for_client_rest_resource, - login.register_servlets, - sync.register_servlets, - logout.register_servlets, - account_validity.register_servlets, - ] - - def make_homeserver(self, reactor, clock): - config = self.default_config() - # Test for account expiring after a week. - config["enable_registration"] = True - config["account_validity"] = { - "enabled": True, - "period": 604800000, # Time in ms for 1 week - } - self.hs = self.setup_test_homeserver(config=config) - - return self.hs - - def test_validity_period(self): - self.register_user("kermit", "monkey") - tok = self.login("kermit", "monkey") - - # The specific endpoint doesn't matter, all we need is an authenticated - # endpoint. - channel = self.make_request(b"GET", "/sync", access_token=tok) - - self.assertEquals(channel.result["code"], b"200", channel.result) - - self.reactor.advance(datetime.timedelta(weeks=1).total_seconds()) - - channel = self.make_request(b"GET", "/sync", access_token=tok) - - self.assertEquals(channel.result["code"], b"403", channel.result) - self.assertEquals( - channel.json_body["errcode"], Codes.EXPIRED_ACCOUNT, channel.result - ) - - def test_manual_renewal(self): - user_id = self.register_user("kermit", "monkey") - tok = self.login("kermit", "monkey") - - self.reactor.advance(datetime.timedelta(weeks=1).total_seconds()) - - # If we register the admin user at the beginning of the test, it will - # expire at the same time as the normal user and the renewal request - # will be denied. - self.register_user("admin", "adminpassword", admin=True) - admin_tok = self.login("admin", "adminpassword") - - url = "/_synapse/admin/v1/account_validity/validity" - params = {"user_id": user_id} - request_data = json.dumps(params) - channel = self.make_request(b"POST", url, request_data, access_token=admin_tok) - self.assertEquals(channel.result["code"], b"200", channel.result) - - # The specific endpoint doesn't matter, all we need is an authenticated - # endpoint. - channel = self.make_request(b"GET", "/sync", access_token=tok) - self.assertEquals(channel.result["code"], b"200", channel.result) - - def test_manual_expire(self): - user_id = self.register_user("kermit", "monkey") - tok = self.login("kermit", "monkey") - - self.register_user("admin", "adminpassword", admin=True) - admin_tok = self.login("admin", "adminpassword") - - url = "/_synapse/admin/v1/account_validity/validity" - params = { - "user_id": user_id, - "expiration_ts": 0, - "enable_renewal_emails": False, - } - request_data = json.dumps(params) - channel = self.make_request(b"POST", url, request_data, access_token=admin_tok) - self.assertEquals(channel.result["code"], b"200", channel.result) - - # The specific endpoint doesn't matter, all we need is an authenticated - # endpoint. - channel = self.make_request(b"GET", "/sync", access_token=tok) - self.assertEquals(channel.result["code"], b"403", channel.result) - self.assertEquals( - channel.json_body["errcode"], Codes.EXPIRED_ACCOUNT, channel.result - ) - - def test_logging_out_expired_user(self): - user_id = self.register_user("kermit", "monkey") - tok = self.login("kermit", "monkey") - - self.register_user("admin", "adminpassword", admin=True) - admin_tok = self.login("admin", "adminpassword") - - url = "/_synapse/admin/v1/account_validity/validity" - params = { - "user_id": user_id, - "expiration_ts": 0, - "enable_renewal_emails": False, - } - request_data = json.dumps(params) - channel = self.make_request(b"POST", url, request_data, access_token=admin_tok) - self.assertEquals(channel.result["code"], b"200", channel.result) - - # Try to log the user out - channel = self.make_request(b"POST", "/logout", access_token=tok) - self.assertEquals(channel.result["code"], b"200", channel.result) - - # Log the user in again (allowed for expired accounts) - tok = self.login("kermit", "monkey") - - # Try to log out all of the user's sessions - channel = self.make_request(b"POST", "/logout/all", access_token=tok) - self.assertEquals(channel.result["code"], b"200", channel.result) - - -class AccountValidityRenewalByEmailTestCase(unittest.HomeserverTestCase): - - servlets = [ - register.register_servlets, - synapse.rest.admin.register_servlets_for_client_rest_resource, - login.register_servlets, - sync.register_servlets, - account_validity.register_servlets, - account.register_servlets, - ] - - def make_homeserver(self, reactor, clock): - config = self.default_config() - - # Test for account expiring after a week and renewal emails being sent 2 - # days before expiry. - config["enable_registration"] = True - config["account_validity"] = { - "enabled": True, - "period": 604800000, # Time in ms for 1 week - "renew_at": 172800000, # Time in ms for 2 days - "renew_by_email_enabled": True, - "renew_email_subject": "Renew your account", - "account_renewed_html_path": "account_renewed.html", - "invalid_token_html_path": "invalid_token.html", - } - - # Email config. - - config["email"] = { - "enable_notifs": True, - "template_dir": os.path.abspath( - pkg_resources.resource_filename("synapse", "res/templates") - ), - "expiry_template_html": "notice_expiry.html", - "expiry_template_text": "notice_expiry.txt", - "notif_template_html": "notif_mail.html", - "notif_template_text": "notif_mail.txt", - "smtp_host": "127.0.0.1", - "smtp_port": 20, - "require_transport_security": False, - "smtp_user": None, - "smtp_pass": None, - "notif_from": "test@example.com", - } - config["public_baseurl"] = "aaa" - - self.hs = self.setup_test_homeserver(config=config) - - async def sendmail(*args, **kwargs): - self.email_attempts.append((args, kwargs)) - - self.email_attempts = [] - self.hs.get_send_email_handler()._sendmail = sendmail - - self.store = self.hs.get_datastore() - - return self.hs - - def test_renewal_email(self): - self.email_attempts = [] - - (user_id, tok) = self.create_user() - - # Move 5 days forward. This should trigger a renewal email to be sent. - self.reactor.advance(datetime.timedelta(days=5).total_seconds()) - self.assertEqual(len(self.email_attempts), 1) - - # Retrieving the URL from the email is too much pain for now, so we - # retrieve the token from the DB. - renewal_token = self.get_success(self.store.get_renewal_token_for_user(user_id)) - url = "/_matrix/client/unstable/account_validity/renew?token=%s" % renewal_token - channel = self.make_request(b"GET", url) - self.assertEquals(channel.result["code"], b"200", channel.result) - - # Check that we're getting HTML back. - content_type = channel.headers.getRawHeaders(b"Content-Type") - self.assertEqual(content_type, [b"text/html; charset=utf-8"], channel.result) - - # Check that the HTML we're getting is the one we expect on a successful renewal. - expiration_ts = self.get_success(self.store.get_expiration_ts_for_user(user_id)) - expected_html = self.hs.config.account_validity.account_validity_account_renewed_template.render( - expiration_ts=expiration_ts - ) - self.assertEqual( - channel.result["body"], expected_html.encode("utf8"), channel.result - ) - - # Move 1 day forward. Try to renew with the same token again. - url = "/_matrix/client/unstable/account_validity/renew?token=%s" % renewal_token - channel = self.make_request(b"GET", url) - self.assertEquals(channel.result["code"], b"200", channel.result) - - # Check that we're getting HTML back. - content_type = channel.headers.getRawHeaders(b"Content-Type") - self.assertEqual(content_type, [b"text/html; charset=utf-8"], channel.result) - - # Check that the HTML we're getting is the one we expect when reusing a - # token. The account expiration date should not have changed. - expected_html = self.hs.config.account_validity.account_validity_account_previously_renewed_template.render( - expiration_ts=expiration_ts - ) - self.assertEqual( - channel.result["body"], expected_html.encode("utf8"), channel.result - ) - - # Move 3 days forward. If the renewal failed, every authed request with - # our access token should be denied from now, otherwise they should - # succeed. - self.reactor.advance(datetime.timedelta(days=3).total_seconds()) - channel = self.make_request(b"GET", "/sync", access_token=tok) - self.assertEquals(channel.result["code"], b"200", channel.result) - - def test_renewal_invalid_token(self): - # Hit the renewal endpoint with an invalid token and check that it behaves as - # expected, i.e. that it responds with 404 Not Found and the correct HTML. - url = "/_matrix/client/unstable/account_validity/renew?token=123" - channel = self.make_request(b"GET", url) - self.assertEquals(channel.result["code"], b"404", channel.result) - - # Check that we're getting HTML back. - content_type = channel.headers.getRawHeaders(b"Content-Type") - self.assertEqual(content_type, [b"text/html; charset=utf-8"], channel.result) - - # Check that the HTML we're getting is the one we expect when using an - # invalid/unknown token. - expected_html = ( - self.hs.config.account_validity.account_validity_invalid_token_template.render() - ) - self.assertEqual( - channel.result["body"], expected_html.encode("utf8"), channel.result - ) - - def test_manual_email_send(self): - self.email_attempts = [] - - (user_id, tok) = self.create_user() - channel = self.make_request( - b"POST", - "/_matrix/client/unstable/account_validity/send_mail", - access_token=tok, - ) - self.assertEquals(channel.result["code"], b"200", channel.result) - - self.assertEqual(len(self.email_attempts), 1) - - def test_deactivated_user(self): - self.email_attempts = [] - - (user_id, tok) = self.create_user() - - request_data = json.dumps( - { - "auth": { - "type": "m.login.password", - "user": user_id, - "password": "monkey", - }, - "erase": False, - } - ) - channel = self.make_request( - "POST", "account/deactivate", request_data, access_token=tok - ) - self.assertEqual(channel.code, 200) - - self.reactor.advance(datetime.timedelta(days=8).total_seconds()) - - self.assertEqual(len(self.email_attempts), 0) - - def create_user(self): - user_id = self.register_user("kermit", "monkey") - tok = self.login("kermit", "monkey") - # We need to manually add an email address otherwise the handler will do - # nothing. - now = self.hs.get_clock().time_msec() - self.get_success( - self.store.user_add_threepid( - user_id=user_id, - medium="email", - address="kermit@example.com", - validated_at=now, - added_at=now, - ) - ) - return user_id, tok - - def test_manual_email_send_expired_account(self): - user_id = self.register_user("kermit", "monkey") - tok = self.login("kermit", "monkey") - - # We need to manually add an email address otherwise the handler will do - # nothing. - now = self.hs.get_clock().time_msec() - self.get_success( - self.store.user_add_threepid( - user_id=user_id, - medium="email", - address="kermit@example.com", - validated_at=now, - added_at=now, - ) - ) - - # Make the account expire. - self.reactor.advance(datetime.timedelta(days=8).total_seconds()) - - # Ignore all emails sent by the automatic background task and only focus on the - # ones sent manually. - self.email_attempts = [] - - # Test that we're still able to manually trigger a mail to be sent. - channel = self.make_request( - b"POST", - "/_matrix/client/unstable/account_validity/send_mail", - access_token=tok, - ) - self.assertEquals(channel.result["code"], b"200", channel.result) - - self.assertEqual(len(self.email_attempts), 1) - - -class AccountValidityBackgroundJobTestCase(unittest.HomeserverTestCase): - - servlets = [synapse.rest.admin.register_servlets_for_client_rest_resource] - - def make_homeserver(self, reactor, clock): - self.validity_period = 10 - self.max_delta = self.validity_period * 10.0 / 100.0 - - config = self.default_config() - - config["enable_registration"] = True - config["account_validity"] = {"enabled": False} - - self.hs = self.setup_test_homeserver(config=config) - - # We need to set these directly, instead of in the homeserver config dict above. - # This is due to account validity-related config options not being read by - # Synapse when account_validity.enabled is False. - self.hs.get_datastore()._account_validity_period = self.validity_period - self.hs.get_datastore()._account_validity_startup_job_max_delta = self.max_delta - - self.store = self.hs.get_datastore() - - return self.hs - - def test_background_job(self): - """ - Tests the same thing as test_background_job, except that it sets the - startup_job_max_delta parameter and checks that the expiration date is within the - allowed range. - """ - user_id = self.register_user("kermit_delta", "user") - - self.hs.config.account_validity.startup_job_max_delta = self.max_delta - - now_ms = self.hs.get_clock().time_msec() - self.get_success(self.store._set_expiration_date_when_missing()) - - res = self.get_success(self.store.get_expiration_ts_for_user(user_id)) - - self.assertGreaterEqual(res, now_ms + self.validity_period - self.max_delta) - self.assertLessEqual(res, now_ms + self.validity_period) diff --git a/tests/rest/client/v2_alpha/test_relations.py b/tests/rest/client/v2_alpha/test_relations.py deleted file mode 100644 index 02b5e9a8d0..0000000000 --- a/tests/rest/client/v2_alpha/test_relations.py +++ /dev/null @@ -1,724 +0,0 @@ -# Copyright 2019 New Vector Ltd -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 itertools -import json -import urllib -from typing import Optional - -from synapse.api.constants import EventTypes, RelationTypes -from synapse.rest import admin -from synapse.rest.client import login, register, relations, room - -from tests import unittest - - -class RelationsTestCase(unittest.HomeserverTestCase): - servlets = [ - relations.register_servlets, - room.register_servlets, - login.register_servlets, - register.register_servlets, - admin.register_servlets_for_client_rest_resource, - ] - hijack_auth = False - - def make_homeserver(self, reactor, clock): - # We need to enable msc1849 support for aggregations - config = self.default_config() - config["experimental_msc1849_support_enabled"] = True - - # We enable frozen dicts as relations/edits change event contents, so we - # want to test that we don't modify the events in the caches. - config["use_frozen_dicts"] = True - - return self.setup_test_homeserver(config=config) - - def prepare(self, reactor, clock, hs): - self.user_id, self.user_token = self._create_user("alice") - self.user2_id, self.user2_token = self._create_user("bob") - - self.room = self.helper.create_room_as(self.user_id, tok=self.user_token) - self.helper.join(self.room, user=self.user2_id, tok=self.user2_token) - res = self.helper.send(self.room, body="Hi!", tok=self.user_token) - self.parent_id = res["event_id"] - - def test_send_relation(self): - """Tests that sending a relation using the new /send_relation works - creates the right shape of event. - """ - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", key="👍") - self.assertEquals(200, channel.code, channel.json_body) - - event_id = channel.json_body["event_id"] - - channel = self.make_request( - "GET", - "/rooms/%s/event/%s" % (self.room, event_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assert_dict( - { - "type": "m.reaction", - "sender": self.user_id, - "content": { - "m.relates_to": { - "event_id": self.parent_id, - "key": "👍", - "rel_type": RelationTypes.ANNOTATION, - } - }, - }, - channel.json_body, - ) - - def test_deny_membership(self): - """Test that we deny relations on membership events""" - channel = self._send_relation(RelationTypes.ANNOTATION, EventTypes.Member) - self.assertEquals(400, channel.code, channel.json_body) - - def test_deny_double_react(self): - """Test that we deny relations on membership events""" - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", key="a") - self.assertEquals(200, channel.code, channel.json_body) - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a") - self.assertEquals(400, channel.code, channel.json_body) - - def test_basic_paginate_relations(self): - """Tests that calling pagination API correctly the latest relations.""" - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction") - self.assertEquals(200, channel.code, channel.json_body) - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction") - self.assertEquals(200, channel.code, channel.json_body) - annotation_id = channel.json_body["event_id"] - - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/relations/%s?limit=1" - % (self.room, self.parent_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - # We expect to get back a single pagination result, which is the full - # relation event we sent above. - self.assertEquals(len(channel.json_body["chunk"]), 1, channel.json_body) - self.assert_dict( - {"event_id": annotation_id, "sender": self.user_id, "type": "m.reaction"}, - channel.json_body["chunk"][0], - ) - - # We also expect to get the original event (the id of which is self.parent_id) - self.assertEquals( - channel.json_body["original_event"]["event_id"], self.parent_id - ) - - # Make sure next_batch has something in it that looks like it could be a - # valid token. - self.assertIsInstance( - channel.json_body.get("next_batch"), str, channel.json_body - ) - - def test_repeated_paginate_relations(self): - """Test that if we paginate using a limit and tokens then we get the - expected events. - """ - - expected_event_ids = [] - for _ in range(10): - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction") - self.assertEquals(200, channel.code, channel.json_body) - expected_event_ids.append(channel.json_body["event_id"]) - - prev_token = None - found_event_ids = [] - for _ in range(20): - from_token = "" - if prev_token: - from_token = "&from=" + prev_token - - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/relations/%s?limit=1%s" - % (self.room, self.parent_id, from_token), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - found_event_ids.extend(e["event_id"] for e in channel.json_body["chunk"]) - next_batch = channel.json_body.get("next_batch") - - self.assertNotEquals(prev_token, next_batch) - prev_token = next_batch - - if not prev_token: - break - - # We paginated backwards, so reverse - found_event_ids.reverse() - self.assertEquals(found_event_ids, expected_event_ids) - - def test_aggregation_pagination_groups(self): - """Test that we can paginate annotation groups correctly.""" - - # We need to create ten separate users to send each reaction. - access_tokens = [self.user_token, self.user2_token] - idx = 0 - while len(access_tokens) < 10: - user_id, token = self._create_user("test" + str(idx)) - idx += 1 - - self.helper.join(self.room, user=user_id, tok=token) - access_tokens.append(token) - - idx = 0 - sent_groups = {"👍": 10, "a": 7, "b": 5, "c": 3, "d": 2, "e": 1} - for key in itertools.chain.from_iterable( - itertools.repeat(key, num) for key, num in sent_groups.items() - ): - channel = self._send_relation( - RelationTypes.ANNOTATION, - "m.reaction", - key=key, - access_token=access_tokens[idx], - ) - self.assertEquals(200, channel.code, channel.json_body) - - idx += 1 - idx %= len(access_tokens) - - prev_token = None - found_groups = {} - for _ in range(20): - from_token = "" - if prev_token: - from_token = "&from=" + prev_token - - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/aggregations/%s?limit=1%s" - % (self.room, self.parent_id, from_token), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertEqual(len(channel.json_body["chunk"]), 1, channel.json_body) - - for groups in channel.json_body["chunk"]: - # We only expect reactions - self.assertEqual(groups["type"], "m.reaction", channel.json_body) - - # We should only see each key once - self.assertNotIn(groups["key"], found_groups, channel.json_body) - - found_groups[groups["key"]] = groups["count"] - - next_batch = channel.json_body.get("next_batch") - - self.assertNotEquals(prev_token, next_batch) - prev_token = next_batch - - if not prev_token: - break - - self.assertEquals(sent_groups, found_groups) - - def test_aggregation_pagination_within_group(self): - """Test that we can paginate within an annotation group.""" - - # We need to create ten separate users to send each reaction. - access_tokens = [self.user_token, self.user2_token] - idx = 0 - while len(access_tokens) < 10: - user_id, token = self._create_user("test" + str(idx)) - idx += 1 - - self.helper.join(self.room, user=user_id, tok=token) - access_tokens.append(token) - - idx = 0 - expected_event_ids = [] - for _ in range(10): - channel = self._send_relation( - RelationTypes.ANNOTATION, - "m.reaction", - key="👍", - access_token=access_tokens[idx], - ) - self.assertEquals(200, channel.code, channel.json_body) - expected_event_ids.append(channel.json_body["event_id"]) - - idx += 1 - - # Also send a different type of reaction so that we test we don't see it - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", key="a") - self.assertEquals(200, channel.code, channel.json_body) - - prev_token = None - found_event_ids = [] - encoded_key = urllib.parse.quote_plus("👍".encode()) - for _ in range(20): - from_token = "" - if prev_token: - from_token = "&from=" + prev_token - - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s" - "/aggregations/%s/%s/m.reaction/%s?limit=1%s" - % ( - self.room, - self.parent_id, - RelationTypes.ANNOTATION, - encoded_key, - from_token, - ), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertEqual(len(channel.json_body["chunk"]), 1, channel.json_body) - - found_event_ids.extend(e["event_id"] for e in channel.json_body["chunk"]) - - next_batch = channel.json_body.get("next_batch") - - self.assertNotEquals(prev_token, next_batch) - prev_token = next_batch - - if not prev_token: - break - - # We paginated backwards, so reverse - found_event_ids.reverse() - self.assertEquals(found_event_ids, expected_event_ids) - - def test_aggregation(self): - """Test that annotations get correctly aggregated.""" - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a") - self.assertEquals(200, channel.code, channel.json_body) - - channel = self._send_relation( - RelationTypes.ANNOTATION, "m.reaction", "a", access_token=self.user2_token - ) - self.assertEquals(200, channel.code, channel.json_body) - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "b") - self.assertEquals(200, channel.code, channel.json_body) - - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/aggregations/%s" - % (self.room, self.parent_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertEquals( - channel.json_body, - { - "chunk": [ - {"type": "m.reaction", "key": "a", "count": 2}, - {"type": "m.reaction", "key": "b", "count": 1}, - ] - }, - ) - - def test_aggregation_redactions(self): - """Test that annotations get correctly aggregated after a redaction.""" - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a") - self.assertEquals(200, channel.code, channel.json_body) - to_redact_event_id = channel.json_body["event_id"] - - channel = self._send_relation( - RelationTypes.ANNOTATION, "m.reaction", "a", access_token=self.user2_token - ) - self.assertEquals(200, channel.code, channel.json_body) - - # Now lets redact one of the 'a' reactions - channel = self.make_request( - "POST", - "/_matrix/client/r0/rooms/%s/redact/%s" % (self.room, to_redact_event_id), - access_token=self.user_token, - content={}, - ) - self.assertEquals(200, channel.code, channel.json_body) - - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/aggregations/%s" - % (self.room, self.parent_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertEquals( - channel.json_body, - {"chunk": [{"type": "m.reaction", "key": "a", "count": 1}]}, - ) - - def test_aggregation_must_be_annotation(self): - """Test that aggregations must be annotations.""" - - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/aggregations/%s/%s?limit=1" - % (self.room, self.parent_id, RelationTypes.REPLACE), - access_token=self.user_token, - ) - self.assertEquals(400, channel.code, channel.json_body) - - def test_aggregation_get_event(self): - """Test that annotations and references get correctly bundled when - getting the parent event. - """ - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "a") - self.assertEquals(200, channel.code, channel.json_body) - - channel = self._send_relation( - RelationTypes.ANNOTATION, "m.reaction", "a", access_token=self.user2_token - ) - self.assertEquals(200, channel.code, channel.json_body) - - channel = self._send_relation(RelationTypes.ANNOTATION, "m.reaction", "b") - self.assertEquals(200, channel.code, channel.json_body) - - channel = self._send_relation(RelationTypes.REFERENCE, "m.room.test") - self.assertEquals(200, channel.code, channel.json_body) - reply_1 = channel.json_body["event_id"] - - channel = self._send_relation(RelationTypes.REFERENCE, "m.room.test") - self.assertEquals(200, channel.code, channel.json_body) - reply_2 = channel.json_body["event_id"] - - channel = self.make_request( - "GET", - "/rooms/%s/event/%s" % (self.room, self.parent_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertEquals( - channel.json_body["unsigned"].get("m.relations"), - { - RelationTypes.ANNOTATION: { - "chunk": [ - {"type": "m.reaction", "key": "a", "count": 2}, - {"type": "m.reaction", "key": "b", "count": 1}, - ] - }, - RelationTypes.REFERENCE: { - "chunk": [{"event_id": reply_1}, {"event_id": reply_2}] - }, - }, - ) - - def test_edit(self): - """Test that a simple edit works.""" - - new_body = {"msgtype": "m.text", "body": "I've been edited!"} - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message", - content={"msgtype": "m.text", "body": "foo", "m.new_content": new_body}, - ) - self.assertEquals(200, channel.code, channel.json_body) - - edit_event_id = channel.json_body["event_id"] - - channel = self.make_request( - "GET", - "/rooms/%s/event/%s" % (self.room, self.parent_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertEquals(channel.json_body["content"], new_body) - - relations_dict = channel.json_body["unsigned"].get("m.relations") - self.assertIn(RelationTypes.REPLACE, relations_dict) - - m_replace_dict = relations_dict[RelationTypes.REPLACE] - for key in ["event_id", "sender", "origin_server_ts"]: - self.assertIn(key, m_replace_dict) - - self.assert_dict( - {"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict - ) - - def test_multi_edit(self): - """Test that multiple edits, including attempts by people who - shouldn't be allowed, are correctly handled. - """ - - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message", - content={ - "msgtype": "m.text", - "body": "Wibble", - "m.new_content": {"msgtype": "m.text", "body": "First edit"}, - }, - ) - self.assertEquals(200, channel.code, channel.json_body) - - new_body = {"msgtype": "m.text", "body": "I've been edited!"} - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message", - content={"msgtype": "m.text", "body": "foo", "m.new_content": new_body}, - ) - self.assertEquals(200, channel.code, channel.json_body) - - edit_event_id = channel.json_body["event_id"] - - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message.WRONG_TYPE", - content={ - "msgtype": "m.text", - "body": "Wibble", - "m.new_content": {"msgtype": "m.text", "body": "Edit, but wrong type"}, - }, - ) - self.assertEquals(200, channel.code, channel.json_body) - - channel = self.make_request( - "GET", - "/rooms/%s/event/%s" % (self.room, self.parent_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertEquals(channel.json_body["content"], new_body) - - relations_dict = channel.json_body["unsigned"].get("m.relations") - self.assertIn(RelationTypes.REPLACE, relations_dict) - - m_replace_dict = relations_dict[RelationTypes.REPLACE] - for key in ["event_id", "sender", "origin_server_ts"]: - self.assertIn(key, m_replace_dict) - - self.assert_dict( - {"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict - ) - - def test_edit_reply(self): - """Test that editing a reply works.""" - - # Create a reply to edit. - channel = self._send_relation( - RelationTypes.REFERENCE, - "m.room.message", - content={"msgtype": "m.text", "body": "A reply!"}, - ) - self.assertEquals(200, channel.code, channel.json_body) - reply = channel.json_body["event_id"] - - new_body = {"msgtype": "m.text", "body": "I've been edited!"} - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message", - content={"msgtype": "m.text", "body": "foo", "m.new_content": new_body}, - parent_id=reply, - ) - self.assertEquals(200, channel.code, channel.json_body) - - edit_event_id = channel.json_body["event_id"] - - channel = self.make_request( - "GET", - "/rooms/%s/event/%s" % (self.room, reply), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - # We expect to see the new body in the dict, as well as the reference - # metadata sill intact. - self.assertDictContainsSubset(new_body, channel.json_body["content"]) - self.assertDictContainsSubset( - { - "m.relates_to": { - "event_id": self.parent_id, - "key": None, - "rel_type": "m.reference", - } - }, - channel.json_body["content"], - ) - - # We expect that the edit relation appears in the unsigned relations - # section. - relations_dict = channel.json_body["unsigned"].get("m.relations") - self.assertIn(RelationTypes.REPLACE, relations_dict) - - m_replace_dict = relations_dict[RelationTypes.REPLACE] - for key in ["event_id", "sender", "origin_server_ts"]: - self.assertIn(key, m_replace_dict) - - self.assert_dict( - {"event_id": edit_event_id, "sender": self.user_id}, m_replace_dict - ) - - def test_relations_redaction_redacts_edits(self): - """Test that edits of an event are redacted when the original event - is redacted. - """ - # Send a new event - res = self.helper.send(self.room, body="Heyo!", tok=self.user_token) - original_event_id = res["event_id"] - - # Add a relation - channel = self._send_relation( - RelationTypes.REPLACE, - "m.room.message", - parent_id=original_event_id, - content={ - "msgtype": "m.text", - "body": "Wibble", - "m.new_content": {"msgtype": "m.text", "body": "First edit"}, - }, - ) - self.assertEquals(200, channel.code, channel.json_body) - - # Check the relation is returned - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/relations/%s/m.replace/m.room.message" - % (self.room, original_event_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertIn("chunk", channel.json_body) - self.assertEquals(len(channel.json_body["chunk"]), 1) - - # Redact the original event - channel = self.make_request( - "PUT", - "/rooms/%s/redact/%s/%s" - % (self.room, original_event_id, "test_relations_redaction_redacts_edits"), - access_token=self.user_token, - content="{}", - ) - self.assertEquals(200, channel.code, channel.json_body) - - # Try to check for remaining m.replace relations - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/relations/%s/m.replace/m.room.message" - % (self.room, original_event_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - # Check that no relations are returned - self.assertIn("chunk", channel.json_body) - self.assertEquals(channel.json_body["chunk"], []) - - def test_aggregations_redaction_prevents_access_to_aggregations(self): - """Test that annotations of an event are redacted when the original event - is redacted. - """ - # Send a new event - res = self.helper.send(self.room, body="Hello!", tok=self.user_token) - original_event_id = res["event_id"] - - # Add a relation - channel = self._send_relation( - RelationTypes.ANNOTATION, "m.reaction", key="👍", parent_id=original_event_id - ) - self.assertEquals(200, channel.code, channel.json_body) - - # Redact the original - channel = self.make_request( - "PUT", - "/rooms/%s/redact/%s/%s" - % ( - self.room, - original_event_id, - "test_aggregations_redaction_prevents_access_to_aggregations", - ), - access_token=self.user_token, - content="{}", - ) - self.assertEquals(200, channel.code, channel.json_body) - - # Check that aggregations returns zero - channel = self.make_request( - "GET", - "/_matrix/client/unstable/rooms/%s/aggregations/%s/m.annotation/m.reaction" - % (self.room, original_event_id), - access_token=self.user_token, - ) - self.assertEquals(200, channel.code, channel.json_body) - - self.assertIn("chunk", channel.json_body) - self.assertEquals(channel.json_body["chunk"], []) - - def _send_relation( - self, - relation_type, - event_type, - key=None, - content: Optional[dict] = None, - access_token=None, - parent_id=None, - ): - """Helper function to send a relation pointing at `self.parent_id` - - Args: - relation_type (str): One of `RelationTypes` - event_type (str): The type of the event to create - parent_id (str): The event_id this relation relates to. If None, then self.parent_id - key (str|None): The aggregation key used for m.annotation relation - type. - content(dict|None): The content of the created event. - access_token (str|None): The access token used to send the relation, - defaults to `self.user_token` - - Returns: - FakeChannel - """ - if not access_token: - access_token = self.user_token - - query = "" - if key: - query = "?key=" + urllib.parse.quote_plus(key.encode("utf-8")) - - original_id = parent_id if parent_id else self.parent_id - - channel = self.make_request( - "POST", - "/_matrix/client/unstable/rooms/%s/send_relation/%s/%s/%s%s" - % (self.room, original_id, relation_type, event_type, query), - json.dumps(content or {}).encode("utf-8"), - access_token=access_token, - ) - return channel - - def _create_user(self, localpart): - user_id = self.register_user(localpart, "abc123") - access_token = self.login(localpart, "abc123") - - return user_id, access_token diff --git a/tests/rest/client/v2_alpha/test_report_event.py b/tests/rest/client/v2_alpha/test_report_event.py deleted file mode 100644 index ee6b0b9ebf..0000000000 --- a/tests/rest/client/v2_alpha/test_report_event.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2021 Callum Brown -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 - -import synapse.rest.admin -from synapse.rest.client import login, report_event, room - -from tests import unittest - - -class ReportEventTestCase(unittest.HomeserverTestCase): - servlets = [ - synapse.rest.admin.register_servlets, - login.register_servlets, - room.register_servlets, - report_event.register_servlets, - ] - - def prepare(self, reactor, clock, hs): - self.admin_user = self.register_user("admin", "pass", admin=True) - self.admin_user_tok = self.login("admin", "pass") - self.other_user = self.register_user("user", "pass") - self.other_user_tok = self.login("user", "pass") - - self.room_id = self.helper.create_room_as( - self.other_user, tok=self.other_user_tok, is_public=True - ) - self.helper.join(self.room_id, user=self.admin_user, tok=self.admin_user_tok) - resp = self.helper.send(self.room_id, tok=self.admin_user_tok) - self.event_id = resp["event_id"] - self.report_path = f"rooms/{self.room_id}/report/{self.event_id}" - - def test_reason_str_and_score_int(self): - data = {"reason": "this makes me sad", "score": -100} - self._assert_status(200, data) - - def test_no_reason(self): - data = {"score": 0} - self._assert_status(200, data) - - def test_no_score(self): - data = {"reason": "this makes me sad"} - self._assert_status(200, data) - - def test_no_reason_and_no_score(self): - data = {} - self._assert_status(200, data) - - def test_reason_int_and_score_str(self): - data = {"reason": 10, "score": "string"} - self._assert_status(400, data) - - def test_reason_zero_and_score_blank(self): - data = {"reason": 0, "score": ""} - self._assert_status(400, data) - - def test_reason_and_score_null(self): - data = {"reason": None, "score": None} - self._assert_status(400, data) - - def _assert_status(self, response_status, data): - channel = self.make_request( - "POST", - self.report_path, - json.dumps(data), - access_token=self.other_user_tok, - ) - self.assertEqual( - response_status, int(channel.result["code"]), msg=channel.result["body"] - ) diff --git a/tests/rest/client/v2_alpha/test_sendtodevice.py b/tests/rest/client/v2_alpha/test_sendtodevice.py deleted file mode 100644 index 6db7062a8e..0000000000 --- a/tests/rest/client/v2_alpha/test_sendtodevice.py +++ /dev/null @@ -1,200 +0,0 @@ -# Copyright 2021 The Matrix.org Foundation C.I.C. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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. - -from synapse.rest import admin -from synapse.rest.client import login, sendtodevice, sync - -from tests.unittest import HomeserverTestCase, override_config - - -class SendToDeviceTestCase(HomeserverTestCase): - servlets = [ - admin.register_servlets, - login.register_servlets, - sendtodevice.register_servlets, - sync.register_servlets, - ] - - def test_user_to_user(self): - """A to-device message from one user to another should get delivered""" - - user1 = self.register_user("u1", "pass") - user1_tok = self.login("u1", "pass", "d1") - - user2 = self.register_user("u2", "pass") - user2_tok = self.login("u2", "pass", "d2") - - # send the message - test_msg = {"foo": "bar"} - chan = self.make_request( - "PUT", - "/_matrix/client/r0/sendToDevice/m.test/1234", - content={"messages": {user2: {"d2": test_msg}}}, - access_token=user1_tok, - ) - self.assertEqual(chan.code, 200, chan.result) - - # check it appears - channel = self.make_request("GET", "/sync", access_token=user2_tok) - self.assertEqual(channel.code, 200, channel.result) - expected_result = { - "events": [ - { - "sender": user1, - "type": "m.test", - "content": test_msg, - } - ] - } - self.assertEqual(channel.json_body["to_device"], expected_result) - - # it should re-appear if we do another sync - channel = self.make_request("GET", "/sync", access_token=user2_tok) - self.assertEqual(channel.code, 200, channel.result) - self.assertEqual(channel.json_body["to_device"], expected_result) - - # it should *not* appear if we do an incremental sync - sync_token = channel.json_body["next_batch"] - channel = self.make_request( - "GET", f"/sync?since={sync_token}", access_token=user2_tok - ) - self.assertEqual(channel.code, 200, channel.result) - self.assertEqual(channel.json_body.get("to_device", {}).get("events", []), []) - - @override_config({"rc_key_requests": {"per_second": 10, "burst_count": 2}}) - def test_local_room_key_request(self): - """m.room_key_request has special-casing; test from local user""" - user1 = self.register_user("u1", "pass") - user1_tok = self.login("u1", "pass", "d1") - - user2 = self.register_user("u2", "pass") - user2_tok = self.login("u2", "pass", "d2") - - # send three messages - for i in range(3): - chan = self.make_request( - "PUT", - f"/_matrix/client/r0/sendToDevice/m.room_key_request/{i}", - content={"messages": {user2: {"d2": {"idx": i}}}}, - access_token=user1_tok, - ) - self.assertEqual(chan.code, 200, chan.result) - - # now sync: we should get two of the three - channel = self.make_request("GET", "/sync", access_token=user2_tok) - self.assertEqual(channel.code, 200, channel.result) - msgs = channel.json_body["to_device"]["events"] - self.assertEqual(len(msgs), 2) - for i in range(2): - self.assertEqual( - msgs[i], - {"sender": user1, "type": "m.room_key_request", "content": {"idx": i}}, - ) - sync_token = channel.json_body["next_batch"] - - # ... time passes - self.reactor.advance(1) - - # and we can send more messages - chan = self.make_request( - "PUT", - "/_matrix/client/r0/sendToDevice/m.room_key_request/3", - content={"messages": {user2: {"d2": {"idx": 3}}}}, - access_token=user1_tok, - ) - self.assertEqual(chan.code, 200, chan.result) - - # ... which should arrive - channel = self.make_request( - "GET", f"/sync?since={sync_token}", access_token=user2_tok - ) - self.assertEqual(channel.code, 200, channel.result) - msgs = channel.json_body["to_device"]["events"] - self.assertEqual(len(msgs), 1) - self.assertEqual( - msgs[0], - {"sender": user1, "type": "m.room_key_request", "content": {"idx": 3}}, - ) - - @override_config({"rc_key_requests": {"per_second": 10, "burst_count": 2}}) - def test_remote_room_key_request(self): - """m.room_key_request has special-casing; test from remote user""" - user2 = self.register_user("u2", "pass") - user2_tok = self.login("u2", "pass", "d2") - - federation_registry = self.hs.get_federation_registry() - - # send three messages - for i in range(3): - self.get_success( - federation_registry.on_edu( - "m.direct_to_device", - "remote_server", - { - "sender": "@user:remote_server", - "type": "m.room_key_request", - "messages": {user2: {"d2": {"idx": i}}}, - "message_id": f"{i}", - }, - ) - ) - - # now sync: we should get two of the three - channel = self.make_request("GET", "/sync", access_token=user2_tok) - self.assertEqual(channel.code, 200, channel.result) - msgs = channel.json_body["to_device"]["events"] - self.assertEqual(len(msgs), 2) - for i in range(2): - self.assertEqual( - msgs[i], - { - "sender": "@user:remote_server", - "type": "m.room_key_request", - "content": {"idx": i}, - }, - ) - sync_token = channel.json_body["next_batch"] - - # ... time passes - self.reactor.advance(1) - - # and we can send more messages - self.get_success( - federation_registry.on_edu( - "m.direct_to_device", - "remote_server", - { - "sender": "@user:remote_server", - "type": "m.room_key_request", - "messages": {user2: {"d2": {"idx": 3}}}, - "message_id": "3", - }, - ) - ) - - # ... which should arrive - channel = self.make_request( - "GET", f"/sync?since={sync_token}", access_token=user2_tok - ) - self.assertEqual(channel.code, 200, channel.result) - msgs = channel.json_body["to_device"]["events"] - self.assertEqual(len(msgs), 1) - self.assertEqual( - msgs[0], - { - "sender": "@user:remote_server", - "type": "m.room_key_request", - "content": {"idx": 3}, - }, - ) diff --git a/tests/rest/client/v2_alpha/test_shared_rooms.py b/tests/rest/client/v2_alpha/test_shared_rooms.py deleted file mode 100644 index 283eccd53f..0000000000 --- a/tests/rest/client/v2_alpha/test_shared_rooms.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright 2020 Half-Shot -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 synapse.rest.admin -from synapse.rest.client import login, room, shared_rooms - -from tests import unittest -from tests.server import FakeChannel - - -class UserSharedRoomsTest(unittest.HomeserverTestCase): - """ - Tests the UserSharedRoomsServlet. - """ - - servlets = [ - login.register_servlets, - synapse.rest.admin.register_servlets_for_client_rest_resource, - room.register_servlets, - shared_rooms.register_servlets, - ] - - def make_homeserver(self, reactor, clock): - config = self.default_config() - config["update_user_directory"] = True - return self.setup_test_homeserver(config=config) - - def prepare(self, reactor, clock, hs): - self.store = hs.get_datastore() - self.handler = hs.get_user_directory_handler() - - def _get_shared_rooms(self, token, other_user) -> FakeChannel: - return self.make_request( - "GET", - "/_matrix/client/unstable/uk.half-shot.msc2666/user/shared_rooms/%s" - % other_user, - access_token=token, - ) - - def test_shared_room_list_public(self): - """ - A room should show up in the shared list of rooms between two users - if it is public. - """ - self._check_shared_rooms_with(room_one_is_public=True, room_two_is_public=True) - - def test_shared_room_list_private(self): - """ - A room should show up in the shared list of rooms between two users - if it is private. - """ - self._check_shared_rooms_with( - room_one_is_public=False, room_two_is_public=False - ) - - def test_shared_room_list_mixed(self): - """ - The shared room list between two users should contain both public and private - rooms. - """ - self._check_shared_rooms_with(room_one_is_public=True, room_two_is_public=False) - - def _check_shared_rooms_with( - self, room_one_is_public: bool, room_two_is_public: bool - ): - """Checks that shared public or private rooms between two users appear in - their shared room lists - """ - u1 = self.register_user("user1", "pass") - u1_token = self.login(u1, "pass") - u2 = self.register_user("user2", "pass") - u2_token = self.login(u2, "pass") - - # Create a room. user1 invites user2, who joins - room_id_one = self.helper.create_room_as( - u1, is_public=room_one_is_public, tok=u1_token - ) - self.helper.invite(room_id_one, src=u1, targ=u2, tok=u1_token) - self.helper.join(room_id_one, user=u2, tok=u2_token) - - # Check shared rooms from user1's perspective. - # We should see the one room in common - channel = self._get_shared_rooms(u1_token, u2) - self.assertEquals(200, channel.code, channel.result) - self.assertEquals(len(channel.json_body["joined"]), 1) - self.assertEquals(channel.json_body["joined"][0], room_id_one) - - # Create another room and invite user2 to it - room_id_two = self.helper.create_room_as( - u1, is_public=room_two_is_public, tok=u1_token - ) - self.helper.invite(room_id_two, src=u1, targ=u2, tok=u1_token) - self.helper.join(room_id_two, user=u2, tok=u2_token) - - # Check shared rooms again. We should now see both rooms. - channel = self._get_shared_rooms(u1_token, u2) - self.assertEquals(200, channel.code, channel.result) - self.assertEquals(len(channel.json_body["joined"]), 2) - for room_id_id in channel.json_body["joined"]: - self.assertIn(room_id_id, [room_id_one, room_id_two]) - - def test_shared_room_list_after_leave(self): - """ - A room should no longer be considered shared if the other - user has left it. - """ - u1 = self.register_user("user1", "pass") - u1_token = self.login(u1, "pass") - u2 = self.register_user("user2", "pass") - u2_token = self.login(u2, "pass") - - room = self.helper.create_room_as(u1, is_public=True, tok=u1_token) - self.helper.invite(room, src=u1, targ=u2, tok=u1_token) - self.helper.join(room, user=u2, tok=u2_token) - - # Assert user directory is not empty - channel = self._get_shared_rooms(u1_token, u2) - self.assertEquals(200, channel.code, channel.result) - self.assertEquals(len(channel.json_body["joined"]), 1) - self.assertEquals(channel.json_body["joined"][0], room) - - self.helper.leave(room, user=u1, tok=u1_token) - - # Check user1's view of shared rooms with user2 - channel = self._get_shared_rooms(u1_token, u2) - self.assertEquals(200, channel.code, channel.result) - self.assertEquals(len(channel.json_body["joined"]), 0) - - # Check user2's view of shared rooms with user1 - channel = self._get_shared_rooms(u2_token, u1) - self.assertEquals(200, channel.code, channel.result) - self.assertEquals(len(channel.json_body["joined"]), 0) diff --git a/tests/rest/client/v2_alpha/test_sync.py b/tests/rest/client/v2_alpha/test_sync.py deleted file mode 100644 index 95be369d4b..0000000000 --- a/tests/rest/client/v2_alpha/test_sync.py +++ /dev/null @@ -1,686 +0,0 @@ -# Copyright 2018-2019 New Vector Ltd -# Copyright 2019 The Matrix.org Foundation C.I.C. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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 - -import synapse.rest.admin -from synapse.api.constants import ( - EventContentFields, - EventTypes, - ReadReceiptEventFields, - RelationTypes, -) -from synapse.rest.client import knock, login, read_marker, receipts, room, sync - -from tests import unittest -from tests.federation.transport.test_knocking import ( - KnockingStrippedStateEventHelperMixin, -) -from tests.server import TimedOutException -from tests.unittest import override_config - - -class FilterTestCase(unittest.HomeserverTestCase): - - user_id = "@apple:test" - servlets = [ - synapse.rest.admin.register_servlets_for_client_rest_resource, - room.register_servlets, - login.register_servlets, - sync.register_servlets, - ] - - def test_sync_argless(self): - channel = self.make_request("GET", "/sync") - - self.assertEqual(channel.code, 200) - self.assertIn("next_batch", channel.json_body) - - -class SyncFilterTestCase(unittest.HomeserverTestCase): - servlets = [ - synapse.rest.admin.register_servlets_for_client_rest_resource, - room.register_servlets, - login.register_servlets, - sync.register_servlets, - ] - - def test_sync_filter_labels(self): - """Test that we can filter by a label.""" - sync_filter = json.dumps( - { - "room": { - "timeline": { - "types": [EventTypes.Message], - "org.matrix.labels": ["#fun"], - } - } - } - ) - - events = self._test_sync_filter_labels(sync_filter) - - self.assertEqual(len(events), 2, [event["content"] for event in events]) - self.assertEqual(events[0]["content"]["body"], "with right label", events[0]) - self.assertEqual(events[1]["content"]["body"], "with right label", events[1]) - - def test_sync_filter_not_labels(self): - """Test that we can filter by the absence of a label.""" - sync_filter = json.dumps( - { - "room": { - "timeline": { - "types": [EventTypes.Message], - "org.matrix.not_labels": ["#fun"], - } - } - } - ) - - events = self._test_sync_filter_labels(sync_filter) - - self.assertEqual(len(events), 3, [event["content"] for event in events]) - self.assertEqual(events[0]["content"]["body"], "without label", events[0]) - self.assertEqual(events[1]["content"]["body"], "with wrong label", events[1]) - self.assertEqual( - events[2]["content"]["body"], "with two wrong labels", events[2] - ) - - def test_sync_filter_labels_not_labels(self): - """Test that we can filter by both a label and the absence of another label.""" - sync_filter = json.dumps( - { - "room": { - "timeline": { - "types": [EventTypes.Message], - "org.matrix.labels": ["#work"], - "org.matrix.not_labels": ["#notfun"], - } - } - } - ) - - events = self._test_sync_filter_labels(sync_filter) - - self.assertEqual(len(events), 1, [event["content"] for event in events]) - self.assertEqual(events[0]["content"]["body"], "with wrong label", events[0]) - - def _test_sync_filter_labels(self, sync_filter): - user_id = self.register_user("kermit", "test") - tok = self.login("kermit", "test") - - room_id = self.helper.create_room_as(user_id, tok=tok) - - self.helper.send_event( - room_id=room_id, - type=EventTypes.Message, - content={ - "msgtype": "m.text", - "body": "with right label", - EventContentFields.LABELS: ["#fun"], - }, - tok=tok, - ) - - self.helper.send_event( - room_id=room_id, - type=EventTypes.Message, - content={"msgtype": "m.text", "body": "without label"}, - tok=tok, - ) - - self.helper.send_event( - room_id=room_id, - type=EventTypes.Message, - content={ - "msgtype": "m.text", - "body": "with wrong label", - EventContentFields.LABELS: ["#work"], - }, - tok=tok, - ) - - self.helper.send_event( - room_id=room_id, - type=EventTypes.Message, - content={ - "msgtype": "m.text", - "body": "with two wrong labels", - EventContentFields.LABELS: ["#work", "#notfun"], - }, - tok=tok, - ) - - self.helper.send_event( - room_id=room_id, - type=EventTypes.Message, - content={ - "msgtype": "m.text", - "body": "with right label", - EventContentFields.LABELS: ["#fun"], - }, - tok=tok, - ) - - channel = self.make_request( - "GET", "/sync?filter=%s" % sync_filter, access_token=tok - ) - self.assertEqual(channel.code, 200, channel.result) - - return channel.json_body["rooms"]["join"][room_id]["timeline"]["events"] - - -class SyncTypingTests(unittest.HomeserverTestCase): - - servlets = [ - synapse.rest.admin.register_servlets_for_client_rest_resource, - room.register_servlets, - login.register_servlets, - sync.register_servlets, - ] - user_id = True - hijack_auth = False - - def test_sync_backwards_typing(self): - """ - If the typing serial goes backwards and the typing handler is then reset - (such as when the master restarts and sets the typing serial to 0), we - do not incorrectly return typing information that had a serial greater - than the now-reset serial. - """ - typing_url = "/rooms/%s/typing/%s?access_token=%s" - sync_url = "/sync?timeout=3000000&access_token=%s&since=%s" - - # Register the user who gets notified - user_id = self.register_user("user", "pass") - access_token = self.login("user", "pass") - - # Register the user who sends the message - other_user_id = self.register_user("otheruser", "pass") - other_access_token = self.login("otheruser", "pass") - - # Create a room - room = self.helper.create_room_as(user_id, tok=access_token) - - # Invite the other person - self.helper.invite(room=room, src=user_id, tok=access_token, targ=other_user_id) - - # The other user joins - self.helper.join(room=room, user=other_user_id, tok=other_access_token) - - # The other user sends some messages - self.helper.send(room, body="Hi!", tok=other_access_token) - self.helper.send(room, body="There!", tok=other_access_token) - - # Start typing. - channel = self.make_request( - "PUT", - typing_url % (room, other_user_id, other_access_token), - b'{"typing": true, "timeout": 30000}', - ) - self.assertEquals(200, channel.code) - - channel = self.make_request("GET", "/sync?access_token=%s" % (access_token,)) - self.assertEquals(200, channel.code) - next_batch = channel.json_body["next_batch"] - - # Stop typing. - channel = self.make_request( - "PUT", - typing_url % (room, other_user_id, other_access_token), - b'{"typing": false}', - ) - self.assertEquals(200, channel.code) - - # Start typing. - channel = self.make_request( - "PUT", - typing_url % (room, other_user_id, other_access_token), - b'{"typing": true, "timeout": 30000}', - ) - self.assertEquals(200, channel.code) - - # Should return immediately - channel = self.make_request("GET", sync_url % (access_token, next_batch)) - self.assertEquals(200, channel.code) - next_batch = channel.json_body["next_batch"] - - # Reset typing serial back to 0, as if the master had. - typing = self.hs.get_typing_handler() - typing._latest_room_serial = 0 - - # Since it checks the state token, we need some state to update to - # invalidate the stream token. - self.helper.send(room, body="There!", tok=other_access_token) - - channel = self.make_request("GET", sync_url % (access_token, next_batch)) - self.assertEquals(200, channel.code) - next_batch = channel.json_body["next_batch"] - - # This should time out! But it does not, because our stream token is - # ahead, and therefore it's saying the typing (that we've actually - # already seen) is new, since it's got a token above our new, now-reset - # stream token. - channel = self.make_request("GET", sync_url % (access_token, next_batch)) - self.assertEquals(200, channel.code) - next_batch = channel.json_body["next_batch"] - - # Clear the typing information, so that it doesn't think everything is - # in the future. - typing._reset() - - # Now it SHOULD fail as it never completes! - with self.assertRaises(TimedOutException): - self.make_request("GET", sync_url % (access_token, next_batch)) - - -class SyncKnockTestCase( - unittest.HomeserverTestCase, KnockingStrippedStateEventHelperMixin -): - servlets = [ - synapse.rest.admin.register_servlets, - login.register_servlets, - room.register_servlets, - sync.register_servlets, - knock.register_servlets, - ] - - def prepare(self, reactor, clock, hs): - self.store = hs.get_datastore() - self.url = "/sync?since=%s" - self.next_batch = "s0" - - # Register the first user (used to create the room to knock on). - self.user_id = self.register_user("kermit", "monkey") - self.tok = self.login("kermit", "monkey") - - # Create the room we'll knock on. - self.room_id = self.helper.create_room_as( - self.user_id, - is_public=False, - room_version="7", - tok=self.tok, - ) - - # Register the second user (used to knock on the room). - self.knocker = self.register_user("knocker", "monkey") - self.knocker_tok = self.login("knocker", "monkey") - - # Perform an initial sync for the knocking user. - channel = self.make_request( - "GET", - self.url % self.next_batch, - access_token=self.tok, - ) - self.assertEqual(channel.code, 200, channel.json_body) - - # Store the next batch for the next request. - self.next_batch = channel.json_body["next_batch"] - - # Set up some room state to test with. - self.expected_room_state = self.send_example_state_events_to_room( - hs, self.room_id, self.user_id - ) - - @override_config({"experimental_features": {"msc2403_enabled": True}}) - def test_knock_room_state(self): - """Tests that /sync returns state from a room after knocking on it.""" - # Knock on a room - channel = self.make_request( - "POST", - "/_matrix/client/r0/knock/%s" % (self.room_id,), - b"{}", - self.knocker_tok, - ) - self.assertEquals(200, channel.code, channel.result) - - # We expect to see the knock event in the stripped room state later - self.expected_room_state[EventTypes.Member] = { - "content": {"membership": "knock", "displayname": "knocker"}, - "state_key": "@knocker:test", - } - - # Check that /sync includes stripped state from the room - channel = self.make_request( - "GET", - self.url % self.next_batch, - access_token=self.knocker_tok, - ) - self.assertEqual(channel.code, 200, channel.json_body) - - # Extract the stripped room state events from /sync - knock_entry = channel.json_body["rooms"]["knock"] - room_state_events = knock_entry[self.room_id]["knock_state"]["events"] - - # Validate that the knock membership event came last - self.assertEqual(room_state_events[-1]["type"], EventTypes.Member) - - # Validate the stripped room state events - self.check_knock_room_state_against_room_state( - room_state_events, self.expected_room_state - ) - - -class ReadReceiptsTestCase(unittest.HomeserverTestCase): - servlets = [ - synapse.rest.admin.register_servlets, - login.register_servlets, - receipts.register_servlets, - room.register_servlets, - sync.register_servlets, - ] - - def prepare(self, reactor, clock, hs): - self.url = "/sync?since=%s" - self.next_batch = "s0" - - # Register the first user - self.user_id = self.register_user("kermit", "monkey") - self.tok = self.login("kermit", "monkey") - - # Create the room - self.room_id = self.helper.create_room_as(self.user_id, tok=self.tok) - - # Register the second user - self.user2 = self.register_user("kermit2", "monkey") - self.tok2 = self.login("kermit2", "monkey") - - # Join the second user - self.helper.join(room=self.room_id, user=self.user2, tok=self.tok2) - - @override_config({"experimental_features": {"msc2285_enabled": True}}) - def test_hidden_read_receipts(self): - # Send a message as the first user - res = self.helper.send(self.room_id, body="hello", tok=self.tok) - - # Send a read receipt to tell the server the first user's message was read - body = json.dumps({ReadReceiptEventFields.MSC2285_HIDDEN: True}).encode("utf8") - channel = self.make_request( - "POST", - "/rooms/%s/receipt/m.read/%s" % (self.room_id, res["event_id"]), - body, - access_token=self.tok2, - ) - self.assertEqual(channel.code, 200) - - # Test that the first user can't see the other user's hidden read receipt - self.assertEqual(self._get_read_receipt(), None) - - def test_read_receipt_with_empty_body(self): - # Send a message as the first user - res = self.helper.send(self.room_id, body="hello", tok=self.tok) - - # Send a read receipt for this message with an empty body - channel = self.make_request( - "POST", - "/rooms/%s/receipt/m.read/%s" % (self.room_id, res["event_id"]), - access_token=self.tok2, - ) - self.assertEqual(channel.code, 200) - - def _get_read_receipt(self): - """Syncs and returns the read receipt.""" - - # Checks if event is a read receipt - def is_read_receipt(event): - return event["type"] == "m.receipt" - - # Sync - channel = self.make_request( - "GET", - self.url % self.next_batch, - access_token=self.tok, - ) - self.assertEqual(channel.code, 200) - - # Store the next batch for the next request. - self.next_batch = channel.json_body["next_batch"] - - # Return the read receipt - ephemeral_events = channel.json_body["rooms"]["join"][self.room_id][ - "ephemeral" - ]["events"] - return next(filter(is_read_receipt, ephemeral_events), None) - - -class UnreadMessagesTestCase(unittest.HomeserverTestCase): - servlets = [ - synapse.rest.admin.register_servlets, - login.register_servlets, - read_marker.register_servlets, - room.register_servlets, - sync.register_servlets, - receipts.register_servlets, - ] - - def prepare(self, reactor, clock, hs): - self.url = "/sync?since=%s" - self.next_batch = "s0" - - # Register the first user (used to check the unread counts). - self.user_id = self.register_user("kermit", "monkey") - self.tok = self.login("kermit", "monkey") - - # Create the room we'll check unread counts for. - self.room_id = self.helper.create_room_as(self.user_id, tok=self.tok) - - # Register the second user (used to send events to the room). - self.user2 = self.register_user("kermit2", "monkey") - self.tok2 = self.login("kermit2", "monkey") - - # Change the power levels of the room so that the second user can send state - # events. - self.helper.send_state( - self.room_id, - EventTypes.PowerLevels, - { - "users": {self.user_id: 100, self.user2: 100}, - "users_default": 0, - "events": { - "m.room.name": 50, - "m.room.power_levels": 100, - "m.room.history_visibility": 100, - "m.room.canonical_alias": 50, - "m.room.avatar": 50, - "m.room.tombstone": 100, - "m.room.server_acl": 100, - "m.room.encryption": 100, - }, - "events_default": 0, - "state_default": 50, - "ban": 50, - "kick": 50, - "redact": 50, - "invite": 0, - }, - tok=self.tok, - ) - - def test_unread_counts(self): - """Tests that /sync returns the right value for the unread count (MSC2654).""" - - # Check that our own messages don't increase the unread count. - self.helper.send(self.room_id, "hello", tok=self.tok) - self._check_unread_count(0) - - # Join the new user and check that this doesn't increase the unread count. - self.helper.join(room=self.room_id, user=self.user2, tok=self.tok2) - self._check_unread_count(0) - - # Check that the new user sending a message increases our unread count. - res = self.helper.send(self.room_id, "hello", tok=self.tok2) - self._check_unread_count(1) - - # Send a read receipt to tell the server we've read the latest event. - body = json.dumps({"m.read": res["event_id"]}).encode("utf8") - channel = self.make_request( - "POST", - "/rooms/%s/read_markers" % self.room_id, - body, - access_token=self.tok, - ) - self.assertEqual(channel.code, 200, channel.json_body) - - # Check that the unread counter is back to 0. - self._check_unread_count(0) - - # Check that hidden read receipts don't break unread counts - res = self.helper.send(self.room_id, "hello", tok=self.tok2) - self._check_unread_count(1) - - # Send a read receipt to tell the server we've read the latest event. - body = json.dumps({ReadReceiptEventFields.MSC2285_HIDDEN: True}).encode("utf8") - channel = self.make_request( - "POST", - "/rooms/%s/receipt/m.read/%s" % (self.room_id, res["event_id"]), - body, - access_token=self.tok, - ) - self.assertEqual(channel.code, 200, channel.json_body) - - # Check that the unread counter is back to 0. - self._check_unread_count(0) - - # Check that room name changes increase the unread counter. - self.helper.send_state( - self.room_id, - "m.room.name", - {"name": "my super room"}, - tok=self.tok2, - ) - self._check_unread_count(1) - - # Check that room topic changes increase the unread counter. - self.helper.send_state( - self.room_id, - "m.room.topic", - {"topic": "welcome!!!"}, - tok=self.tok2, - ) - self._check_unread_count(2) - - # Check that encrypted messages increase the unread counter. - self.helper.send_event(self.room_id, EventTypes.Encrypted, {}, tok=self.tok2) - self._check_unread_count(3) - - # Check that custom events with a body increase the unread counter. - self.helper.send_event( - self.room_id, - "org.matrix.custom_type", - {"body": "hello"}, - tok=self.tok2, - ) - self._check_unread_count(4) - - # Check that edits don't increase the unread counter. - self.helper.send_event( - room_id=self.room_id, - type=EventTypes.Message, - content={ - "body": "hello", - "msgtype": "m.text", - "m.relates_to": {"rel_type": RelationTypes.REPLACE}, - }, - tok=self.tok2, - ) - self._check_unread_count(4) - - # Check that notices don't increase the unread counter. - self.helper.send_event( - room_id=self.room_id, - type=EventTypes.Message, - content={"body": "hello", "msgtype": "m.notice"}, - tok=self.tok2, - ) - self._check_unread_count(4) - - # Check that tombstone events changes increase the unread counter. - self.helper.send_state( - self.room_id, - EventTypes.Tombstone, - {"replacement_room": "!someroom:test"}, - tok=self.tok2, - ) - self._check_unread_count(5) - - def _check_unread_count(self, expected_count: int): - """Syncs and compares the unread count with the expected value.""" - - channel = self.make_request( - "GET", - self.url % self.next_batch, - access_token=self.tok, - ) - - self.assertEqual(channel.code, 200, channel.json_body) - - room_entry = channel.json_body["rooms"]["join"][self.room_id] - self.assertEqual( - room_entry["org.matrix.msc2654.unread_count"], - expected_count, - room_entry, - ) - - # Store the next batch for the next request. - self.next_batch = channel.json_body["next_batch"] - - -class SyncCacheTestCase(unittest.HomeserverTestCase): - servlets = [ - synapse.rest.admin.register_servlets, - login.register_servlets, - sync.register_servlets, - ] - - def test_noop_sync_does_not_tightloop(self): - """If the sync times out, we shouldn't cache the result - - Essentially a regression test for #8518. - """ - self.user_id = self.register_user("kermit", "monkey") - self.tok = self.login("kermit", "monkey") - - # we should immediately get an initial sync response - channel = self.make_request("GET", "/sync", access_token=self.tok) - self.assertEqual(channel.code, 200, channel.json_body) - - # now, make an incremental sync request, with a timeout - next_batch = channel.json_body["next_batch"] - channel = self.make_request( - "GET", - f"/sync?since={next_batch}&timeout=10000", - access_token=self.tok, - await_result=False, - ) - # that should block for 10 seconds - with self.assertRaises(TimedOutException): - channel.await_result(timeout_ms=9900) - channel.await_result(timeout_ms=200) - self.assertEqual(channel.code, 200, channel.json_body) - - # we expect the next_batch in the result to be the same as before - self.assertEqual(channel.json_body["next_batch"], next_batch) - - # another incremental sync should also block. - channel = self.make_request( - "GET", - f"/sync?since={next_batch}&timeout=10000", - access_token=self.tok, - await_result=False, - ) - # that should block for 10 seconds - with self.assertRaises(TimedOutException): - channel.await_result(timeout_ms=9900) - channel.await_result(timeout_ms=200) - self.assertEqual(channel.code, 200, channel.json_body) diff --git a/tests/rest/client/v2_alpha/test_upgrade_room.py b/tests/rest/client/v2_alpha/test_upgrade_room.py deleted file mode 100644 index 72f976d8e2..0000000000 --- a/tests/rest/client/v2_alpha/test_upgrade_room.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright 2021 The Matrix.org Foundation C.I.C. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# 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. -from typing import Optional - -from synapse.config.server import DEFAULT_ROOM_VERSION -from synapse.rest import admin -from synapse.rest.client import login, room, room_upgrade_rest_servlet - -from tests import unittest -from tests.server import FakeChannel - - -class UpgradeRoomTest(unittest.HomeserverTestCase): - servlets = [ - admin.register_servlets, - login.register_servlets, - room.register_servlets, - room_upgrade_rest_servlet.register_servlets, - ] - - def prepare(self, reactor, clock, hs): - self.store = hs.get_datastore() - self.handler = hs.get_user_directory_handler() - - self.creator = self.register_user("creator", "pass") - self.creator_token = self.login(self.creator, "pass") - - self.other = self.register_user("user", "pass") - self.other_token = self.login(self.other, "pass") - - self.room_id = self.helper.create_room_as(self.creator, tok=self.creator_token) - self.helper.join(self.room_id, self.other, tok=self.other_token) - - def _upgrade_room(self, token: Optional[str] = None) -> FakeChannel: - # We never want a cached response. - self.reactor.advance(5 * 60 + 1) - - return self.make_request( - "POST", - "/_matrix/client/r0/rooms/%s/upgrade" % self.room_id, - # This will upgrade a room to the same version, but that's fine. - content={"new_version": DEFAULT_ROOM_VERSION}, - access_token=token or self.creator_token, - ) - - def test_upgrade(self): - """ - Upgrading a room should work fine. - """ - channel = self._upgrade_room() - self.assertEquals(200, channel.code, channel.result) - self.assertIn("replacement_room", channel.json_body) - - def test_not_in_room(self): - """ - Upgrading a room should work fine. - """ - # THe user isn't in the room. - roomless = self.register_user("roomless", "pass") - roomless_token = self.login(roomless, "pass") - - channel = self._upgrade_room(roomless_token) - self.assertEquals(403, channel.code, channel.result) - - def test_power_levels(self): - """ - Another user can upgrade the room if their power level is increased. - """ - # The other user doesn't have the proper power level. - channel = self._upgrade_room(self.other_token) - self.assertEquals(403, channel.code, channel.result) - - # Increase the power levels so that this user can upgrade. - power_levels = self.helper.get_state( - self.room_id, - "m.room.power_levels", - tok=self.creator_token, - ) - power_levels["users"][self.other] = 100 - self.helper.send_state( - self.room_id, - "m.room.power_levels", - body=power_levels, - tok=self.creator_token, - ) - - # The upgrade should succeed! - channel = self._upgrade_room(self.other_token) - self.assertEquals(200, channel.code, channel.result) - - def test_power_levels_user_default(self): - """ - Another user can upgrade the room if the default power level for users is increased. - """ - # The other user doesn't have the proper power level. - channel = self._upgrade_room(self.other_token) - self.assertEquals(403, channel.code, channel.result) - - # Increase the power levels so that this user can upgrade. - power_levels = self.helper.get_state( - self.room_id, - "m.room.power_levels", - tok=self.creator_token, - ) - power_levels["users_default"] = 100 - self.helper.send_state( - self.room_id, - "m.room.power_levels", - body=power_levels, - tok=self.creator_token, - ) - - # The upgrade should succeed! - channel = self._upgrade_room(self.other_token) - self.assertEquals(200, channel.code, channel.result) - - def test_power_levels_tombstone(self): - """ - Another user can upgrade the room if they can send the tombstone event. - """ - # The other user doesn't have the proper power level. - channel = self._upgrade_room(self.other_token) - self.assertEquals(403, channel.code, channel.result) - - # Increase the power levels so that this user can upgrade. - power_levels = self.helper.get_state( - self.room_id, - "m.room.power_levels", - tok=self.creator_token, - ) - power_levels["events"]["m.room.tombstone"] = 0 - self.helper.send_state( - self.room_id, - "m.room.power_levels", - body=power_levels, - tok=self.creator_token, - ) - - # The upgrade should succeed! - channel = self._upgrade_room(self.other_token) - self.assertEquals(200, channel.code, channel.result) - - power_levels = self.helper.get_state( - self.room_id, - "m.room.power_levels", - tok=self.creator_token, - ) - self.assertNotIn(self.other, power_levels["users"]) |