From f347547d32669c285f06fcde74cab28d69fe23e9 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jan 2025 16:05:31 -0700 Subject: [PATCH 01/16] Add report user API from MSC4260 --- synapse/config/experimental.py | 3 + synapse/rest/client/reporting.py | 56 +++++++++++++ synapse/storage/databases/main/room.py | 40 ++++++++++ .../main/delta/88/07_add_user_reports.sql | 21 +++++ tests/rest/client/test_reporting.py | 78 +++++++++++++++++++ 5 files changed, 198 insertions(+) create mode 100644 synapse/storage/schema/main/delta/88/07_add_user_reports.sql diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index 94a25c7ee8..2a482d6360 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -447,3 +447,6 @@ class ExperimentalConfig(Config): # MSC4076: Add `disable_badge_count`` to pusher configuration self.msc4076_enabled: bool = experimental.get("msc4076_enabled", False) + + # MSC4260: Report user API (Client-Server) + self.msc4260_enabled: bool = experimental.get("msc4260_enabled", False) diff --git a/synapse/rest/client/reporting.py b/synapse/rest/client/reporting.py index c5037be8b7..9428fc093e 100644 --- a/synapse/rest/client/reporting.py +++ b/synapse/rest/client/reporting.py @@ -150,6 +150,62 @@ class ReportRoomRestServlet(RestServlet): return 200, {} +class ReportUserRestServlet(RestServlet): + """This endpoint lets clients report a user for abuse. + + Introduced by MSC4260: https://github.com/matrix-org/matrix-spec-proposals/pull/4260 + """ + + # Cast the Iterable to a list so that we can `append` below. + PATTERNS = list( + client_patterns( + "/org.matrix.msc4260/users/(?P[^/]*)/report$", + releases=[], # unstable only + unstable=True, + v1=False, + ) + ) + + def __init__(self, hs: "HomeServer"): + super().__init__() + self.hs = hs + self.auth = hs.get_auth() + self.clock = hs.get_clock() + self.store = hs.get_datastores().main + + class PostBody(RequestBodyModel): + reason: StrictStr + + async def on_POST( + self, request: SynapseRequest, target_user_id: str + ) -> Tuple[int, JsonDict]: + requester = await self.auth.get_user_by_req(request) + user_id = requester.user.to_string() + + body = parse_and_validate_json_object_from_request(request, self.PostBody) + + # We can't deal with non-local users. + if not self.hs.is_mine_id(target_user_id): + raise NotFoundError("User does not belong to this server") + + user = await self.store.get_user_by_id(target_user_id) + if user is None: + # raise NotFoundError("User does not exist") + return 200, {} # hide existence + + await self.store.add_user_report( + target_user_id=target_user_id, + user_id=user_id, + reason=body.reason, + received_ts=self.clock.time_msec(), + ) + + return 200, {} + + def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: ReportEventRestServlet(hs).register(http_server) ReportRoomRestServlet(hs).register(http_server) + + if hs.config.experimental.msc4260_enabled: + ReportUserRestServlet(hs).register(http_server) diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py index d673adba16..794798b4be 100644 --- a/synapse/storage/databases/main/room.py +++ b/synapse/storage/databases/main/room.py @@ -2303,6 +2303,7 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore): self._event_reports_id_gen = IdGenerator(db_conn, "event_reports", "id") self._room_reports_id_gen = IdGenerator(db_conn, "room_reports", "id") + self._user_reports_id_gen = IdGenerator(db_conn, "user_reports", "id") self._instance_name = hs.get_instance_name() @@ -2544,6 +2545,45 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore): ) return next_id + async def add_user_report( + self, + target_user_id: str, + user_id: str, + reason: str, + received_ts: int, + ) -> int: + """Add a user report + + Args: + target_user_id: The user ID being reported. + user_id: User who reported the user. + reason: Description that the user specifies. + received_ts: Time when the user submitted the report (milliseconds). + Returns: + Id of the room report. + """ + next_id = self._user_reports_id_gen.get_next() + await self.db_pool.simple_insert( + table="user_reports", + values={ + "id": next_id, + "received_ts": received_ts, + "target_user_id": target_user_id, + "user_id": user_id, + "reason": reason, + }, + desc="add_user_report", + ) + return next_id + + async def get_user_report_ids(self, target_user_id: str) -> List[str]: + return await self.db_pool.simple_select_onecol( + table="user_reports", + keyvalues={"target_user_id": target_user_id}, + retcol="id", + desc="get_user_report_ids", + ) + async def clear_partial_state_room(self, room_id: str) -> Optional[int]: """Clears the partial state flag for a room. diff --git a/synapse/storage/schema/main/delta/88/07_add_user_reports.sql b/synapse/storage/schema/main/delta/88/07_add_user_reports.sql new file mode 100644 index 0000000000..2521aefc51 --- /dev/null +++ b/synapse/storage/schema/main/delta/88/07_add_user_reports.sql @@ -0,0 +1,21 @@ +-- +-- This file is licensed under the Affero General Public License (AGPL) version 3. +-- +-- Copyright (C) 2025 New Vector, Ltd +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as +-- published by the Free Software Foundation, either version 3 of the +-- License, or (at your option) any later version. +-- +-- See the GNU Affero General Public License for more details: +-- . + +CREATE TABLE user_reports ( + id BIGINT NOT NULL PRIMARY KEY, + received_ts BIGINT NOT NULL, + target_user_id TEXT NOT NULL, + user_id TEXT NOT NULL, + reason TEXT NOT NULL +); +CREATE INDEX user_reports_target_user_id ON user_reports(target_user_id); -- for lookups diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 723553979f..84206680db 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -28,6 +28,7 @@ from synapse.types import JsonDict from synapse.util import Clock from tests import unittest +from tests.unittest import override_config class ReportEventTestCase(unittest.HomeserverTestCase): @@ -201,3 +202,80 @@ class ReportRoomTestCase(unittest.HomeserverTestCase): shorthand=False, ) self.assertEqual(response_status, channel.code, msg=channel.result["body"]) + + +@override_config({"experimental_features": {"msc4260_enabled": True}}) +class ReportUserTestCase(unittest.HomeserverTestCase): + servlets = [ + synapse.rest.admin.register_servlets, + login.register_servlets, + room.register_servlets, + reporting.register_servlets, + ] + + def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None: + self.other_user = self.register_user("user", "pass") + self.other_user_tok = self.login("user", "pass") + + self.target_user_id = self.register_user("target_user", "pass") + self.report_path = f"/_matrix/client/unstable/org.matrix.msc4260/users/{self.target_user_id}/report" + + def test_reason_str(self) -> None: + data = {"reason": "this makes me sad"} + self._assert_status(200, data) + self.assertEqual(1, self.hs.get_datastores().main.get_user_report_ids(self.target_user_id)) + + def test_no_reason(self) -> None: + data = {"not_reason": "for typechecking"} + self._assert_status(400, data) + + def test_reason_nonstring(self) -> None: + data = {"reason": 42} + self._assert_status(400, data) + + def test_reason_null(self) -> None: + data = {"reason": None} + self._assert_status(400, data) + + def test_cannot_report_nonlcoal_user(self) -> None: + """ + Tests that we don't accept event reports for users which aren't local users. + """ + channel = self.make_request( + "POST", + "/_matrix/client/unstable/org.matrix.msc4260/users/@bloop:example.org/report", + {"reason": "i am very sad"}, + access_token=self.other_user_tok, + shorthand=False, + ) + self.assertEqual(404, channel.code, msg=channel.result["body"]) + self.assertEqual( + "User does not belong to this server", + channel.json_body["error"], + msg=channel.result["body"], + ) + + def test_can_report_nonexistent_user(self) -> None: + """ + Tests that we ignore reports for nonexistent users. + """ + target_user_id = f"@bloop:{self.hs.hostname}" + channel = self.make_request( + "POST", + f"/_matrix/client/unstable/org.matrix.msc4260/users/{target_user_id}/report", + {"reason": "i am very sad"}, + access_token=self.other_user_tok, + shorthand=False, + ) + self.assertEqual(200, channel.code, msg=channel.result["body"]) + self.assertEqual(0, self.hs.get_datastores().main.get_user_report_ids(target_user_id)) + + def _assert_status(self, response_status: int, data: JsonDict) -> None: + channel = self.make_request( + "POST", + self.report_path, + data, + access_token=self.other_user_tok, + shorthand=False, + ) + self.assertEqual(response_status, channel.code, msg=channel.result["body"]) From b5f359aa0c1884403df7208adfabf061fccaa738 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jan 2025 16:08:11 -0700 Subject: [PATCH 02/16] changelog --- changelog.d/18120.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/18120.feature diff --git a/changelog.d/18120.feature b/changelog.d/18120.feature new file mode 100644 index 0000000000..8441a0811a --- /dev/null +++ b/changelog.d/18120.feature @@ -0,0 +1 @@ +Add support for the unstable [MSC4260](https://github.com/matrix-org/matrix-spec-proposals/pull/4260) report user API. \ No newline at end of file From f0dcc7a802f729e2c01700370a1990d84d116e99 Mon Sep 17 00:00:00 2001 From: turt2live <1190097+turt2live@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:13:19 +0000 Subject: [PATCH 03/16] Attempt to fix linting --- tests/rest/client/test_reporting.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 84206680db..f6ed28d76f 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -223,7 +223,9 @@ class ReportUserTestCase(unittest.HomeserverTestCase): def test_reason_str(self) -> None: data = {"reason": "this makes me sad"} self._assert_status(200, data) - self.assertEqual(1, self.hs.get_datastores().main.get_user_report_ids(self.target_user_id)) + self.assertEqual( + 1, self.hs.get_datastores().main.get_user_report_ids(self.target_user_id) + ) def test_no_reason(self) -> None: data = {"not_reason": "for typechecking"} @@ -268,7 +270,9 @@ class ReportUserTestCase(unittest.HomeserverTestCase): shorthand=False, ) self.assertEqual(200, channel.code, msg=channel.result["body"]) - self.assertEqual(0, self.hs.get_datastores().main.get_user_report_ids(target_user_id)) + self.assertEqual( + 0, self.hs.get_datastores().main.get_user_report_ids(target_user_id) + ) def _assert_status(self, response_status: int, data: JsonDict) -> None: channel = self.make_request( From 61f2750c9b7e578fee9aae3b46c4a45638e55aa7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jan 2025 16:16:25 -0700 Subject: [PATCH 04/16] kick ci From facf07a568113f6645d1bec47893a340fc70c8c7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jan 2025 17:13:45 -0700 Subject: [PATCH 05/16] Include in /versions --- synapse/rest/client/versions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py index 266a0b835b..caad93104e 100644 --- a/synapse/rest/client/versions.py +++ b/synapse/rest/client/versions.py @@ -174,6 +174,8 @@ class VersionsRestServlet(RestServlet): "org.matrix.simplified_msc3575": msc3575_enabled, # Arbitrary key-value profile fields. "uk.tcpip.msc4133": self.config.experimental.msc4133_enabled, + # MSC4260: Report users API (Client-Server) + "org.matrix.msc4260": self.config.experimental.msc4260_enabled, }, }, ) From 85747b70a7cec36c5f90c32eab3a8dfa5268adfe Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jan 2025 17:13:56 -0700 Subject: [PATCH 06/16] Annotate the tests instead --- tests/rest/client/test_reporting.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index f6ed28d76f..450453d4cb 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -204,7 +204,6 @@ class ReportRoomTestCase(unittest.HomeserverTestCase): self.assertEqual(response_status, channel.code, msg=channel.result["body"]) -@override_config({"experimental_features": {"msc4260_enabled": True}}) class ReportUserTestCase(unittest.HomeserverTestCase): servlets = [ synapse.rest.admin.register_servlets, @@ -220,6 +219,7 @@ class ReportUserTestCase(unittest.HomeserverTestCase): self.target_user_id = self.register_user("target_user", "pass") self.report_path = f"/_matrix/client/unstable/org.matrix.msc4260/users/{self.target_user_id}/report" + @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_reason_str(self) -> None: data = {"reason": "this makes me sad"} self._assert_status(200, data) @@ -227,18 +227,22 @@ class ReportUserTestCase(unittest.HomeserverTestCase): 1, self.hs.get_datastores().main.get_user_report_ids(self.target_user_id) ) + @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_no_reason(self) -> None: data = {"not_reason": "for typechecking"} self._assert_status(400, data) + @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_reason_nonstring(self) -> None: data = {"reason": 42} self._assert_status(400, data) + @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_reason_null(self) -> None: data = {"reason": None} self._assert_status(400, data) + @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_cannot_report_nonlcoal_user(self) -> None: """ Tests that we don't accept event reports for users which aren't local users. @@ -257,6 +261,7 @@ class ReportUserTestCase(unittest.HomeserverTestCase): msg=channel.result["body"], ) + @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_can_report_nonexistent_user(self) -> None: """ Tests that we ignore reports for nonexistent users. From e8d102d8c5f7afa4734d139381d77cdd3e183f2d Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 30 Jan 2025 17:30:57 -0700 Subject: [PATCH 07/16] await --- tests/rest/client/test_reporting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 450453d4cb..8934cbeff8 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -220,11 +220,11 @@ class ReportUserTestCase(unittest.HomeserverTestCase): self.report_path = f"/_matrix/client/unstable/org.matrix.msc4260/users/{self.target_user_id}/report" @override_config({"experimental_features": {"msc4260_enabled": True}}) - def test_reason_str(self) -> None: + async def test_reason_str(self) -> None: data = {"reason": "this makes me sad"} self._assert_status(200, data) self.assertEqual( - 1, self.hs.get_datastores().main.get_user_report_ids(self.target_user_id) + 1, await self.hs.get_datastores().main.get_user_report_ids(self.target_user_id) ) @override_config({"experimental_features": {"msc4260_enabled": True}}) @@ -262,7 +262,7 @@ class ReportUserTestCase(unittest.HomeserverTestCase): ) @override_config({"experimental_features": {"msc4260_enabled": True}}) - def test_can_report_nonexistent_user(self) -> None: + async def test_can_report_nonexistent_user(self) -> None: """ Tests that we ignore reports for nonexistent users. """ @@ -276,7 +276,7 @@ class ReportUserTestCase(unittest.HomeserverTestCase): ) self.assertEqual(200, channel.code, msg=channel.result["body"]) self.assertEqual( - 0, self.hs.get_datastores().main.get_user_report_ids(target_user_id) + 0, await self.hs.get_datastores().main.get_user_report_ids(target_user_id) ) def _assert_status(self, response_status: int, data: JsonDict) -> None: From a842c66ecb8dee8e711661e7ecbd9ae8512717cc Mon Sep 17 00:00:00 2001 From: turt2live <1190097+turt2live@users.noreply.github.com> Date: Fri, 31 Jan 2025 00:32:51 +0000 Subject: [PATCH 08/16] Attempt to fix linting --- tests/rest/client/test_reporting.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 8934cbeff8..57e2ff0827 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -224,7 +224,10 @@ class ReportUserTestCase(unittest.HomeserverTestCase): data = {"reason": "this makes me sad"} self._assert_status(200, data) self.assertEqual( - 1, await self.hs.get_datastores().main.get_user_report_ids(self.target_user_id) + 1, + await self.hs.get_datastores().main.get_user_report_ids( + self.target_user_id + ), ) @override_config({"experimental_features": {"msc4260_enabled": True}}) From 44dbcab047b82889c12328c7e99b22a9c4966aea Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 31 Jan 2025 11:29:14 -0700 Subject: [PATCH 09/16] kick ci From 41d185c71cf1da26d71847d00a4dfe873d7efe57 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 31 Jan 2025 12:02:12 -0700 Subject: [PATCH 10/16] Adjust testing --- synapse/storage/databases/main/room.py | 8 ------- tests/rest/client/test_reporting.py | 29 ++++++++++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/synapse/storage/databases/main/room.py b/synapse/storage/databases/main/room.py index 794798b4be..d0c57b29d8 100644 --- a/synapse/storage/databases/main/room.py +++ b/synapse/storage/databases/main/room.py @@ -2576,14 +2576,6 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore): ) return next_id - async def get_user_report_ids(self, target_user_id: str) -> List[str]: - return await self.db_pool.simple_select_onecol( - table="user_reports", - keyvalues={"target_user_id": target_user_id}, - retcol="id", - desc="get_user_report_ids", - ) - async def clear_partial_state_room(self, room_id: str) -> Optional[int]: """Clears the partial state flag for a room. diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 57e2ff0827..2789a20e07 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -220,15 +220,17 @@ class ReportUserTestCase(unittest.HomeserverTestCase): self.report_path = f"/_matrix/client/unstable/org.matrix.msc4260/users/{self.target_user_id}/report" @override_config({"experimental_features": {"msc4260_enabled": True}}) - async def test_reason_str(self) -> None: + def test_reason_str(self) -> None: data = {"reason": "this makes me sad"} self._assert_status(200, data) - self.assertEqual( - 1, - await self.hs.get_datastores().main.get_user_report_ids( - self.target_user_id - ), - ) + + rows = self.get_success(self.hs.get_datastores().main.db_pool.simple_select_onecol( + table="user_reports", + keyvalues={"target_user_id": self.target_user_id}, + retcol="id", + desc="get_user_report_ids", + )) + self.assertEqual(len(rows), 1) @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_no_reason(self) -> None: @@ -265,7 +267,7 @@ class ReportUserTestCase(unittest.HomeserverTestCase): ) @override_config({"experimental_features": {"msc4260_enabled": True}}) - async def test_can_report_nonexistent_user(self) -> None: + def test_can_report_nonexistent_user(self) -> None: """ Tests that we ignore reports for nonexistent users. """ @@ -278,9 +280,14 @@ class ReportUserTestCase(unittest.HomeserverTestCase): shorthand=False, ) self.assertEqual(200, channel.code, msg=channel.result["body"]) - self.assertEqual( - 0, await self.hs.get_datastores().main.get_user_report_ids(target_user_id) - ) + + rows = self.get_success(self.hs.get_datastores().main.db_pool.simple_select_onecol( + table="user_reports", + keyvalues={"target_user_id": self.target_user_id}, + retcol="id", + desc="get_user_report_ids", + )) + self.assertEqual(len(rows), 0) def _assert_status(self, response_status: int, data: JsonDict) -> None: channel = self.make_request( From 133380ff6d916db50a513660af9e1b2f53b5ba0e Mon Sep 17 00:00:00 2001 From: turt2live <1190097+turt2live@users.noreply.github.com> Date: Fri, 31 Jan 2025 19:03:54 +0000 Subject: [PATCH 11/16] Attempt to fix linting --- tests/rest/client/test_reporting.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 2789a20e07..86ca1a3a16 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -224,12 +224,14 @@ class ReportUserTestCase(unittest.HomeserverTestCase): data = {"reason": "this makes me sad"} self._assert_status(200, data) - rows = self.get_success(self.hs.get_datastores().main.db_pool.simple_select_onecol( - table="user_reports", - keyvalues={"target_user_id": self.target_user_id}, - retcol="id", - desc="get_user_report_ids", - )) + rows = self.get_success( + self.hs.get_datastores().main.db_pool.simple_select_onecol( + table="user_reports", + keyvalues={"target_user_id": self.target_user_id}, + retcol="id", + desc="get_user_report_ids", + ) + ) self.assertEqual(len(rows), 1) @override_config({"experimental_features": {"msc4260_enabled": True}}) @@ -281,12 +283,14 @@ class ReportUserTestCase(unittest.HomeserverTestCase): ) self.assertEqual(200, channel.code, msg=channel.result["body"]) - rows = self.get_success(self.hs.get_datastores().main.db_pool.simple_select_onecol( - table="user_reports", - keyvalues={"target_user_id": self.target_user_id}, - retcol="id", - desc="get_user_report_ids", - )) + rows = self.get_success( + self.hs.get_datastores().main.db_pool.simple_select_onecol( + table="user_reports", + keyvalues={"target_user_id": self.target_user_id}, + retcol="id", + desc="get_user_report_ids", + ) + ) self.assertEqual(len(rows), 0) def _assert_status(self, response_status: int, data: JsonDict) -> None: From 6ef7a87dc157f62921b470bcafc6971d24d4cf60 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 31 Jan 2025 12:04:03 -0700 Subject: [PATCH 12/16] kick ci From 79c2a0ac8d6645be2a759da380b16566bfe0e01a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 18 Mar 2025 15:07:31 -0600 Subject: [PATCH 13/16] Move delta --- .../{88/07_add_user_reports.sql => 90/02_add_user_reports.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename synapse/storage/schema/main/delta/{88/07_add_user_reports.sql => 90/02_add_user_reports.sql} (100%) diff --git a/synapse/storage/schema/main/delta/88/07_add_user_reports.sql b/synapse/storage/schema/main/delta/90/02_add_user_reports.sql similarity index 100% rename from synapse/storage/schema/main/delta/88/07_add_user_reports.sql rename to synapse/storage/schema/main/delta/90/02_add_user_reports.sql From 1029a79ee666c24325596a8650a7cf24c4dbe814 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 18 Mar 2025 15:10:08 -0600 Subject: [PATCH 14/16] Unstable -> Stable --- changelog.d/18120.feature | 2 +- synapse/config/experimental.py | 3 --- synapse/rest/client/reporting.py | 11 ++++------- synapse/rest/client/versions.py | 2 -- tests/rest/client/test_reporting.py | 12 +++--------- 5 files changed, 8 insertions(+), 22 deletions(-) diff --git a/changelog.d/18120.feature b/changelog.d/18120.feature index 8441a0811a..15cfabba42 100644 --- a/changelog.d/18120.feature +++ b/changelog.d/18120.feature @@ -1 +1 @@ -Add support for the unstable [MSC4260](https://github.com/matrix-org/matrix-spec-proposals/pull/4260) report user API. \ No newline at end of file +Add support for the [MSC4260 user report API](https://github.com/matrix-org/matrix-spec-proposals/pull/4260). \ No newline at end of file diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py index b192dec9f5..0a963b121a 100644 --- a/synapse/config/experimental.py +++ b/synapse/config/experimental.py @@ -560,6 +560,3 @@ class ExperimentalConfig(Config): # MSC4076: Add `disable_badge_count`` to pusher configuration self.msc4076_enabled: bool = experimental.get("msc4076_enabled", False) - - # MSC4260: Report user API (Client-Server) - self.msc4260_enabled: bool = experimental.get("msc4260_enabled", False) diff --git a/synapse/rest/client/reporting.py b/synapse/rest/client/reporting.py index 9428fc093e..17f4f12033 100644 --- a/synapse/rest/client/reporting.py +++ b/synapse/rest/client/reporting.py @@ -156,12 +156,11 @@ class ReportUserRestServlet(RestServlet): Introduced by MSC4260: https://github.com/matrix-org/matrix-spec-proposals/pull/4260 """ - # Cast the Iterable to a list so that we can `append` below. PATTERNS = list( client_patterns( - "/org.matrix.msc4260/users/(?P[^/]*)/report$", - releases=[], # unstable only - unstable=True, + "/users/(?P[^/]*)/report$", + releases=("v3",), + unstable=False, v1=False, ) ) @@ -206,6 +205,4 @@ class ReportUserRestServlet(RestServlet): def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: ReportEventRestServlet(hs).register(http_server) ReportRoomRestServlet(hs).register(http_server) - - if hs.config.experimental.msc4260_enabled: - ReportUserRestServlet(hs).register(http_server) + ReportUserRestServlet(hs).register(http_server) diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py index caad93104e..266a0b835b 100644 --- a/synapse/rest/client/versions.py +++ b/synapse/rest/client/versions.py @@ -174,8 +174,6 @@ class VersionsRestServlet(RestServlet): "org.matrix.simplified_msc3575": msc3575_enabled, # Arbitrary key-value profile fields. "uk.tcpip.msc4133": self.config.experimental.msc4133_enabled, - # MSC4260: Report users API (Client-Server) - "org.matrix.msc4260": self.config.experimental.msc4260_enabled, }, }, ) diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 86ca1a3a16..7bc6150e67 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -217,9 +217,8 @@ class ReportUserTestCase(unittest.HomeserverTestCase): self.other_user_tok = self.login("user", "pass") self.target_user_id = self.register_user("target_user", "pass") - self.report_path = f"/_matrix/client/unstable/org.matrix.msc4260/users/{self.target_user_id}/report" + self.report_path = f"/_matrix/client/v3/users/{self.target_user_id}/report" - @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_reason_str(self) -> None: data = {"reason": "this makes me sad"} self._assert_status(200, data) @@ -234,29 +233,25 @@ class ReportUserTestCase(unittest.HomeserverTestCase): ) self.assertEqual(len(rows), 1) - @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_no_reason(self) -> None: data = {"not_reason": "for typechecking"} self._assert_status(400, data) - @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_reason_nonstring(self) -> None: data = {"reason": 42} self._assert_status(400, data) - @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_reason_null(self) -> None: data = {"reason": None} self._assert_status(400, data) - @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_cannot_report_nonlcoal_user(self) -> None: """ Tests that we don't accept event reports for users which aren't local users. """ channel = self.make_request( "POST", - "/_matrix/client/unstable/org.matrix.msc4260/users/@bloop:example.org/report", + "/_matrix/client/v3/users/@bloop:example.org/report", {"reason": "i am very sad"}, access_token=self.other_user_tok, shorthand=False, @@ -268,7 +263,6 @@ class ReportUserTestCase(unittest.HomeserverTestCase): msg=channel.result["body"], ) - @override_config({"experimental_features": {"msc4260_enabled": True}}) def test_can_report_nonexistent_user(self) -> None: """ Tests that we ignore reports for nonexistent users. @@ -276,7 +270,7 @@ class ReportUserTestCase(unittest.HomeserverTestCase): target_user_id = f"@bloop:{self.hs.hostname}" channel = self.make_request( "POST", - f"/_matrix/client/unstable/org.matrix.msc4260/users/{target_user_id}/report", + f"/_matrix/client/v3/users/{target_user_id}/report", {"reason": "i am very sad"}, access_token=self.other_user_tok, shorthand=False, From 6ce8cc06c62004c606868b402ccf1ebe8f88c60c Mon Sep 17 00:00:00 2001 From: turt2live <1190097+turt2live@users.noreply.github.com> Date: Tue, 18 Mar 2025 21:12:37 +0000 Subject: [PATCH 15/16] Attempt to fix linting --- tests/rest/client/test_reporting.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/rest/client/test_reporting.py b/tests/rest/client/test_reporting.py index 7bc6150e67..80281a2e75 100644 --- a/tests/rest/client/test_reporting.py +++ b/tests/rest/client/test_reporting.py @@ -28,7 +28,6 @@ from synapse.types import JsonDict from synapse.util import Clock from tests import unittest -from tests.unittest import override_config class ReportEventTestCase(unittest.HomeserverTestCase): From 42b4207134b044b4f3fee5a1ebd25600822312e7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 18 Mar 2025 15:12:49 -0600 Subject: [PATCH 16/16] Empty commit to fix CI