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

Commit

Permalink
Show error when timestamp in seconds is provided to the /purge_media_…
Browse files Browse the repository at this point in the history
…cache API (#11101)
  • Loading branch information
aaronraimist authored Oct 20, 2021
1 parent ee2cee5 commit 2c61a31
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 13 deletions.
1 change: 1 addition & 0 deletions changelog.d/11101.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Show an error when timestamp in seconds is provided to the `/purge_media_cache` Admin API.
6 changes: 3 additions & 3 deletions docs/admin_api/media_admin_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ POST /_synapse/admin/v1/media/<server_name>/delete?before_ts=<before_ts>
URL Parameters

* `server_name`: string - The name of your local server (e.g `matrix.org`).
* `before_ts`: string representing a positive integer - Unix timestamp in ms.
* `before_ts`: string representing a positive integer - Unix timestamp in milliseconds.
Files that were last used before this timestamp will be deleted. It is the timestamp of
last access and not the timestamp creation.
last access, not the timestamp when the file was created.
* `size_gt`: Optional - string representing a positive integer - Size of the media in bytes.
Files that are larger will be deleted. Defaults to `0`.
* `keep_profiles`: Optional - string representing a boolean - Switch to also delete files
Expand Down Expand Up @@ -302,7 +302,7 @@ POST /_synapse/admin/v1/purge_media_cache?before_ts=<unix_timestamp_in_ms>

URL Parameters

* `unix_timestamp_in_ms`: string representing a positive integer - Unix timestamp in ms.
* `unix_timestamp_in_ms`: string representing a positive integer - Unix timestamp in milliseconds.
All cached media that was last accessed before this timestamp will be removed.

Response:
Expand Down
33 changes: 27 additions & 6 deletions synapse/rest/admin/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class QuarantineMediaInRoom(RestServlet):
"""

PATTERNS = [
*admin_patterns("/room/(?P<room_id>[^/]+)/media/quarantine"),
*admin_patterns("/room/(?P<room_id>[^/]+)/media/quarantine$"),
# This path kept around for legacy reasons
*admin_patterns("/quarantine_media/(?P<room_id>[^/]+)"),
]
Expand Down Expand Up @@ -70,7 +70,7 @@ class QuarantineMediaByUser(RestServlet):
this server.
"""

PATTERNS = admin_patterns("/user/(?P<user_id>[^/]+)/media/quarantine")
PATTERNS = admin_patterns("/user/(?P<user_id>[^/]+)/media/quarantine$")

def __init__(self, hs: "HomeServer"):
self.store = hs.get_datastore()
Expand Down Expand Up @@ -199,7 +199,7 @@ async def on_POST(
class ListMediaInRoom(RestServlet):
"""Lists all of the media in a given room."""

PATTERNS = admin_patterns("/room/(?P<room_id>[^/]+)/media")
PATTERNS = admin_patterns("/room/(?P<room_id>[^/]+)/media$")

def __init__(self, hs: "HomeServer"):
self.store = hs.get_datastore()
Expand All @@ -219,7 +219,7 @@ async def on_GET(


class PurgeMediaCacheRestServlet(RestServlet):
PATTERNS = admin_patterns("/purge_media_cache")
PATTERNS = admin_patterns("/purge_media_cache$")

def __init__(self, hs: "HomeServer"):
self.media_repository = hs.get_media_repository()
Expand All @@ -231,6 +231,20 @@ async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
before_ts = parse_integer(request, "before_ts", required=True)
logger.info("before_ts: %r", before_ts)

if before_ts < 0:
raise SynapseError(
400,
"Query parameter before_ts must be a positive integer.",
errcode=Codes.INVALID_PARAM,
)
elif before_ts < 30000000000: # Dec 1970 in milliseconds, Aug 2920 in seconds
raise SynapseError(
400,
"Query parameter before_ts you provided is from the year 1970. "
+ "Double check that you are providing a timestamp in milliseconds.",
errcode=Codes.INVALID_PARAM,
)

ret = await self.media_repository.delete_old_remote_media(before_ts)

return 200, ret
Expand Down Expand Up @@ -271,7 +285,7 @@ class DeleteMediaByDateSize(RestServlet):
timestamp and size.
"""

PATTERNS = admin_patterns("/media/(?P<server_name>[^/]+)/delete")
PATTERNS = admin_patterns("/media/(?P<server_name>[^/]+)/delete$")

def __init__(self, hs: "HomeServer"):
self.store = hs.get_datastore()
Expand All @@ -291,7 +305,14 @@ async def on_POST(
if before_ts < 0:
raise SynapseError(
400,
"Query parameter before_ts must be a string representing a positive integer.",
"Query parameter before_ts must be a positive integer.",
errcode=Codes.INVALID_PARAM,
)
elif before_ts < 30000000000: # Dec 1970 in milliseconds, Aug 2920 in seconds
raise SynapseError(
400,
"Query parameter before_ts you provided is from the year 1970. "
+ "Double check that you are providing a timestamp in milliseconds.",
errcode=Codes.INVALID_PARAM,
)
if size_gt < 0:
Expand Down
106 changes: 102 additions & 4 deletions tests/rest/admin/test_media.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
from tests.server import FakeSite, make_request
from tests.test_utils import SMALL_PNG

VALID_TIMESTAMP = 1609459200000 # 2021-01-01 in milliseconds
INVALID_TIMESTAMP_IN_S = 1893456000 # 2030-01-01 in seconds


class DeleteMediaByIDTestCase(unittest.HomeserverTestCase):

Expand Down Expand Up @@ -203,6 +206,9 @@ def prepare(self, reactor, clock, hs):
self.filepaths = MediaFilePaths(hs.config.media.media_store_path)
self.url = "/_synapse/admin/v1/media/%s/delete" % self.server_name

# Move clock up to somewhat realistic time
self.reactor.advance(1000000000)

def test_no_auth(self):
"""
Try to delete media without authentication.
Expand Down Expand Up @@ -237,7 +243,7 @@ def test_media_is_not_local(self):

channel = self.make_request(
"POST",
url + "?before_ts=1234",
url + f"?before_ts={VALID_TIMESTAMP}",
access_token=self.admin_user_tok,
)

Expand Down Expand Up @@ -273,13 +279,27 @@ def test_invalid_parameter(self):
self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
self.assertEqual(
"Query parameter before_ts must be a string representing a positive integer.",
"Query parameter before_ts must be a positive integer.",
channel.json_body["error"],
)

channel = self.make_request(
"POST",
self.url + "?before_ts=1234&size_gt=-1234",
self.url + f"?before_ts={INVALID_TIMESTAMP_IN_S}",
access_token=self.admin_user_tok,
)

self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
self.assertEqual(
"Query parameter before_ts you provided is from the year 1970. "
+ "Double check that you are providing a timestamp in milliseconds.",
channel.json_body["error"],
)

channel = self.make_request(
"POST",
self.url + f"?before_ts={VALID_TIMESTAMP}&size_gt=-1234",
access_token=self.admin_user_tok,
)

Expand All @@ -292,7 +312,7 @@ def test_invalid_parameter(self):

channel = self.make_request(
"POST",
self.url + "?before_ts=1234&keep_profiles=not_bool",
self.url + f"?before_ts={VALID_TIMESTAMP}&keep_profiles=not_bool",
access_token=self.admin_user_tok,
)

Expand Down Expand Up @@ -767,3 +787,81 @@ def test_protect_media(self):

media_info = self.get_success(self.store.get_local_media(self.media_id))
self.assertFalse(media_info["safe_from_quarantine"])


class PurgeMediaCacheTestCase(unittest.HomeserverTestCase):

servlets = [
synapse.rest.admin.register_servlets,
synapse.rest.admin.register_servlets_for_media_repo,
login.register_servlets,
profile.register_servlets,
room.register_servlets,
]

def prepare(self, reactor, clock, hs):
self.media_repo = hs.get_media_repository_resource()
self.server_name = hs.hostname

self.admin_user = self.register_user("admin", "pass", admin=True)
self.admin_user_tok = self.login("admin", "pass")

self.filepaths = MediaFilePaths(hs.config.media.media_store_path)
self.url = "/_synapse/admin/v1/purge_media_cache"

def test_no_auth(self):
"""
Try to delete media without authentication.
"""

channel = self.make_request("POST", self.url, b"{}")

self.assertEqual(401, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.MISSING_TOKEN, channel.json_body["errcode"])

def test_requester_is_not_admin(self):
"""
If the user is not a server admin, an error is returned.
"""
self.other_user = self.register_user("user", "pass")
self.other_user_token = self.login("user", "pass")

channel = self.make_request(
"POST",
self.url,
access_token=self.other_user_token,
)

self.assertEqual(403, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.FORBIDDEN, channel.json_body["errcode"])

def test_invalid_parameter(self):
"""
If parameters are invalid, an error is returned.
"""
channel = self.make_request(
"POST",
self.url + "?before_ts=-1234",
access_token=self.admin_user_tok,
)

self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
self.assertEqual(
"Query parameter before_ts must be a positive integer.",
channel.json_body["error"],
)

channel = self.make_request(
"POST",
self.url + f"?before_ts={INVALID_TIMESTAMP_IN_S}",
access_token=self.admin_user_tok,
)

self.assertEqual(400, int(channel.result["code"]), msg=channel.result["body"])
self.assertEqual(Codes.INVALID_PARAM, channel.json_body["errcode"])
self.assertEqual(
"Query parameter before_ts you provided is from the year 1970. "
+ "Double check that you are providing a timestamp in milliseconds.",
channel.json_body["error"],
)

0 comments on commit 2c61a31

Please sign in to comment.