Add admin handler to list of handlers used for background tasks (#17847)

Fixes #17823

While we're at it, makes a change where the redactions are sent as the
admin if the user is not a member of the server (otherwise these fail
with a "User must be our own" message).
This commit is contained in:
Shay 2024-10-29 11:50:13 -07:00 committed by GitHub
parent d427403c67
commit 58deef5eba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 108 additions and 2 deletions

2
changelog.d/17847.bugfix Normal file
View File

@ -0,0 +1,2 @@
Fix a bug in the admin redact endpoint where the background task would not run if a worker was specified in
the config option `run_background_tasks_on`.

View File

@ -1365,6 +1365,9 @@ _Added in Synapse 1.72.0._
## Redact all the events of a user ## Redact all the events of a user
This endpoint allows an admin to redact the events of a given user. There are no restrictions on redactions for a
local user. By default, we puppet the user who sent the message to redact it themselves. Redactions for non-local users are issued using the admin user, and will fail in rooms where the admin user is not admin/does not have the specified power level to issue redactions.
The API is The API is
``` ```
POST /_synapse/admin/v1/user/$user_id/redact POST /_synapse/admin/v1/user/$user_id/redact

View File

@ -73,6 +73,8 @@ class AdminHandler:
self._redact_all_events, REDACT_ALL_EVENTS_ACTION_NAME self._redact_all_events, REDACT_ALL_EVENTS_ACTION_NAME
) )
self.hs = hs
async def get_redact_task(self, redact_id: str) -> Optional[ScheduledTask]: async def get_redact_task(self, redact_id: str) -> Optional[ScheduledTask]:
"""Get the current status of an active redaction process """Get the current status of an active redaction process
@ -423,8 +425,10 @@ class AdminHandler:
user_id = task.params.get("user_id") user_id = task.params.get("user_id")
assert user_id is not None assert user_id is not None
# puppet the user if they're ours, otherwise use admin to redact
requester = create_requester( requester = create_requester(
user_id, authenticated_entity=admin.user.to_string() user_id if self.hs.is_mine_id(user_id) else admin.user.to_string(),
authenticated_entity=admin.user.to_string(),
) )
reason = task.params.get("reason") reason = task.params.get("reason")

View File

@ -249,6 +249,7 @@ class HomeServer(metaclass=abc.ABCMeta):
""" """
REQUIRED_ON_BACKGROUND_TASK_STARTUP = [ REQUIRED_ON_BACKGROUND_TASK_STARTUP = [
"admin",
"account_validity", "account_validity",
"auth", "auth",
"deactivate_account", "deactivate_account",

View File

@ -23,6 +23,7 @@ import hashlib
import hmac import hmac
import json import json
import os import os
import time
import urllib.parse import urllib.parse
from binascii import unhexlify from binascii import unhexlify
from http import HTTPStatus from http import HTTPStatus
@ -56,6 +57,7 @@ from synapse.types import JsonDict, UserID, create_requester
from synapse.util import Clock from synapse.util import Clock
from tests import unittest from tests import unittest
from tests.replication._base import BaseMultiWorkerStreamTestCase
from tests.test_utils import SMALL_PNG from tests.test_utils import SMALL_PNG
from tests.unittest import override_config from tests.unittest import override_config
@ -5127,7 +5129,6 @@ class UserRedactionTestCase(unittest.HomeserverTestCase):
""" """
Test that request to redact events in all rooms user is member of is successful Test that request to redact events in all rooms user is member of is successful
""" """
# join rooms, send some messages # join rooms, send some messages
originals = [] originals = []
for rm in [self.rm1, self.rm2, self.rm3]: for rm in [self.rm1, self.rm2, self.rm3]:
@ -5404,3 +5405,98 @@ class UserRedactionTestCase(unittest.HomeserverTestCase):
matches.append((event_id, event)) matches.append((event_id, event))
# we redacted 6 messages # we redacted 6 messages
self.assertEqual(len(matches), 6) self.assertEqual(len(matches), 6)
class UserRedactionBackgroundTaskTestCase(BaseMultiWorkerStreamTestCase):
servlets = [
synapse.rest.admin.register_servlets,
login.register_servlets,
admin.register_servlets,
room.register_servlets,
sync.register_servlets,
]
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
self.admin = self.register_user("thomas", "pass", True)
self.admin_tok = self.login("thomas", "pass")
self.bad_user = self.register_user("teresa", "pass")
self.bad_user_tok = self.login("teresa", "pass")
# create rooms - room versions 11+ store the `redacts` key in content while
# earlier ones don't so we use a mix of room versions
self.rm1 = self.helper.create_room_as(
self.admin, tok=self.admin_tok, room_version="7"
)
self.rm2 = self.helper.create_room_as(self.admin, tok=self.admin_tok)
self.rm3 = self.helper.create_room_as(
self.admin, tok=self.admin_tok, room_version="11"
)
@override_config({"run_background_tasks_on": "worker1"})
def test_redact_messages_all_rooms(self) -> None:
"""
Test that redact task successfully runs when `run_background_tasks_on` is specified
"""
self.make_worker_hs(
"synapse.app.generic_worker",
extra_config={
"worker_name": "worker1",
"run_background_tasks_on": "worker1",
"redis": {"enabled": True},
},
)
# join rooms, send some messages
original_event_ids = set()
for rm in [self.rm1, self.rm2, self.rm3]:
join = self.helper.join(rm, self.bad_user, tok=self.bad_user_tok)
original_event_ids.add(join["event_id"])
for i in range(15):
event = {"body": f"hello{i}", "msgtype": "m.text"}
res = self.helper.send_event(
rm, "m.room.message", event, tok=self.bad_user_tok, expect_code=200
)
original_event_ids.add(res["event_id"])
# redact all events in all rooms
channel = self.make_request(
"POST",
f"/_synapse/admin/v1/user/{self.bad_user}/redact",
content={"rooms": []},
access_token=self.admin_tok,
)
self.assertEqual(channel.code, 200)
id = channel.json_body.get("redact_id")
timeout_s = 10
start_time = time.time()
redact_result = ""
while redact_result != "complete":
if start_time + timeout_s < time.time():
self.fail("Timed out waiting for redactions.")
channel2 = self.make_request(
"GET",
f"/_synapse/admin/v1/user/redact_status/{id}",
access_token=self.admin_tok,
)
redact_result = channel2.json_body["status"]
if redact_result == "failed":
self.fail("Redaction task failed.")
redaction_ids = set()
for rm in [self.rm1, self.rm2, self.rm3]:
filter = json.dumps({"types": [EventTypes.Redaction]})
channel = self.make_request(
"GET",
f"rooms/{rm}/messages?filter={filter}&limit=50",
access_token=self.admin_tok,
)
self.assertEqual(channel.code, 200)
for event in channel.json_body["chunk"]:
if event["type"] == "m.room.redaction":
redaction_ids.add(event["redacts"])
self.assertIncludes(redaction_ids, original_event_ids, exact=True)