mirror of
https://github.com/element-hq/synapse.git
synced 2025-12-05 01:10:13 +00:00
Add an option to issue redactions as admin user on admin redaction endpoint (#18671)
Currently the [admin redaction endpoint](https://element-hq.github.io/synapse/latest/admin_api/user_admin_api.html#redact-all-the-events-of-a-user) defaults to puppeting the user being redacted. This PR adds an optional param `use_admin`, which when provided issues the redactions as the admin user instead.
This commit is contained in:
1
changelog.d/18671.feature
Normal file
1
changelog.d/18671.feature
Normal file
@@ -0,0 +1 @@
|
||||
Add an option to issue redactions as admin user on via the [admin redaction endpoint](https://element-hq.github.io/synapse/latest/admin_api/user_admin_api.html#redact-all-the-events-of-a-user).
|
||||
@@ -1227,7 +1227,7 @@ See also the
|
||||
|
||||
## Controlling whether a user is shadow-banned
|
||||
|
||||
Shadow-banning is a useful tool for moderating malicious or egregiously abusive users.
|
||||
Shadow-banning is a useful tool for moderating malicious or egregiously abusive users.
|
||||
A shadow-banned users receives successful responses to their client-server API requests,
|
||||
but the events are not propagated into rooms. This can be an effective tool as it
|
||||
(hopefully) takes longer for the user to realise they are being moderated before
|
||||
@@ -1464,8 +1464,11 @@ _Added in Synapse 1.72.0._
|
||||
|
||||
## 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.
|
||||
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. An option
|
||||
is provided to override the default and allow the admin to issue the redactions in all cases.
|
||||
|
||||
The API is
|
||||
```
|
||||
@@ -1475,7 +1478,7 @@ POST /_synapse/admin/v1/user/$user_id/redact
|
||||
"rooms": ["!roomid1", "!roomid2"]
|
||||
}
|
||||
```
|
||||
If an empty list is provided as the key for `rooms`, all events in all the rooms the user is member of will be redacted,
|
||||
If an empty list is provided as the key for `rooms`, all events in all the rooms the user is member of will be redacted,
|
||||
otherwise all the events in the rooms provided in the request will be redacted.
|
||||
|
||||
The API starts redaction process running, and returns immediately with a JSON body with
|
||||
@@ -1501,7 +1504,10 @@ The following JSON body parameter must be provided:
|
||||
The following JSON body parameters are optional:
|
||||
|
||||
- `reason` - Reason the redaction is being requested, ie "spam", "abuse", etc. This will be included in each redaction event, and be visible to users.
|
||||
- `limit` - a limit on the number of the user's events to search for ones that can be redacted (events are redacted newest to oldest) in each room, defaults to 1000 if not provided
|
||||
- `limit` - a limit on the number of the user's events to search for ones that can be redacted (events are redacted newest to oldest) in each room, defaults to 1000 if not provided.
|
||||
- `use_admin` - If set to `true`, the admin user is used to issue the redactions, rather than puppeting the user. Useful
|
||||
when the admin is also the moderator of the rooms that require redactions. Note that the redactions will fail in rooms
|
||||
where the admin does not have the sufficient power level to issue the redactions.
|
||||
|
||||
_Added in Synapse 1.116.0._
|
||||
|
||||
|
||||
@@ -358,6 +358,7 @@ class AdminHandler:
|
||||
user_id: str,
|
||||
rooms: list,
|
||||
requester: JsonMapping,
|
||||
use_admin: bool,
|
||||
reason: Optional[str],
|
||||
limit: Optional[int],
|
||||
) -> str:
|
||||
@@ -368,6 +369,7 @@ class AdminHandler:
|
||||
user_id: the user ID of the user whose events should be redacted
|
||||
rooms: the rooms in which to redact the user's events
|
||||
requester: the user requesting the events
|
||||
use_admin: whether to use the admin account to issue the redactions
|
||||
reason: reason for requesting the redaction, ie spam, etc
|
||||
limit: limit on the number of events in each room to redact
|
||||
|
||||
@@ -395,6 +397,7 @@ class AdminHandler:
|
||||
"rooms": rooms,
|
||||
"requester": requester,
|
||||
"user_id": user_id,
|
||||
"use_admin": use_admin,
|
||||
"reason": reason,
|
||||
"limit": limit,
|
||||
},
|
||||
@@ -426,9 +429,17 @@ class AdminHandler:
|
||||
user_id = task.params.get("user_id")
|
||||
assert user_id is not None
|
||||
|
||||
# puppet the user if they're ours, otherwise use admin to redact
|
||||
use_admin = task.params.get("use_admin", False)
|
||||
|
||||
# default to puppeting the user unless they are not local or it's been requested to
|
||||
# use the admin user to issue the redactions
|
||||
requester_id = (
|
||||
admin.user.to_string()
|
||||
if use_admin or not self.hs.is_mine_id(user_id)
|
||||
else user_id
|
||||
)
|
||||
requester = create_requester(
|
||||
user_id if self.hs.is_mine_id(user_id) else admin.user.to_string(),
|
||||
requester_id,
|
||||
authenticated_entity=admin.user.to_string(),
|
||||
)
|
||||
|
||||
|
||||
@@ -1414,7 +1414,7 @@ class RedactUser(RestServlet):
|
||||
"""
|
||||
Redact all the events of a given user in the given rooms or if empty dict is provided
|
||||
then all events in all rooms user is member of. Kicks off a background process and
|
||||
returns an id that can be used to check on the progress of the redaction progress
|
||||
returns an id that can be used to check on the progress of the redaction progress.
|
||||
"""
|
||||
|
||||
PATTERNS = admin_patterns("/user/(?P<user_id>[^/]*)/redact")
|
||||
@@ -1428,6 +1428,7 @@ class RedactUser(RestServlet):
|
||||
rooms: List[StrictStr]
|
||||
reason: Optional[StrictStr]
|
||||
limit: Optional[StrictInt]
|
||||
use_admin: Optional[StrictBool]
|
||||
|
||||
async def on_POST(
|
||||
self, request: SynapseRequest, user_id: str
|
||||
@@ -1455,8 +1456,12 @@ class RedactUser(RestServlet):
|
||||
)
|
||||
rooms = current_rooms + banned_rooms
|
||||
|
||||
use_admin = body.use_admin
|
||||
if not use_admin:
|
||||
use_admin = False
|
||||
|
||||
redact_id = await self.admin_handler.start_redact_events(
|
||||
user_id, rooms, requester.serialize(), body.reason, limit
|
||||
user_id, rooms, requester.serialize(), use_admin, body.reason, limit
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"redact_id": redact_id}
|
||||
|
||||
@@ -5667,6 +5667,54 @@ class UserRedactionTestCase(unittest.HomeserverTestCase):
|
||||
matched.append(event_id)
|
||||
self.assertEqual(len(matched), len(originals))
|
||||
|
||||
def test_use_admin_param_for_redactions(self) -> None:
|
||||
"""
|
||||
Test that if the `use_admin` param is set to true, the admin user is used to issue
|
||||
the redactions and that they succeed in a room where the admin user has sufficient
|
||||
power to issue redactions
|
||||
"""
|
||||
|
||||
originals = []
|
||||
join = self.helper.join(self.rm1, self.bad_user, tok=self.bad_user_tok)
|
||||
originals.append(join["event_id"])
|
||||
for i in range(15):
|
||||
event = {"body": f"hello{i}", "msgtype": "m.text"}
|
||||
res = self.helper.send_event(
|
||||
self.rm1, "m.room.message", event, tok=self.bad_user_tok
|
||||
)
|
||||
originals.append(res["event_id"])
|
||||
|
||||
# redact messages
|
||||
channel = self.make_request(
|
||||
"POST",
|
||||
f"/_synapse/admin/v1/user/{self.bad_user}/redact",
|
||||
content={"rooms": [self.rm1], "use_admin": True},
|
||||
access_token=self.admin_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200)
|
||||
|
||||
# messages are redacted, and redactions are issued by the admin user
|
||||
filter = json.dumps({"types": [EventTypes.Redaction]})
|
||||
channel = self.make_request(
|
||||
"GET",
|
||||
f"rooms/{self.rm1}/messages?filter={filter}&limit=50",
|
||||
access_token=self.admin_tok,
|
||||
)
|
||||
self.assertEqual(channel.code, 200)
|
||||
|
||||
matches = []
|
||||
for event in channel.json_body["chunk"]:
|
||||
for event_id in originals:
|
||||
if event["type"] == "m.room.redaction" and event["redacts"] == event_id:
|
||||
matches.append((event_id, event))
|
||||
# we redacted 16 messages
|
||||
self.assertEqual(len(matches), 16)
|
||||
|
||||
for redaction_tuple in matches:
|
||||
redaction = redaction_tuple[1]
|
||||
if redaction["sender"] != self.admin:
|
||||
self.fail("Redaction was not issued by admin account")
|
||||
|
||||
|
||||
class UserRedactionBackgroundTaskTestCase(BaseMultiWorkerStreamTestCase):
|
||||
servlets = [
|
||||
|
||||
Reference in New Issue
Block a user