Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Admin API to delete media for a specific user #10558

Merged
merged 6 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/10558.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Admin API to delete several media for a specific user.
6 changes: 6 additions & 0 deletions docs/admin_api/media_admin_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [Delete local media](#delete-local-media)
* [Delete a specific local media](#delete-a-specific-local-media)
* [Delete local media by date or size](#delete-local-media-by-date-or-size)
* [Delete media of a user](#delete-media-of-a-user)
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
- [Purge Remote Media API](#purge-remote-media-api)

# Querying media
Expand Down Expand Up @@ -281,6 +282,11 @@ The following fields are returned in the JSON response body:
* `deleted_media`: an array of strings - List of deleted `media_id`
* `total`: integer - Total number of deleted `media_id`

## Delete media of a user

Details how to delete multiple media of a user can you find in
[User Admin API](user_admin_api.md#delete-media-of-a-user).
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

# Purge Remote Media API

The purge remote media API allows server admins to purge old cached remote media.
Expand Down
52 changes: 48 additions & 4 deletions docs/admin_api/user_admin_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,9 @@ The following fields are returned in the JSON response body:
- `joined_rooms` - An array of `room_id`.
- `total` - Number of rooms.

## User media

## List media of a user
### List media of a user
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be "uploaded by" as well, but I don't know if we have other links to it

Gets a list of all local media that a specific `user_id` has created.
By default, the response is ordered by descending creation date and ascending media ID.
The newest media is on top. You can change the order with parameters
Expand Down Expand Up @@ -543,21 +544,64 @@ The following fields are returned in the JSON response body:

- `media` - An array of objects, each containing information about a media.
Media objects contain the following fields:

- `created_ts` - integer - Timestamp when the content was uploaded in ms.
- `last_access_ts` - integer - Timestamp when the content was last accessed in ms.
- `media_id` - string - The id used to refer to the media.
- `media_length` - integer - Length of the media in bytes.
- `media_type` - string - The MIME-type of the media.
- `quarantined_by` - string - The user ID that initiated the quarantine request
for this media.

- `safe_from_quarantine` - bool - Status if this media is safe from quarantining.
- `upload_name` - string - The name the media was uploaded with.

- `next_token`: integer - Indication for pagination. See above.
- `total` - integer - Total number of media.

### Delete media of a user
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

This API deletes the *local* media from the disk of your own server
that a specific `user_id` has created. This includes any local thumbnails.

This API will not affect media that has been uploaded to external
media repositories (e.g https://github.com/turt2live/matrix-media-repo/).

By default, the API delete ordered by descending creation date and ascending media ID.
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
The newest media is on top. You can change the order with parameters
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
`order_by` and `dir`. If no `limit` is set the API delete `100` files per request.
dklimpel marked this conversation as resolved.
Show resolved Hide resolved

The API is:

```
DELETE /_synapse/admin/v1/users/<user_id>/media
```

To use it, you will need to authenticate by providing an `access_token` for a
server admin: [Admin API](../usage/administration/admin_api)

A response body like the following is returned:

```json
{
"deleted_media": [
"abcdefghijklmnopqrstuvwx"
],
"total": 1
}
```

The following fields are returned in the JSON response body:

* `deleted_media`: an array of strings - List of deleted `media_id`
* `total`: integer - Total number of deleted `media_id`

**Note**: There is no `next_token`. This is not useful for deleting media, because
after deleting media the remaining media have a new order.

**Parameters**

This API has the same parameters like [List media of a user](#list-media-of-a-user).
dklimpel marked this conversation as resolved.
Show resolved Hide resolved
With the parameters you can for example limit the number of files to delete at once or
delete largest/smallest or newest/oldest files first.

## Login as a user

Get an access token that can be used to authenticate as that user. Useful for
Expand Down
4 changes: 3 additions & 1 deletion synapse/rest/admin/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@ async def on_DELETE(

logging.info("Deleting local media by ID: %s", media_id)

deleted_media, total = await self.media_repository.delete_local_media(media_id)
deleted_media, total = await self.media_repository.delete_local_media_ids(
[media_id]
)
return 200, {"deleted_media": deleted_media, "total": total}


Expand Down
72 changes: 70 additions & 2 deletions synapse/rest/admin/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,10 +811,10 @@ async def on_GET(
class UserMediaRestServlet(RestServlet):
"""
Gets information about all uploaded local media for a specific `user_id`.
With DELETE request you can delete all this media.

Example:
http://localhost:8008/_synapse/admin/v1/users/
@user:server/media
http://localhost:8008/_synapse/admin/v1/users/@user:server/media

Args:
The parameters `from` and `limit` are required for pagination.
Expand All @@ -830,6 +830,7 @@ def __init__(self, hs: "HomeServer"):
self.is_mine = hs.is_mine
self.auth = hs.get_auth()
self.store = hs.get_datastore()
self.media_repository = hs.get_media_repository()

async def on_GET(
self, request: SynapseRequest, user_id: str
Expand Down Expand Up @@ -898,6 +899,73 @@ async def on_GET(

return 200, ret

async def on_DELETE(
self, request: SynapseRequest, user_id: str
) -> Tuple[int, JsonDict]:
# This will always be set by the time Twisted calls us.
assert request.args is not None

await assert_requester_is_admin(self.auth, request)

if not self.is_mine(UserID.from_string(user_id)):
raise SynapseError(400, "Can only lookup local users")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise SynapseError(400, "Can only lookup local users")
raise SynapseError(400, "Can only look up local users")


user = await self.store.get_user_by_id(user_id)
if user is None:
raise NotFoundError("Unknown user")

start = parse_integer(request, "from", default=0)
limit = parse_integer(request, "limit", default=100)

if start < 0:
raise SynapseError(
400,
"Query parameter from must be a string representing a positive integer.",
errcode=Codes.INVALID_PARAM,
)

if limit < 0:
raise SynapseError(
400,
"Query parameter limit must be a string representing a positive integer.",
errcode=Codes.INVALID_PARAM,
)

# If neither `order_by` nor `dir` is set, set the default order
# to newest media is on top for backward compatibility.
if b"order_by" not in request.args and b"dir" not in request.args:
order_by = MediaSortOrder.CREATED_TS.value
direction = "b"
else:
order_by = parse_string(
request,
"order_by",
default=MediaSortOrder.CREATED_TS.value,
allowed_values=(
MediaSortOrder.MEDIA_ID.value,
MediaSortOrder.UPLOAD_NAME.value,
MediaSortOrder.CREATED_TS.value,
MediaSortOrder.LAST_ACCESS_TS.value,
MediaSortOrder.MEDIA_LENGTH.value,
MediaSortOrder.MEDIA_TYPE.value,
MediaSortOrder.QUARANTINED_BY.value,
MediaSortOrder.SAFE_FROM_QUARANTINE.value,
),
)
direction = parse_string(
request, "dir", default="f", allowed_values=("f", "b")
)

media, _ = await self.store.get_local_media_by_user_paginate(
start, limit, user_id, order_by, direction
)

deleted_media, total = await self.media_repository.delete_local_media_ids(
([row["media_id"] for row in media])
)

return 200, {"deleted_media": deleted_media, "total": total}


class UserTokenRestServlet(RestServlet):
"""An admin API for logging in as a user.
Expand Down
6 changes: 4 additions & 2 deletions synapse/rest/media/v1/media_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,9 @@ async def delete_old_remote_media(self, before_ts: int) -> Dict[str, int]:

return {"deleted": deleted}

async def delete_local_media(self, media_id: str) -> Tuple[List[str], int]:
async def delete_local_media_ids(
self, media_ids: List[str]
) -> Tuple[List[str], int]:
"""
Delete the given local or remote media ID from this server

Expand All @@ -845,7 +847,7 @@ async def delete_local_media(self, media_id: str) -> Tuple[List[str], int]:
Returns:
A tuple of (list of deleted media IDs, total deleted media IDs).
"""
return await self._remove_local_media_from_disk([media_id])
return await self._remove_local_media_from_disk(media_ids)

async def delete_old_local_media(
self,
Expand Down
Loading