Skip to content

Commit 58deef5

Browse files
authored
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).
1 parent d427403 commit 58deef5

File tree

5 files changed

+108
-2
lines changed

5 files changed

+108
-2
lines changed

changelog.d/17847.bugfix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a bug in the admin redact endpoint where the background task would not run if a worker was specified in
2+
the config option `run_background_tasks_on`.

docs/admin_api/user_admin_api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,9 @@ _Added in Synapse 1.72.0._
13651365

13661366
## Redact all the events of a user
13671367

1368+
This endpoint allows an admin to redact the events of a given user. There are no restrictions on redactions for a
1369+
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.
1370+
13681371
The API is
13691372
```
13701373
POST /_synapse/admin/v1/user/$user_id/redact

synapse/handlers/admin.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ def __init__(self, hs: "HomeServer"):
7373
self._redact_all_events, REDACT_ALL_EVENTS_ACTION_NAME
7474
)
7575

76+
self.hs = hs
77+
7678
async def get_redact_task(self, redact_id: str) -> Optional[ScheduledTask]:
7779
"""Get the current status of an active redaction process
7880
@@ -423,8 +425,10 @@ async def _redact_all_events(
423425
user_id = task.params.get("user_id")
424426
assert user_id is not None
425427

428+
# puppet the user if they're ours, otherwise use admin to redact
426429
requester = create_requester(
427-
user_id, authenticated_entity=admin.user.to_string()
430+
user_id if self.hs.is_mine_id(user_id) else admin.user.to_string(),
431+
authenticated_entity=admin.user.to_string(),
428432
)
429433

430434
reason = task.params.get("reason")

synapse/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ class HomeServer(metaclass=abc.ABCMeta):
249249
"""
250250

251251
REQUIRED_ON_BACKGROUND_TASK_STARTUP = [
252+
"admin",
252253
"account_validity",
253254
"auth",
254255
"deactivate_account",

tests/rest/admin/test_user.py

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import hmac
2424
import json
2525
import os
26+
import time
2627
import urllib.parse
2728
from binascii import unhexlify
2829
from http import HTTPStatus
@@ -56,6 +57,7 @@
5657
from synapse.util import Clock
5758

5859
from tests import unittest
60+
from tests.replication._base import BaseMultiWorkerStreamTestCase
5961
from tests.test_utils import SMALL_PNG
6062
from tests.unittest import override_config
6163

@@ -5127,7 +5129,6 @@ def test_redact_messages_all_rooms(self) -> None:
51275129
"""
51285130
Test that request to redact events in all rooms user is member of is successful
51295131
"""
5130-
51315132
# join rooms, send some messages
51325133
originals = []
51335134
for rm in [self.rm1, self.rm2, self.rm3]:
@@ -5404,3 +5405,98 @@ def test_admin_redact_works_if_user_kicked_or_banned(self) -> None:
54045405
matches.append((event_id, event))
54055406
# we redacted 6 messages
54065407
self.assertEqual(len(matches), 6)
5408+
5409+
5410+
class UserRedactionBackgroundTaskTestCase(BaseMultiWorkerStreamTestCase):
5411+
servlets = [
5412+
synapse.rest.admin.register_servlets,
5413+
login.register_servlets,
5414+
admin.register_servlets,
5415+
room.register_servlets,
5416+
sync.register_servlets,
5417+
]
5418+
5419+
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
5420+
self.admin = self.register_user("thomas", "pass", True)
5421+
self.admin_tok = self.login("thomas", "pass")
5422+
5423+
self.bad_user = self.register_user("teresa", "pass")
5424+
self.bad_user_tok = self.login("teresa", "pass")
5425+
5426+
# create rooms - room versions 11+ store the `redacts` key in content while
5427+
# earlier ones don't so we use a mix of room versions
5428+
self.rm1 = self.helper.create_room_as(
5429+
self.admin, tok=self.admin_tok, room_version="7"
5430+
)
5431+
self.rm2 = self.helper.create_room_as(self.admin, tok=self.admin_tok)
5432+
self.rm3 = self.helper.create_room_as(
5433+
self.admin, tok=self.admin_tok, room_version="11"
5434+
)
5435+
5436+
@override_config({"run_background_tasks_on": "worker1"})
5437+
def test_redact_messages_all_rooms(self) -> None:
5438+
"""
5439+
Test that redact task successfully runs when `run_background_tasks_on` is specified
5440+
"""
5441+
self.make_worker_hs(
5442+
"synapse.app.generic_worker",
5443+
extra_config={
5444+
"worker_name": "worker1",
5445+
"run_background_tasks_on": "worker1",
5446+
"redis": {"enabled": True},
5447+
},
5448+
)
5449+
5450+
# join rooms, send some messages
5451+
original_event_ids = set()
5452+
for rm in [self.rm1, self.rm2, self.rm3]:
5453+
join = self.helper.join(rm, self.bad_user, tok=self.bad_user_tok)
5454+
original_event_ids.add(join["event_id"])
5455+
for i in range(15):
5456+
event = {"body": f"hello{i}", "msgtype": "m.text"}
5457+
res = self.helper.send_event(
5458+
rm, "m.room.message", event, tok=self.bad_user_tok, expect_code=200
5459+
)
5460+
original_event_ids.add(res["event_id"])
5461+
5462+
# redact all events in all rooms
5463+
channel = self.make_request(
5464+
"POST",
5465+
f"/_synapse/admin/v1/user/{self.bad_user}/redact",
5466+
content={"rooms": []},
5467+
access_token=self.admin_tok,
5468+
)
5469+
self.assertEqual(channel.code, 200)
5470+
id = channel.json_body.get("redact_id")
5471+
5472+
timeout_s = 10
5473+
start_time = time.time()
5474+
redact_result = ""
5475+
while redact_result != "complete":
5476+
if start_time + timeout_s < time.time():
5477+
self.fail("Timed out waiting for redactions.")
5478+
5479+
channel2 = self.make_request(
5480+
"GET",
5481+
f"/_synapse/admin/v1/user/redact_status/{id}",
5482+
access_token=self.admin_tok,
5483+
)
5484+
redact_result = channel2.json_body["status"]
5485+
if redact_result == "failed":
5486+
self.fail("Redaction task failed.")
5487+
5488+
redaction_ids = set()
5489+
for rm in [self.rm1, self.rm2, self.rm3]:
5490+
filter = json.dumps({"types": [EventTypes.Redaction]})
5491+
channel = self.make_request(
5492+
"GET",
5493+
f"rooms/{rm}/messages?filter={filter}&limit=50",
5494+
access_token=self.admin_tok,
5495+
)
5496+
self.assertEqual(channel.code, 200)
5497+
5498+
for event in channel.json_body["chunk"]:
5499+
if event["type"] == "m.room.redaction":
5500+
redaction_ids.add(event["redacts"])
5501+
5502+
self.assertIncludes(redaction_ids, original_event_ids, exact=True)

0 commit comments

Comments
 (0)