mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-09-20 08:24:55 -04:00
Revert accidental fast-forward merge from v1.49.0rc1
Revert "Sort internal changes in changelog" Revert "Update CHANGES.md" Revert "1.49.0rc1" Revert "Revert "Move `glob_to_regex` and `re_word_boundary` to `matrix-python-common` (#11505) (#11527)" Revert "Refactors in `_generate_sync_entry_for_rooms` (#11515)" Revert "Correctly register shutdown handler for presence workers (#11518)" Revert "Fix `ModuleApi.looping_background_call` for non-async functions (#11524)" Revert "Fix 'delete room' admin api to work on incomplete rooms (#11523)" Revert "Correctly ignore invites from ignored users (#11511)" Revert "Fix the test breakage introduced by #11435 as a result of concurrent PRs (#11522)" Revert "Stabilise support for MSC2918 refresh tokens as they have now been merged into the Matrix specification. (#11435)" Revert "Save the OIDC session ID (sid) with the device on login (#11482)" Revert "Add admin API to get some information about federation status (#11407)" Revert "Include bundled aggregations in /sync and related fixes (#11478)" Revert "Move `glob_to_regex` and `re_word_boundary` to `matrix-python-common` (#11505)" Revert "Update backward extremity docs to make it clear that it does not indicate whether we have fetched an events' `prev_events` (#11469)" Revert "Support configuring the lifetime of non-refreshable access tokens separately to refreshable access tokens. (#11445)" Revert "Add type hints to `synapse/tests/rest/admin` (#11501)" Revert "Revert accidental commits to develop." Revert "Newsfile" Revert "Give `tests.server.setup_test_homeserver` (nominally!) the same behaviour" Revert "Move `tests.utils.setup_test_homeserver` to `tests.server`" Revert "Convert one of the `setup_test_homeserver`s to `make_test_homeserver_synchronous`" Revert "Disambiguate queries on `state_key` (#11497)" Revert "Comments on the /sync tentacles (#11494)" Revert "Clean up tests.storage.test_appservice (#11492)" Revert "Clean up `tests.storage.test_main` to remove use of legacy code. (#11493)" Revert "Clean up `tests.test_visibility` to remove legacy code. (#11495)" Revert "Minor cleanup on recently ported doc pages (#11466)" Revert "Add most of the missing type hints to `synapse.federation`. (#11483)" Revert "Avoid waiting for zombie processes in `synctl stop` (#11490)" Revert "Fix media repository failing when media store path contains symlinks (#11446)" Revert "Add type annotations to `tests.storage.test_appservice`. (#11488)" Revert "`scripts-dev/sign_json`: support for signing events (#11486)" Revert "Add MSC3030 experimental client and federation API endpoints to get the closest event to a given timestamp (#9445)" Revert "Port wiki pages to documentation website (#11402)" Revert "Add a license header and comment. (#11479)" Revert "Clean-up get_version_string (#11468)" Revert "Link background update controller docs to summary (#11475)" Revert "Additional type hints for config module. (#11465)" Revert "Register the login redirect endpoint for v3. (#11451)" Revert "Update openid.md" Revert "Remove mention of OIDC certification from Dex (#11470)" Revert "Add a note about huge pages to our Postgres doc (#11467)" Revert "Don't start Synapse master process if `worker_app` is set (#11416)" Revert "Expose worker & homeserver as entrypoints in `setup.py` (#11449)" Revert "Bundle relations of relations into the `/relations` result. (#11284)" Revert "Fix `LruCache` corruption bug with a `size_callback` that can return 0 (#11454)" Revert "Eliminate a few `Any`s in `LruCache` type hints (#11453)" Revert "Remove unnecessary `json.dumps` from `tests.rest.admin` (#11461)" Revert "Merge branch 'master' into develop" This reverts commit26b5d2320f
. This reverts commitbce4220f38
. This reverts commit966b5d0fa0
. This reverts commit088d748f2c
. This reverts commit14d593f72d
. This reverts commit2a3ec6facf
. This reverts commiteccc49d755
. This reverts commitb1ecd19c5d
. This reverts commit9c55dedc8c
. This reverts commit2d42e586a8
. This reverts commit2f053f3f82
. This reverts commita15a893df8
. This reverts commit8b4b153c9e
. This reverts commit494ebd7347
. This reverts commita77c369897
. This reverts commit4eb77965cd
. This reverts commit637df95de6
. This reverts commite5f426cd54
. This reverts commit8cd68b8102
. This reverts commit6cae125e20
. This reverts commit7be88fbf48
. This reverts commitb3fd99b74a
. This reverts commitf7ec6e7d9e
. This reverts commit5640992d17
. This reverts commitd26808dd85
. This reverts commitf91624a595
. This reverts commit16d39a5490
. This reverts commit8a4c296987
. This reverts commit49e1356ee3
. This reverts commitd2279f471b
. This reverts commitb50e39df57
. This reverts commit858d80bf0f
. This reverts commit435f044807
. This reverts commitf61462e1be
. This reverts commita6f1a3abec
. This reverts commit84dc50e160
. This reverts commited635d3285
. This reverts commit7b62791e00
. This reverts commit153194c771
. This reverts commitf44d729d4c
. This reverts commita265fbd397
. This reverts commitb9fef1a7cd
. This reverts commitb0eb64ff7b
. This reverts commitf1795463bf
. This reverts commit70cbb1a5e3
. This reverts commit42bf020463
. This reverts commit379f2650cf
. This reverts commit7ff22d6da4
. This reverts commit5a0b652d36
. This reverts commit432a174bc1
. This reverts commitb14f8a1baf
, reversing changes made toe713855dca
.
This commit is contained in:
parent
26b5d2320f
commit
158d73ebdd
165 changed files with 2709 additions and 7721 deletions
|
@ -17,7 +17,6 @@
|
|||
|
||||
import logging
|
||||
import platform
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Optional, Tuple
|
||||
|
||||
import synapse
|
||||
|
@ -40,10 +39,6 @@ from synapse.rest.admin.event_reports import (
|
|||
EventReportDetailRestServlet,
|
||||
EventReportsRestServlet,
|
||||
)
|
||||
from synapse.rest.admin.federation import (
|
||||
DestinationsRestServlet,
|
||||
ListDestinationsRestServlet,
|
||||
)
|
||||
from synapse.rest.admin.groups import DeleteGroupAdminRestServlet
|
||||
from synapse.rest.admin.media import ListMediaInRoom, register_servlets_for_media_repo
|
||||
from synapse.rest.admin.registration_tokens import (
|
||||
|
@ -103,7 +98,7 @@ class VersionServlet(RestServlet):
|
|||
}
|
||||
|
||||
def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||
return HTTPStatus.OK, self.res
|
||||
return 200, self.res
|
||||
|
||||
|
||||
class PurgeHistoryRestServlet(RestServlet):
|
||||
|
@ -135,7 +130,7 @@ class PurgeHistoryRestServlet(RestServlet):
|
|||
event = await self.store.get_event(event_id)
|
||||
|
||||
if event.room_id != room_id:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Event is for wrong room.")
|
||||
raise SynapseError(400, "Event is for wrong room.")
|
||||
|
||||
# RoomStreamToken expects [int] not Optional[int]
|
||||
assert event.internal_metadata.stream_ordering is not None
|
||||
|
@ -149,9 +144,7 @@ class PurgeHistoryRestServlet(RestServlet):
|
|||
ts = body["purge_up_to_ts"]
|
||||
if not isinstance(ts, int):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"purge_up_to_ts must be an int",
|
||||
errcode=Codes.BAD_JSON,
|
||||
400, "purge_up_to_ts must be an int", errcode=Codes.BAD_JSON
|
||||
)
|
||||
|
||||
stream_ordering = await self.store.find_first_stream_ordering_after_ts(ts)
|
||||
|
@ -167,9 +160,7 @@ class PurgeHistoryRestServlet(RestServlet):
|
|||
stream_ordering,
|
||||
)
|
||||
raise SynapseError(
|
||||
HTTPStatus.NOT_FOUND,
|
||||
"there is no event to be purged",
|
||||
errcode=Codes.NOT_FOUND,
|
||||
404, "there is no event to be purged", errcode=Codes.NOT_FOUND
|
||||
)
|
||||
(stream, topo, _event_id) = r
|
||||
token = "t%d-%d" % (topo, stream)
|
||||
|
@ -182,7 +173,7 @@ class PurgeHistoryRestServlet(RestServlet):
|
|||
)
|
||||
else:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"must specify purge_up_to_event_id or purge_up_to_ts",
|
||||
errcode=Codes.BAD_JSON,
|
||||
)
|
||||
|
@ -191,7 +182,7 @@ class PurgeHistoryRestServlet(RestServlet):
|
|||
room_id, token, delete_local_events=delete_local_events
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"purge_id": purge_id}
|
||||
return 200, {"purge_id": purge_id}
|
||||
|
||||
|
||||
class PurgeHistoryStatusRestServlet(RestServlet):
|
||||
|
@ -210,7 +201,7 @@ class PurgeHistoryStatusRestServlet(RestServlet):
|
|||
if purge_status is None:
|
||||
raise NotFoundError("purge id '%s' not found" % purge_id)
|
||||
|
||||
return HTTPStatus.OK, purge_status.asdict()
|
||||
return 200, purge_status.asdict()
|
||||
|
||||
|
||||
########################################################################################
|
||||
|
@ -265,8 +256,6 @@ def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
|
|||
ListRegistrationTokensRestServlet(hs).register(http_server)
|
||||
NewRegistrationTokenRestServlet(hs).register(http_server)
|
||||
RegistrationTokenRestServlet(hs).register(http_server)
|
||||
DestinationsRestServlet(hs).register(http_server)
|
||||
ListDestinationsRestServlet(hs).register(http_server)
|
||||
|
||||
# Some servlets only get registered for the main process.
|
||||
if hs.config.worker.worker_app is None:
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
# limitations under the License.
|
||||
|
||||
import re
|
||||
from http import HTTPStatus
|
||||
from typing import Iterable, Pattern
|
||||
|
||||
from synapse.api.auth import Auth
|
||||
|
@ -63,4 +62,4 @@ async def assert_user_is_admin(auth: Auth, user_id: UserID) -> None:
|
|||
"""
|
||||
is_admin = await auth.is_server_admin(user_id)
|
||||
if not is_admin:
|
||||
raise AuthError(HTTPStatus.FORBIDDEN, "You are not a server admin")
|
||||
raise AuthError(403, "You are not a server admin")
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import logging
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from synapse.api.errors import NotFoundError, SynapseError
|
||||
|
@ -54,7 +53,7 @@ class DeviceRestServlet(RestServlet):
|
|||
|
||||
target_user = UserID.from_string(user_id)
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only lookup local users")
|
||||
raise SynapseError(400, "Can only lookup local users")
|
||||
|
||||
u = await self.store.get_user_by_id(target_user.to_string())
|
||||
if u is None:
|
||||
|
@ -63,7 +62,7 @@ class DeviceRestServlet(RestServlet):
|
|||
device = await self.device_handler.get_device(
|
||||
target_user.to_string(), device_id
|
||||
)
|
||||
return HTTPStatus.OK, device
|
||||
return 200, device
|
||||
|
||||
async def on_DELETE(
|
||||
self, request: SynapseRequest, user_id: str, device_id: str
|
||||
|
@ -72,14 +71,14 @@ class DeviceRestServlet(RestServlet):
|
|||
|
||||
target_user = UserID.from_string(user_id)
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only lookup local users")
|
||||
raise SynapseError(400, "Can only lookup local users")
|
||||
|
||||
u = await self.store.get_user_by_id(target_user.to_string())
|
||||
if u is None:
|
||||
raise NotFoundError("Unknown user")
|
||||
|
||||
await self.device_handler.delete_device(target_user.to_string(), device_id)
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
async def on_PUT(
|
||||
self, request: SynapseRequest, user_id: str, device_id: str
|
||||
|
@ -88,7 +87,7 @@ class DeviceRestServlet(RestServlet):
|
|||
|
||||
target_user = UserID.from_string(user_id)
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only lookup local users")
|
||||
raise SynapseError(400, "Can only lookup local users")
|
||||
|
||||
u = await self.store.get_user_by_id(target_user.to_string())
|
||||
if u is None:
|
||||
|
@ -98,7 +97,7 @@ class DeviceRestServlet(RestServlet):
|
|||
await self.device_handler.update_device(
|
||||
target_user.to_string(), device_id, body
|
||||
)
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class DevicesRestServlet(RestServlet):
|
||||
|
@ -125,14 +124,14 @@ class DevicesRestServlet(RestServlet):
|
|||
|
||||
target_user = UserID.from_string(user_id)
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only lookup local users")
|
||||
raise SynapseError(400, "Can only lookup local users")
|
||||
|
||||
u = await self.store.get_user_by_id(target_user.to_string())
|
||||
if u is None:
|
||||
raise NotFoundError("Unknown user")
|
||||
|
||||
devices = await self.device_handler.get_devices_by_user(target_user.to_string())
|
||||
return HTTPStatus.OK, {"devices": devices, "total": len(devices)}
|
||||
return 200, {"devices": devices, "total": len(devices)}
|
||||
|
||||
|
||||
class DeleteDevicesRestServlet(RestServlet):
|
||||
|
@ -156,7 +155,7 @@ class DeleteDevicesRestServlet(RestServlet):
|
|||
|
||||
target_user = UserID.from_string(user_id)
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only lookup local users")
|
||||
raise SynapseError(400, "Can only lookup local users")
|
||||
|
||||
u = await self.store.get_user_by_id(target_user.to_string())
|
||||
if u is None:
|
||||
|
@ -168,4 +167,4 @@ class DeleteDevicesRestServlet(RestServlet):
|
|||
await self.device_handler.delete_devices(
|
||||
target_user.to_string(), body["devices"]
|
||||
)
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from synapse.api.errors import Codes, NotFoundError, SynapseError
|
||||
|
@ -67,23 +66,21 @@ class EventReportsRestServlet(RestServlet):
|
|||
|
||||
if start < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"The start parameter must be a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if limit < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"The limit parameter must be a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if direction not in ("f", "b"):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Unknown direction: %s" % (direction,),
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
400, "Unknown direction: %s" % (direction,), errcode=Codes.INVALID_PARAM
|
||||
)
|
||||
|
||||
event_reports, total = await self.store.get_event_reports_paginate(
|
||||
|
@ -93,7 +90,7 @@ class EventReportsRestServlet(RestServlet):
|
|||
if (start + limit) < total:
|
||||
ret["next_token"] = start + len(event_reports)
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class EventReportDetailRestServlet(RestServlet):
|
||||
|
@ -130,17 +127,13 @@ class EventReportDetailRestServlet(RestServlet):
|
|||
try:
|
||||
resolved_report_id = int(report_id)
|
||||
except ValueError:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, message, errcode=Codes.INVALID_PARAM
|
||||
)
|
||||
raise SynapseError(400, message, errcode=Codes.INVALID_PARAM)
|
||||
|
||||
if resolved_report_id < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, message, errcode=Codes.INVALID_PARAM
|
||||
)
|
||||
raise SynapseError(400, message, errcode=Codes.INVALID_PARAM)
|
||||
|
||||
ret = await self.store.get_event_report(resolved_report_id)
|
||||
if not ret:
|
||||
raise NotFoundError("Event report not found")
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
# Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import logging
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from synapse.api.errors import Codes, NotFoundError, SynapseError
|
||||
from synapse.http.servlet import RestServlet, parse_integer, parse_string
|
||||
from synapse.http.site import SynapseRequest
|
||||
from synapse.rest.admin._base import admin_patterns, assert_requester_is_admin
|
||||
from synapse.storage.databases.main.transactions import DestinationSortOrder
|
||||
from synapse.types import JsonDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from synapse.server import HomeServer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ListDestinationsRestServlet(RestServlet):
|
||||
"""Get request to list all destinations.
|
||||
This needs user to have administrator access in Synapse.
|
||||
|
||||
GET /_synapse/admin/v1/federation/destinations?from=0&limit=10
|
||||
|
||||
returns:
|
||||
200 OK with list of destinations if success otherwise an error.
|
||||
|
||||
The parameters `from` and `limit` are required only for pagination.
|
||||
By default, a `limit` of 100 is used.
|
||||
The parameter `destination` can be used to filter by destination.
|
||||
The parameter `order_by` can be used to order the result.
|
||||
"""
|
||||
|
||||
PATTERNS = admin_patterns("/federation/destinations$")
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
self._auth = hs.get_auth()
|
||||
self._store = hs.get_datastore()
|
||||
|
||||
async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||
await assert_requester_is_admin(self._auth, request)
|
||||
|
||||
start = parse_integer(request, "from", default=0)
|
||||
limit = parse_integer(request, "limit", default=100)
|
||||
|
||||
if start < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Query parameter from must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if limit < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Query parameter limit must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
destination = parse_string(request, "destination")
|
||||
|
||||
order_by = parse_string(
|
||||
request,
|
||||
"order_by",
|
||||
default=DestinationSortOrder.DESTINATION.value,
|
||||
allowed_values=[dest.value for dest in DestinationSortOrder],
|
||||
)
|
||||
|
||||
direction = parse_string(request, "dir", default="f", allowed_values=("f", "b"))
|
||||
|
||||
destinations, total = await self._store.get_destinations_paginate(
|
||||
start, limit, destination, order_by, direction
|
||||
)
|
||||
response = {"destinations": destinations, "total": total}
|
||||
if (start + limit) < total:
|
||||
response["next_token"] = str(start + len(destinations))
|
||||
|
||||
return HTTPStatus.OK, response
|
||||
|
||||
|
||||
class DestinationsRestServlet(RestServlet):
|
||||
"""Get details of a destination.
|
||||
This needs user to have administrator access in Synapse.
|
||||
|
||||
GET /_synapse/admin/v1/federation/destinations/<destination>
|
||||
|
||||
returns:
|
||||
200 OK with details of a destination if success otherwise an error.
|
||||
"""
|
||||
|
||||
PATTERNS = admin_patterns("/federation/destinations/(?P<destination>[^/]+)$")
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
self._auth = hs.get_auth()
|
||||
self._store = hs.get_datastore()
|
||||
|
||||
async def on_GET(
|
||||
self, request: SynapseRequest, destination: str
|
||||
) -> Tuple[int, JsonDict]:
|
||||
await assert_requester_is_admin(self._auth, request)
|
||||
|
||||
destination_retry_timings = await self._store.get_destination_retry_timings(
|
||||
destination
|
||||
)
|
||||
|
||||
if not destination_retry_timings:
|
||||
raise NotFoundError("Unknown destination")
|
||||
|
||||
last_successful_stream_ordering = (
|
||||
await self._store.get_destination_last_successful_stream_ordering(
|
||||
destination
|
||||
)
|
||||
)
|
||||
|
||||
response = {
|
||||
"destination": destination,
|
||||
"failure_ts": destination_retry_timings.failure_ts,
|
||||
"retry_last_ts": destination_retry_timings.retry_last_ts,
|
||||
"retry_interval": destination_retry_timings.retry_interval,
|
||||
"last_successful_stream_ordering": last_successful_stream_ordering,
|
||||
}
|
||||
|
||||
return HTTPStatus.OK, response
|
|
@ -12,7 +12,6 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import logging
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from synapse.api.errors import SynapseError
|
||||
|
@ -44,7 +43,7 @@ class DeleteGroupAdminRestServlet(RestServlet):
|
|||
await assert_user_is_admin(self.auth, requester.user)
|
||||
|
||||
if not self.is_mine_id(group_id):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only delete local groups")
|
||||
raise SynapseError(400, "Can only delete local groups")
|
||||
|
||||
await self.group_server.delete_group(group_id, requester.user.to_string())
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from synapse.api.errors import AuthError, Codes, NotFoundError, SynapseError
|
||||
|
@ -63,7 +62,7 @@ class QuarantineMediaInRoom(RestServlet):
|
|||
room_id, requester.user.to_string()
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"num_quarantined": num_quarantined}
|
||||
return 200, {"num_quarantined": num_quarantined}
|
||||
|
||||
|
||||
class QuarantineMediaByUser(RestServlet):
|
||||
|
@ -90,7 +89,7 @@ class QuarantineMediaByUser(RestServlet):
|
|||
user_id, requester.user.to_string()
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"num_quarantined": num_quarantined}
|
||||
return 200, {"num_quarantined": num_quarantined}
|
||||
|
||||
|
||||
class QuarantineMediaByID(RestServlet):
|
||||
|
@ -119,7 +118,7 @@ class QuarantineMediaByID(RestServlet):
|
|||
server_name, media_id, requester.user.to_string()
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class UnquarantineMediaByID(RestServlet):
|
||||
|
@ -148,7 +147,7 @@ class UnquarantineMediaByID(RestServlet):
|
|||
# Remove from quarantine this media id
|
||||
await self.store.quarantine_media_by_id(server_name, media_id, None)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class ProtectMediaByID(RestServlet):
|
||||
|
@ -171,7 +170,7 @@ class ProtectMediaByID(RestServlet):
|
|||
# Protect this media id
|
||||
await self.store.mark_local_media_as_safe(media_id, safe=True)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class UnprotectMediaByID(RestServlet):
|
||||
|
@ -194,7 +193,7 @@ class UnprotectMediaByID(RestServlet):
|
|||
# Unprotect this media id
|
||||
await self.store.mark_local_media_as_safe(media_id, safe=False)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class ListMediaInRoom(RestServlet):
|
||||
|
@ -212,11 +211,11 @@ class ListMediaInRoom(RestServlet):
|
|||
requester = await self.auth.get_user_by_req(request)
|
||||
is_admin = await self.auth.is_server_admin(requester.user)
|
||||
if not is_admin:
|
||||
raise AuthError(HTTPStatus.FORBIDDEN, "You are not a server admin")
|
||||
raise AuthError(403, "You are not a server admin")
|
||||
|
||||
local_mxcs, remote_mxcs = await self.store.get_media_mxcs_in_room(room_id)
|
||||
|
||||
return HTTPStatus.OK, {"local": local_mxcs, "remote": remote_mxcs}
|
||||
return 200, {"local": local_mxcs, "remote": remote_mxcs}
|
||||
|
||||
|
||||
class PurgeMediaCacheRestServlet(RestServlet):
|
||||
|
@ -234,13 +233,13 @@ class PurgeMediaCacheRestServlet(RestServlet):
|
|||
|
||||
if before_ts < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
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(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
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,
|
||||
|
@ -248,7 +247,7 @@ class PurgeMediaCacheRestServlet(RestServlet):
|
|||
|
||||
ret = await self.media_repository.delete_old_remote_media(before_ts)
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class DeleteMediaByID(RestServlet):
|
||||
|
@ -268,7 +267,7 @@ class DeleteMediaByID(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if self.server_name != server_name:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only delete local media")
|
||||
raise SynapseError(400, "Can only delete local media")
|
||||
|
||||
if await self.store.get_local_media(media_id) is None:
|
||||
raise NotFoundError("Unknown media")
|
||||
|
@ -278,7 +277,7 @@ class DeleteMediaByID(RestServlet):
|
|||
deleted_media, total = await self.media_repository.delete_local_media_ids(
|
||||
[media_id]
|
||||
)
|
||||
return HTTPStatus.OK, {"deleted_media": deleted_media, "total": total}
|
||||
return 200, {"deleted_media": deleted_media, "total": total}
|
||||
|
||||
|
||||
class DeleteMediaByDateSize(RestServlet):
|
||||
|
@ -305,26 +304,26 @@ class DeleteMediaByDateSize(RestServlet):
|
|||
|
||||
if before_ts < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
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(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
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:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter size_gt must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if self.server_name != server_name:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only delete local media")
|
||||
raise SynapseError(400, "Can only delete local media")
|
||||
|
||||
logging.info(
|
||||
"Deleting local media by timestamp: %s, size larger than: %s, keep profile media: %s"
|
||||
|
@ -334,7 +333,7 @@ class DeleteMediaByDateSize(RestServlet):
|
|||
deleted_media, total = await self.media_repository.delete_old_local_media(
|
||||
before_ts, size_gt, keep_profiles
|
||||
)
|
||||
return HTTPStatus.OK, {"deleted_media": deleted_media, "total": total}
|
||||
return 200, {"deleted_media": deleted_media, "total": total}
|
||||
|
||||
|
||||
class UserMediaRestServlet(RestServlet):
|
||||
|
@ -370,7 +369,7 @@ class UserMediaRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.is_mine(UserID.from_string(user_id)):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up 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:
|
||||
|
@ -381,14 +380,14 @@ class UserMediaRestServlet(RestServlet):
|
|||
|
||||
if start < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter from must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if limit < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter limit must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -426,7 +425,7 @@ class UserMediaRestServlet(RestServlet):
|
|||
if (start + limit) < total:
|
||||
ret["next_token"] = start + len(media)
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
async def on_DELETE(
|
||||
self, request: SynapseRequest, user_id: str
|
||||
|
@ -437,7 +436,7 @@ class UserMediaRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.is_mine(UserID.from_string(user_id)):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up 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:
|
||||
|
@ -448,14 +447,14 @@ class UserMediaRestServlet(RestServlet):
|
|||
|
||||
if start < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter from must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if limit < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter limit must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -493,7 +492,7 @@ class UserMediaRestServlet(RestServlet):
|
|||
([row["media_id"] for row in media])
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"deleted_media": deleted_media, "total": total}
|
||||
return 200, {"deleted_media": deleted_media, "total": total}
|
||||
|
||||
|
||||
def register_servlets_for_media_repo(hs: "HomeServer", http_server: HttpServer) -> None:
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
import logging
|
||||
import string
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from synapse.api.errors import Codes, NotFoundError, SynapseError
|
||||
|
@ -78,7 +77,7 @@ class ListRegistrationTokensRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
valid = parse_boolean(request, "valid")
|
||||
token_list = await self.store.get_registration_tokens(valid)
|
||||
return HTTPStatus.OK, {"registration_tokens": token_list}
|
||||
return 200, {"registration_tokens": token_list}
|
||||
|
||||
|
||||
class NewRegistrationTokenRestServlet(RestServlet):
|
||||
|
@ -124,20 +123,16 @@ class NewRegistrationTokenRestServlet(RestServlet):
|
|||
if "token" in body:
|
||||
token = body["token"]
|
||||
if not isinstance(token, str):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"token must be a string",
|
||||
Codes.INVALID_PARAM,
|
||||
)
|
||||
raise SynapseError(400, "token must be a string", Codes.INVALID_PARAM)
|
||||
if not (0 < len(token) <= 64):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"token must not be empty and must not be longer than 64 characters",
|
||||
Codes.INVALID_PARAM,
|
||||
)
|
||||
if not set(token).issubset(self.allowed_chars_set):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"token must consist only of characters matched by the regex [A-Za-z0-9-_]",
|
||||
Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -147,13 +142,11 @@ class NewRegistrationTokenRestServlet(RestServlet):
|
|||
length = body.get("length", 16)
|
||||
if not isinstance(length, int):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"length must be an integer",
|
||||
Codes.INVALID_PARAM,
|
||||
400, "length must be an integer", Codes.INVALID_PARAM
|
||||
)
|
||||
if not (0 < length <= 64):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"length must be greater than zero and not greater than 64",
|
||||
Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -169,7 +162,7 @@ class NewRegistrationTokenRestServlet(RestServlet):
|
|||
or (isinstance(uses_allowed, int) and uses_allowed >= 0)
|
||||
):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"uses_allowed must be a non-negative integer or null",
|
||||
Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -177,15 +170,11 @@ class NewRegistrationTokenRestServlet(RestServlet):
|
|||
expiry_time = body.get("expiry_time", None)
|
||||
if not isinstance(expiry_time, (int, type(None))):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"expiry_time must be an integer or null",
|
||||
Codes.INVALID_PARAM,
|
||||
400, "expiry_time must be an integer or null", Codes.INVALID_PARAM
|
||||
)
|
||||
if isinstance(expiry_time, int) and expiry_time < self.clock.time_msec():
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"expiry_time must not be in the past",
|
||||
Codes.INVALID_PARAM,
|
||||
400, "expiry_time must not be in the past", Codes.INVALID_PARAM
|
||||
)
|
||||
|
||||
created = await self.store.create_registration_token(
|
||||
|
@ -193,9 +182,7 @@ class NewRegistrationTokenRestServlet(RestServlet):
|
|||
)
|
||||
if not created:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
f"Token already exists: {token}",
|
||||
Codes.INVALID_PARAM,
|
||||
400, f"Token already exists: {token}", Codes.INVALID_PARAM
|
||||
)
|
||||
|
||||
resp = {
|
||||
|
@ -205,7 +192,7 @@ class NewRegistrationTokenRestServlet(RestServlet):
|
|||
"completed": 0,
|
||||
"expiry_time": expiry_time,
|
||||
}
|
||||
return HTTPStatus.OK, resp
|
||||
return 200, resp
|
||||
|
||||
|
||||
class RegistrationTokenRestServlet(RestServlet):
|
||||
|
@ -274,7 +261,7 @@ class RegistrationTokenRestServlet(RestServlet):
|
|||
if token_info is None:
|
||||
raise NotFoundError(f"No such registration token: {token}")
|
||||
|
||||
return HTTPStatus.OK, token_info
|
||||
return 200, token_info
|
||||
|
||||
async def on_PUT(self, request: SynapseRequest, token: str) -> Tuple[int, JsonDict]:
|
||||
"""Update a registration token."""
|
||||
|
@ -290,7 +277,7 @@ class RegistrationTokenRestServlet(RestServlet):
|
|||
or (isinstance(uses_allowed, int) and uses_allowed >= 0)
|
||||
):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"uses_allowed must be a non-negative integer or null",
|
||||
Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -300,15 +287,11 @@ class RegistrationTokenRestServlet(RestServlet):
|
|||
expiry_time = body["expiry_time"]
|
||||
if not isinstance(expiry_time, (int, type(None))):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"expiry_time must be an integer or null",
|
||||
Codes.INVALID_PARAM,
|
||||
400, "expiry_time must be an integer or null", Codes.INVALID_PARAM
|
||||
)
|
||||
if isinstance(expiry_time, int) and expiry_time < self.clock.time_msec():
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"expiry_time must not be in the past",
|
||||
Codes.INVALID_PARAM,
|
||||
400, "expiry_time must not be in the past", Codes.INVALID_PARAM
|
||||
)
|
||||
new_attributes["expiry_time"] = expiry_time
|
||||
|
||||
|
@ -324,7 +307,7 @@ class RegistrationTokenRestServlet(RestServlet):
|
|||
if token_info is None:
|
||||
raise NotFoundError(f"No such registration token: {token}")
|
||||
|
||||
return HTTPStatus.OK, token_info
|
||||
return 200, token_info
|
||||
|
||||
async def on_DELETE(
|
||||
self, request: SynapseRequest, token: str
|
||||
|
@ -333,6 +316,6 @@ class RegistrationTokenRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if await self.store.delete_registration_token(token):
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
raise NotFoundError(f"No such registration token: {token}")
|
||||
|
|
|
@ -102,9 +102,10 @@ class RoomRestV2Servlet(RestServlet):
|
|||
)
|
||||
|
||||
if not RoomID.is_valid(room_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "%s is not a legal room ID" % (room_id,)
|
||||
)
|
||||
raise SynapseError(400, "%s is not a legal room ID" % (room_id,))
|
||||
|
||||
if not await self._store.get_room(room_id):
|
||||
raise NotFoundError("Unknown room id %s" % (room_id,))
|
||||
|
||||
delete_id = self._pagination_handler.start_shutdown_and_purge_room(
|
||||
room_id=room_id,
|
||||
|
@ -117,7 +118,7 @@ class RoomRestV2Servlet(RestServlet):
|
|||
force_purge=force_purge,
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"delete_id": delete_id}
|
||||
return 200, {"delete_id": delete_id}
|
||||
|
||||
|
||||
class DeleteRoomStatusByRoomIdRestServlet(RestServlet):
|
||||
|
@ -136,9 +137,7 @@ class DeleteRoomStatusByRoomIdRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self._auth, request)
|
||||
|
||||
if not RoomID.is_valid(room_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "%s is not a legal room ID" % (room_id,)
|
||||
)
|
||||
raise SynapseError(400, "%s is not a legal room ID" % (room_id,))
|
||||
|
||||
delete_ids = self._pagination_handler.get_delete_ids_by_room(room_id)
|
||||
if delete_ids is None:
|
||||
|
@ -154,7 +153,7 @@ class DeleteRoomStatusByRoomIdRestServlet(RestServlet):
|
|||
**delete.asdict(),
|
||||
}
|
||||
]
|
||||
return HTTPStatus.OK, {"results": cast(JsonDict, response)}
|
||||
return 200, {"results": cast(JsonDict, response)}
|
||||
|
||||
|
||||
class DeleteRoomStatusByDeleteIdRestServlet(RestServlet):
|
||||
|
@ -176,7 +175,7 @@ class DeleteRoomStatusByDeleteIdRestServlet(RestServlet):
|
|||
if delete_status is None:
|
||||
raise NotFoundError("delete id '%s' not found" % delete_id)
|
||||
|
||||
return HTTPStatus.OK, cast(JsonDict, delete_status.asdict())
|
||||
return 200, cast(JsonDict, delete_status.asdict())
|
||||
|
||||
|
||||
class ListRoomRestServlet(RestServlet):
|
||||
|
@ -218,7 +217,7 @@ class ListRoomRestServlet(RestServlet):
|
|||
RoomSortOrder.STATE_EVENTS.value,
|
||||
):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Unknown value for order_by: %s" % (order_by,),
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -226,7 +225,7 @@ class ListRoomRestServlet(RestServlet):
|
|||
search_term = parse_string(request, "search_term", encoding="utf-8")
|
||||
if search_term == "":
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"search_term cannot be an empty string",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -234,9 +233,7 @@ class ListRoomRestServlet(RestServlet):
|
|||
direction = parse_string(request, "dir", default="f")
|
||||
if direction not in ("f", "b"):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Unknown direction: %s" % (direction,),
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
400, "Unknown direction: %s" % (direction,), errcode=Codes.INVALID_PARAM
|
||||
)
|
||||
|
||||
reverse_order = True if direction == "b" else False
|
||||
|
@ -268,7 +265,7 @@ class ListRoomRestServlet(RestServlet):
|
|||
else:
|
||||
response["prev_batch"] = 0
|
||||
|
||||
return HTTPStatus.OK, response
|
||||
return 200, response
|
||||
|
||||
|
||||
class RoomRestServlet(RestServlet):
|
||||
|
@ -313,7 +310,7 @@ class RoomRestServlet(RestServlet):
|
|||
members = await self.store.get_users_in_room(room_id)
|
||||
ret["joined_local_devices"] = await self.store.count_devices_by_users(members)
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
async def on_DELETE(
|
||||
self, request: SynapseRequest, room_id: str
|
||||
|
@ -389,7 +386,7 @@ class RoomRestServlet(RestServlet):
|
|||
# See https://github.com/python/mypy/issues/4976#issuecomment-579883622
|
||||
# for some discussion on why this is necessary. Either way,
|
||||
# `ret` is an opaque dictionary blob as far as the rest of the app cares.
|
||||
return HTTPStatus.OK, cast(JsonDict, ret)
|
||||
return 200, cast(JsonDict, ret)
|
||||
|
||||
|
||||
class RoomMembersRestServlet(RestServlet):
|
||||
|
@ -416,7 +413,7 @@ class RoomMembersRestServlet(RestServlet):
|
|||
members = await self.store.get_users_in_room(room_id)
|
||||
ret = {"members": members, "total": len(members)}
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class RoomStateRestServlet(RestServlet):
|
||||
|
@ -446,10 +443,16 @@ class RoomStateRestServlet(RestServlet):
|
|||
event_ids = await self.store.get_current_state_ids(room_id)
|
||||
events = await self.store.get_events(event_ids.values())
|
||||
now = self.clock.time_msec()
|
||||
room_state = await self._event_serializer.serialize_events(events.values(), now)
|
||||
room_state = await self._event_serializer.serialize_events(
|
||||
events.values(),
|
||||
now,
|
||||
# We don't bother bundling aggregations in when asked for state
|
||||
# events, as clients won't use them.
|
||||
bundle_relations=False,
|
||||
)
|
||||
ret = {"state": room_state}
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class JoinRoomAliasServlet(ResolveRoomIdMixin, RestServlet):
|
||||
|
@ -478,10 +481,7 @@ class JoinRoomAliasServlet(ResolveRoomIdMixin, RestServlet):
|
|||
target_user = UserID.from_string(content["user_id"])
|
||||
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"This endpoint can only be used with local users",
|
||||
)
|
||||
raise SynapseError(400, "This endpoint can only be used with local users")
|
||||
|
||||
if not await self.admin_handler.get_user(target_user):
|
||||
raise NotFoundError("User not found")
|
||||
|
@ -527,7 +527,7 @@ class JoinRoomAliasServlet(ResolveRoomIdMixin, RestServlet):
|
|||
ratelimit=False,
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"room_id": room_id}
|
||||
return 200, {"room_id": room_id}
|
||||
|
||||
|
||||
class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
||||
|
@ -568,7 +568,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
# Figure out which local users currently have power in the room, if any.
|
||||
room_state = await self.state_handler.get_current_state(room_id)
|
||||
if not room_state:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Server not in room")
|
||||
raise SynapseError(400, "Server not in room")
|
||||
|
||||
create_event = room_state[(EventTypes.Create, "")]
|
||||
power_levels = room_state.get((EventTypes.PowerLevels, ""))
|
||||
|
@ -582,9 +582,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
admin_users.sort(key=lambda user: user_power[user])
|
||||
|
||||
if not admin_users:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "No local admin user in room"
|
||||
)
|
||||
raise SynapseError(400, "No local admin user in room")
|
||||
|
||||
admin_user_id = None
|
||||
|
||||
|
@ -601,7 +599,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
|
||||
if not admin_user_id:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"No local admin user in room",
|
||||
)
|
||||
|
||||
|
@ -612,7 +610,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
admin_user_id = create_event.sender
|
||||
if not self.is_mine_id(admin_user_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"No local admin user in room",
|
||||
)
|
||||
|
||||
|
@ -641,8 +639,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
except AuthError:
|
||||
# The admin user we found turned out not to have enough power.
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"No local admin user in room with power to update power levels.",
|
||||
400, "No local admin user in room with power to update power levels."
|
||||
)
|
||||
|
||||
# Now we check if the user we're granting admin rights to is already in
|
||||
|
@ -656,7 +653,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
)
|
||||
|
||||
if is_joined:
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
join_rules = room_state.get((EventTypes.JoinRules, ""))
|
||||
is_public = False
|
||||
|
@ -664,7 +661,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
is_public = join_rules.content.get("join_rule") == JoinRules.PUBLIC
|
||||
|
||||
if is_public:
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
await self.room_member_handler.update_membership(
|
||||
fake_requester,
|
||||
|
@ -673,7 +670,7 @@ class MakeRoomAdminRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
action=Membership.INVITE,
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class ForwardExtremitiesRestServlet(ResolveRoomIdMixin, RestServlet):
|
||||
|
@ -705,7 +702,7 @@ class ForwardExtremitiesRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
room_id, _ = await self.resolve_room_id(room_identifier)
|
||||
|
||||
deleted_count = await self.store.delete_forward_extremities_for_room(room_id)
|
||||
return HTTPStatus.OK, {"deleted": deleted_count}
|
||||
return 200, {"deleted": deleted_count}
|
||||
|
||||
async def on_GET(
|
||||
self, request: SynapseRequest, room_identifier: str
|
||||
|
@ -716,7 +713,7 @@ class ForwardExtremitiesRestServlet(ResolveRoomIdMixin, RestServlet):
|
|||
room_id, _ = await self.resolve_room_id(room_identifier)
|
||||
|
||||
extremities = await self.store.get_forward_extremities_for_room(room_id)
|
||||
return HTTPStatus.OK, {"count": len(extremities), "results": extremities}
|
||||
return 200, {"count": len(extremities), "results": extremities}
|
||||
|
||||
|
||||
class RoomEventContextServlet(RestServlet):
|
||||
|
@ -765,9 +762,7 @@ class RoomEventContextServlet(RestServlet):
|
|||
)
|
||||
|
||||
if not results:
|
||||
raise SynapseError(
|
||||
HTTPStatus.NOT_FOUND, "Event not found.", errcode=Codes.NOT_FOUND
|
||||
)
|
||||
raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
|
||||
|
||||
time_now = self.clock.time_msec()
|
||||
results["events_before"] = await self._event_serializer.serialize_events(
|
||||
|
@ -780,10 +775,13 @@ class RoomEventContextServlet(RestServlet):
|
|||
results["events_after"], time_now
|
||||
)
|
||||
results["state"] = await self._event_serializer.serialize_events(
|
||||
results["state"], time_now
|
||||
results["state"],
|
||||
time_now,
|
||||
# No need to bundle aggregations for state events
|
||||
bundle_relations=False,
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, results
|
||||
return 200, results
|
||||
|
||||
|
||||
class BlockRoomRestServlet(RestServlet):
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Awaitable, Optional, Tuple
|
||||
|
||||
from synapse.api.constants import EventTypes
|
||||
|
@ -83,15 +82,11 @@ class SendServerNoticeServlet(RestServlet):
|
|||
# but worker processes still need to initialise SendServerNoticeServlet (as it is part of the
|
||||
# admin api).
|
||||
if not self.server_notices_manager.is_enabled():
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Server notices are not enabled on this server"
|
||||
)
|
||||
raise SynapseError(400, "Server notices are not enabled on this server")
|
||||
|
||||
target_user = UserID.from_string(body["user_id"])
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Server notices can only be sent to local users"
|
||||
)
|
||||
raise SynapseError(400, "Server notices can only be sent to local users")
|
||||
|
||||
if not await self.admin_handler.get_user(target_user):
|
||||
raise NotFoundError("User not found")
|
||||
|
@ -104,7 +99,7 @@ class SendServerNoticeServlet(RestServlet):
|
|||
txn_id=txn_id,
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"event_id": event.event_id}
|
||||
return 200, {"event_id": event.event_id}
|
||||
|
||||
def on_PUT(
|
||||
self, request: SynapseRequest, txn_id: str
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
from http import HTTPStatus
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from synapse.api.errors import Codes, SynapseError
|
||||
|
@ -54,7 +53,7 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
UserSortOrder.DISPLAYNAME.value,
|
||||
):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Unknown value for order_by: %s" % (order_by,),
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -62,7 +61,7 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
start = parse_integer(request, "from", default=0)
|
||||
if start < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter from must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -70,7 +69,7 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
limit = parse_integer(request, "limit", default=100)
|
||||
if limit < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter limit must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -78,7 +77,7 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
from_ts = parse_integer(request, "from_ts", default=0)
|
||||
if from_ts < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter from_ts must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -87,13 +86,13 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
if until_ts is not None:
|
||||
if until_ts < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter until_ts must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
if until_ts <= from_ts:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter until_ts must be greater than from_ts.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -101,7 +100,7 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
search_term = parse_string(request, "search_term")
|
||||
if search_term == "":
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter search_term cannot be an empty string.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -109,9 +108,7 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
direction = parse_string(request, "dir", default="f")
|
||||
if direction not in ("f", "b"):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Unknown direction: %s" % (direction,),
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
400, "Unknown direction: %s" % (direction,), errcode=Codes.INVALID_PARAM
|
||||
)
|
||||
|
||||
users_media, total = await self.store.get_users_media_usage_paginate(
|
||||
|
@ -121,4 +118,4 @@ class UserMediaStatisticsRestServlet(RestServlet):
|
|||
if (start + limit) < total:
|
||||
ret["next_token"] = start + len(users_media)
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
|
|
@ -79,14 +79,14 @@ class UsersRestServletV2(RestServlet):
|
|||
|
||||
if start < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter from must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if limit < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Query parameter limit must be a string representing a positive integer.",
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -122,7 +122,7 @@ class UsersRestServletV2(RestServlet):
|
|||
if (start + limit) < total:
|
||||
ret["next_token"] = str(start + len(users))
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class UserRestServletV2(RestServlet):
|
||||
|
@ -172,14 +172,14 @@ class UserRestServletV2(RestServlet):
|
|||
|
||||
target_user = UserID.from_string(user_id)
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up local users")
|
||||
raise SynapseError(400, "Can only look up local users")
|
||||
|
||||
ret = await self.admin_handler.get_user(target_user)
|
||||
|
||||
if not ret:
|
||||
raise NotFoundError("User not found")
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
async def on_PUT(
|
||||
self, request: SynapseRequest, user_id: str
|
||||
|
@ -191,10 +191,7 @@ class UserRestServletV2(RestServlet):
|
|||
body = parse_json_object_from_request(request)
|
||||
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"This endpoint can only be used with local users",
|
||||
)
|
||||
raise SynapseError(400, "This endpoint can only be used with local users")
|
||||
|
||||
user = await self.admin_handler.get_user(target_user)
|
||||
user_id = target_user.to_string()
|
||||
|
@ -213,7 +210,7 @@ class UserRestServletV2(RestServlet):
|
|||
|
||||
user_type = body.get("user_type", None)
|
||||
if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid user type")
|
||||
raise SynapseError(400, "Invalid user type")
|
||||
|
||||
set_admin_to = body.get("admin", False)
|
||||
if not isinstance(set_admin_to, bool):
|
||||
|
@ -226,13 +223,11 @@ class UserRestServletV2(RestServlet):
|
|||
password = body.get("password", None)
|
||||
if password is not None:
|
||||
if not isinstance(password, str) or len(password) > 512:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password")
|
||||
raise SynapseError(400, "Invalid password")
|
||||
|
||||
deactivate = body.get("deactivated", False)
|
||||
if not isinstance(deactivate, bool):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "'deactivated' parameter is not of type boolean"
|
||||
)
|
||||
raise SynapseError(400, "'deactivated' parameter is not of type boolean")
|
||||
|
||||
# convert List[Dict[str, str]] into List[Tuple[str, str]]
|
||||
if external_ids is not None:
|
||||
|
@ -287,9 +282,7 @@ class UserRestServletV2(RestServlet):
|
|||
user_id,
|
||||
)
|
||||
except ExternalIDReuseException:
|
||||
raise SynapseError(
|
||||
HTTPStatus.CONFLICT, "External id is already in use."
|
||||
)
|
||||
raise SynapseError(409, "External id is already in use.")
|
||||
|
||||
if "avatar_url" in body and isinstance(body["avatar_url"], str):
|
||||
await self.profile_handler.set_avatar_url(
|
||||
|
@ -300,9 +293,7 @@ class UserRestServletV2(RestServlet):
|
|||
if set_admin_to != user["admin"]:
|
||||
auth_user = requester.user
|
||||
if target_user == auth_user and not set_admin_to:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "You may not demote yourself."
|
||||
)
|
||||
raise SynapseError(400, "You may not demote yourself.")
|
||||
|
||||
await self.store.set_server_admin(target_user, set_admin_to)
|
||||
|
||||
|
@ -328,8 +319,7 @@ class UserRestServletV2(RestServlet):
|
|||
and self.auth_handler.can_change_password()
|
||||
):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Must provide a password to re-activate an account.",
|
||||
400, "Must provide a password to re-activate an account."
|
||||
)
|
||||
|
||||
await self.deactivate_account_handler.activate_account(
|
||||
|
@ -342,7 +332,7 @@ class UserRestServletV2(RestServlet):
|
|||
user = await self.admin_handler.get_user(target_user)
|
||||
assert user is not None
|
||||
|
||||
return HTTPStatus.OK, user
|
||||
return 200, user
|
||||
|
||||
else: # create user
|
||||
displayname = body.get("displayname", None)
|
||||
|
@ -391,9 +381,7 @@ class UserRestServletV2(RestServlet):
|
|||
user_id,
|
||||
)
|
||||
except ExternalIDReuseException:
|
||||
raise SynapseError(
|
||||
HTTPStatus.CONFLICT, "External id is already in use."
|
||||
)
|
||||
raise SynapseError(409, "External id is already in use.")
|
||||
|
||||
if "avatar_url" in body and isinstance(body["avatar_url"], str):
|
||||
await self.profile_handler.set_avatar_url(
|
||||
|
@ -441,61 +429,51 @@ class UserRegisterServlet(RestServlet):
|
|||
|
||||
nonce = secrets.token_hex(64)
|
||||
self.nonces[nonce] = int(self.reactor.seconds())
|
||||
return HTTPStatus.OK, {"nonce": nonce}
|
||||
return 200, {"nonce": nonce}
|
||||
|
||||
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||
self._clear_old_nonces()
|
||||
|
||||
if not self.hs.config.registration.registration_shared_secret:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Shared secret registration is not enabled"
|
||||
)
|
||||
raise SynapseError(400, "Shared secret registration is not enabled")
|
||||
|
||||
body = parse_json_object_from_request(request)
|
||||
|
||||
if "nonce" not in body:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"nonce must be specified",
|
||||
errcode=Codes.BAD_JSON,
|
||||
)
|
||||
raise SynapseError(400, "nonce must be specified", errcode=Codes.BAD_JSON)
|
||||
|
||||
nonce = body["nonce"]
|
||||
|
||||
if nonce not in self.nonces:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "unrecognised nonce")
|
||||
raise SynapseError(400, "unrecognised nonce")
|
||||
|
||||
# Delete the nonce, so it can't be reused, even if it's invalid
|
||||
del self.nonces[nonce]
|
||||
|
||||
if "username" not in body:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"username must be specified",
|
||||
errcode=Codes.BAD_JSON,
|
||||
400, "username must be specified", errcode=Codes.BAD_JSON
|
||||
)
|
||||
else:
|
||||
if not isinstance(body["username"], str) or len(body["username"]) > 512:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid username")
|
||||
raise SynapseError(400, "Invalid username")
|
||||
|
||||
username = body["username"].encode("utf-8")
|
||||
if b"\x00" in username:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid username")
|
||||
raise SynapseError(400, "Invalid username")
|
||||
|
||||
if "password" not in body:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"password must be specified",
|
||||
errcode=Codes.BAD_JSON,
|
||||
400, "password must be specified", errcode=Codes.BAD_JSON
|
||||
)
|
||||
else:
|
||||
password = body["password"]
|
||||
if not isinstance(password, str) or len(password) > 512:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password")
|
||||
raise SynapseError(400, "Invalid password")
|
||||
|
||||
password_bytes = password.encode("utf-8")
|
||||
if b"\x00" in password_bytes:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password")
|
||||
raise SynapseError(400, "Invalid password")
|
||||
|
||||
password_hash = await self.auth_handler.hash(password)
|
||||
|
||||
|
@ -504,12 +482,10 @@ class UserRegisterServlet(RestServlet):
|
|||
displayname = body.get("displayname", None)
|
||||
|
||||
if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid user type")
|
||||
raise SynapseError(400, "Invalid user type")
|
||||
|
||||
if "mac" not in body:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "mac must be specified", errcode=Codes.BAD_JSON
|
||||
)
|
||||
raise SynapseError(400, "mac must be specified", errcode=Codes.BAD_JSON)
|
||||
|
||||
got_mac = body["mac"]
|
||||
|
||||
|
@ -531,7 +507,7 @@ class UserRegisterServlet(RestServlet):
|
|||
want_mac = want_mac_builder.hexdigest()
|
||||
|
||||
if not hmac.compare_digest(want_mac.encode("ascii"), got_mac.encode("ascii")):
|
||||
raise SynapseError(HTTPStatus.FORBIDDEN, "HMAC incorrect")
|
||||
raise SynapseError(403, "HMAC incorrect")
|
||||
|
||||
# Reuse the parts of RegisterRestServlet to reduce code duplication
|
||||
from synapse.rest.client.register import RegisterRestServlet
|
||||
|
@ -548,7 +524,7 @@ class UserRegisterServlet(RestServlet):
|
|||
)
|
||||
|
||||
result = await register._create_registration_details(user_id, body)
|
||||
return HTTPStatus.OK, result
|
||||
return 200, result
|
||||
|
||||
|
||||
class WhoisRestServlet(RestServlet):
|
||||
|
@ -576,11 +552,11 @@ class WhoisRestServlet(RestServlet):
|
|||
await assert_user_is_admin(self.auth, auth_user)
|
||||
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only whois a local user")
|
||||
raise SynapseError(400, "Can only whois a local user")
|
||||
|
||||
ret = await self.admin_handler.get_whois(target_user)
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class DeactivateAccountRestServlet(RestServlet):
|
||||
|
@ -599,9 +575,7 @@ class DeactivateAccountRestServlet(RestServlet):
|
|||
await assert_user_is_admin(self.auth, requester.user)
|
||||
|
||||
if not self.is_mine(UserID.from_string(target_user_id)):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Can only deactivate local users"
|
||||
)
|
||||
raise SynapseError(400, "Can only deactivate local users")
|
||||
|
||||
if not await self.store.get_user_by_id(target_user_id):
|
||||
raise NotFoundError("User not found")
|
||||
|
@ -623,7 +597,7 @@ class DeactivateAccountRestServlet(RestServlet):
|
|||
else:
|
||||
id_server_unbind_result = "no-support"
|
||||
|
||||
return HTTPStatus.OK, {"id_server_unbind_result": id_server_unbind_result}
|
||||
return 200, {"id_server_unbind_result": id_server_unbind_result}
|
||||
|
||||
|
||||
class AccountValidityRenewServlet(RestServlet):
|
||||
|
@ -646,7 +620,7 @@ class AccountValidityRenewServlet(RestServlet):
|
|||
|
||||
if "user_id" not in body:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"Missing property 'user_id' in the request body",
|
||||
)
|
||||
|
||||
|
@ -657,7 +631,7 @@ class AccountValidityRenewServlet(RestServlet):
|
|||
)
|
||||
|
||||
res = {"expiration_ts": expiration_ts}
|
||||
return HTTPStatus.OK, res
|
||||
return 200, res
|
||||
|
||||
|
||||
class ResetPasswordRestServlet(RestServlet):
|
||||
|
@ -704,7 +678,7 @@ class ResetPasswordRestServlet(RestServlet):
|
|||
await self._set_password_handler.set_password(
|
||||
target_user_id, new_password_hash, logout_devices, requester
|
||||
)
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class SearchUsersRestServlet(RestServlet):
|
||||
|
@ -738,16 +712,16 @@ class SearchUsersRestServlet(RestServlet):
|
|||
|
||||
# To allow all users to get the users list
|
||||
# if not is_admin and target_user != auth_user:
|
||||
# raise AuthError(HTTPStatus.FORBIDDEN, "You are not a server admin")
|
||||
# raise AuthError(403, "You are not a server admin")
|
||||
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only users a local user")
|
||||
raise SynapseError(400, "Can only users a local user")
|
||||
|
||||
term = parse_string(request, "term", required=True)
|
||||
logger.info("term: %s ", term)
|
||||
|
||||
ret = await self.store.search_users(term)
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class UserAdminServlet(RestServlet):
|
||||
|
@ -791,14 +765,11 @@ class UserAdminServlet(RestServlet):
|
|||
target_user = UserID.from_string(user_id)
|
||||
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Only local users can be admins of this homeserver",
|
||||
)
|
||||
raise SynapseError(400, "Only local users can be admins of this homeserver")
|
||||
|
||||
is_admin = await self.store.is_server_admin(target_user)
|
||||
|
||||
return HTTPStatus.OK, {"admin": is_admin}
|
||||
return 200, {"admin": is_admin}
|
||||
|
||||
async def on_PUT(
|
||||
self, request: SynapseRequest, user_id: str
|
||||
|
@ -814,19 +785,16 @@ class UserAdminServlet(RestServlet):
|
|||
assert_params_in_dict(body, ["admin"])
|
||||
|
||||
if not self.hs.is_mine(target_user):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
"Only local users can be admins of this homeserver",
|
||||
)
|
||||
raise SynapseError(400, "Only local users can be admins of this homeserver")
|
||||
|
||||
set_admin_to = bool(body["admin"])
|
||||
|
||||
if target_user == auth_user and not set_admin_to:
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "You may not demote yourself.")
|
||||
raise SynapseError(400, "You may not demote yourself.")
|
||||
|
||||
await self.store.set_server_admin(target_user, set_admin_to)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class UserMembershipRestServlet(RestServlet):
|
||||
|
@ -848,7 +816,7 @@ class UserMembershipRestServlet(RestServlet):
|
|||
|
||||
room_ids = await self.store.get_rooms_for_user(user_id)
|
||||
ret = {"joined_rooms": list(room_ids), "total": len(room_ids)}
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
|
||||
class PushersRestServlet(RestServlet):
|
||||
|
@ -877,7 +845,7 @@ class PushersRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.is_mine(UserID.from_string(user_id)):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up local users")
|
||||
raise SynapseError(400, "Can only look up local users")
|
||||
|
||||
if not await self.store.get_user_by_id(user_id):
|
||||
raise NotFoundError("User not found")
|
||||
|
@ -886,10 +854,7 @@ class PushersRestServlet(RestServlet):
|
|||
|
||||
filtered_pushers = [p.as_dict() for p in pushers]
|
||||
|
||||
return HTTPStatus.OK, {
|
||||
"pushers": filtered_pushers,
|
||||
"total": len(filtered_pushers),
|
||||
}
|
||||
return 200, {"pushers": filtered_pushers, "total": len(filtered_pushers)}
|
||||
|
||||
|
||||
class UserTokenRestServlet(RestServlet):
|
||||
|
@ -922,22 +887,16 @@ class UserTokenRestServlet(RestServlet):
|
|||
auth_user = requester.user
|
||||
|
||||
if not self.hs.is_mine_id(user_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Only local users can be logged in as"
|
||||
)
|
||||
raise SynapseError(400, "Only local users can be logged in as")
|
||||
|
||||
body = parse_json_object_from_request(request, allow_empty_body=True)
|
||||
|
||||
valid_until_ms = body.get("valid_until_ms")
|
||||
if valid_until_ms and not isinstance(valid_until_ms, int):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "'valid_until_ms' parameter must be an int"
|
||||
)
|
||||
raise SynapseError(400, "'valid_until_ms' parameter must be an int")
|
||||
|
||||
if auth_user.to_string() == user_id:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Cannot use admin API to login as self"
|
||||
)
|
||||
raise SynapseError(400, "Cannot use admin API to login as self")
|
||||
|
||||
token = await self.auth_handler.create_access_token_for_user_id(
|
||||
user_id=auth_user.to_string(),
|
||||
|
@ -946,7 +905,7 @@ class UserTokenRestServlet(RestServlet):
|
|||
puppets_user_id=user_id,
|
||||
)
|
||||
|
||||
return HTTPStatus.OK, {"access_token": token}
|
||||
return 200, {"access_token": token}
|
||||
|
||||
|
||||
class ShadowBanRestServlet(RestServlet):
|
||||
|
@ -988,13 +947,11 @@ class ShadowBanRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.hs.is_mine_id(user_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Only local users can be shadow-banned"
|
||||
)
|
||||
raise SynapseError(400, "Only local users can be shadow-banned")
|
||||
|
||||
await self.store.set_shadow_banned(UserID.from_string(user_id), True)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
async def on_DELETE(
|
||||
self, request: SynapseRequest, user_id: str
|
||||
|
@ -1002,13 +959,11 @@ class ShadowBanRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.hs.is_mine_id(user_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Only local users can be shadow-banned"
|
||||
)
|
||||
raise SynapseError(400, "Only local users can be shadow-banned")
|
||||
|
||||
await self.store.set_shadow_banned(UserID.from_string(user_id), False)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
||||
|
||||
class RateLimitRestServlet(RestServlet):
|
||||
|
@ -1040,7 +995,7 @@ class RateLimitRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.hs.is_mine_id(user_id):
|
||||
raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up local users")
|
||||
raise SynapseError(400, "Can only look up local users")
|
||||
|
||||
if not await self.store.get_user_by_id(user_id):
|
||||
raise NotFoundError("User not found")
|
||||
|
@ -1061,7 +1016,7 @@ class RateLimitRestServlet(RestServlet):
|
|||
else:
|
||||
ret = {}
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
async def on_POST(
|
||||
self, request: SynapseRequest, user_id: str
|
||||
|
@ -1069,9 +1024,7 @@ class RateLimitRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.hs.is_mine_id(user_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Only local users can be ratelimited"
|
||||
)
|
||||
raise SynapseError(400, "Only local users can be ratelimited")
|
||||
|
||||
if not await self.store.get_user_by_id(user_id):
|
||||
raise NotFoundError("User not found")
|
||||
|
@ -1083,14 +1036,14 @@ class RateLimitRestServlet(RestServlet):
|
|||
|
||||
if not isinstance(messages_per_second, int) or messages_per_second < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"%r parameter must be a positive int" % (messages_per_second,),
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
||||
if not isinstance(burst_count, int) or burst_count < 0:
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST,
|
||||
400,
|
||||
"%r parameter must be a positive int" % (burst_count,),
|
||||
errcode=Codes.INVALID_PARAM,
|
||||
)
|
||||
|
@ -1106,7 +1059,7 @@ class RateLimitRestServlet(RestServlet):
|
|||
"burst_count": ratelimit.burst_count,
|
||||
}
|
||||
|
||||
return HTTPStatus.OK, ret
|
||||
return 200, ret
|
||||
|
||||
async def on_DELETE(
|
||||
self, request: SynapseRequest, user_id: str
|
||||
|
@ -1114,13 +1067,11 @@ class RateLimitRestServlet(RestServlet):
|
|||
await assert_requester_is_admin(self.auth, request)
|
||||
|
||||
if not self.hs.is_mine_id(user_id):
|
||||
raise SynapseError(
|
||||
HTTPStatus.BAD_REQUEST, "Only local users can be ratelimited"
|
||||
)
|
||||
raise SynapseError(400, "Only local users can be ratelimited")
|
||||
|
||||
if not await self.store.get_user_by_id(user_id):
|
||||
raise NotFoundError("User not found")
|
||||
|
||||
await self.store.delete_ratelimit_for_user(user_id)
|
||||
|
||||
return HTTPStatus.OK, {}
|
||||
return 200, {}
|
||||
|
|
|
@ -14,17 +14,7 @@
|
|||
|
||||
import logging
|
||||
import re
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, List, Optional, Tuple
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
|
@ -38,6 +28,7 @@ from synapse.http.server import HttpServer, finish_request
|
|||
from synapse.http.servlet import (
|
||||
RestServlet,
|
||||
assert_params_in_dict,
|
||||
parse_boolean,
|
||||
parse_bytes_from_args,
|
||||
parse_json_object_from_request,
|
||||
parse_string,
|
||||
|
@ -72,7 +63,7 @@ class LoginRestServlet(RestServlet):
|
|||
JWT_TYPE_DEPRECATED = "m.login.jwt"
|
||||
APPSERVICE_TYPE = "m.login.application_service"
|
||||
APPSERVICE_TYPE_UNSTABLE = "uk.half-shot.msc2778.login.application_service"
|
||||
REFRESH_TOKEN_PARAM = "refresh_token"
|
||||
REFRESH_TOKEN_PARAM = "org.matrix.msc2918.refresh_token"
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
super().__init__()
|
||||
|
@ -90,7 +81,7 @@ class LoginRestServlet(RestServlet):
|
|||
self.saml2_enabled = hs.config.saml2.saml2_enabled
|
||||
self.cas_enabled = hs.config.cas.cas_enabled
|
||||
self.oidc_enabled = hs.config.oidc.oidc_enabled
|
||||
self._refresh_tokens_enabled = (
|
||||
self._msc2918_enabled = (
|
||||
hs.config.registration.refreshable_access_token_lifetime is not None
|
||||
)
|
||||
|
||||
|
@ -163,16 +154,14 @@ class LoginRestServlet(RestServlet):
|
|||
async def on_POST(self, request: SynapseRequest) -> Tuple[int, LoginResponse]:
|
||||
login_submission = parse_json_object_from_request(request)
|
||||
|
||||
# Check to see if the client requested a refresh token.
|
||||
client_requested_refresh_token = login_submission.get(
|
||||
LoginRestServlet.REFRESH_TOKEN_PARAM, False
|
||||
)
|
||||
if not isinstance(client_requested_refresh_token, bool):
|
||||
raise SynapseError(400, "`refresh_token` should be true or false.")
|
||||
|
||||
should_issue_refresh_token = (
|
||||
self._refresh_tokens_enabled and client_requested_refresh_token
|
||||
)
|
||||
if self._msc2918_enabled:
|
||||
# Check if this login should also issue a refresh token, as per
|
||||
# MSC2918
|
||||
should_issue_refresh_token = parse_boolean(
|
||||
request, name=LoginRestServlet.REFRESH_TOKEN_PARAM, default=False
|
||||
)
|
||||
else:
|
||||
should_issue_refresh_token = False
|
||||
|
||||
try:
|
||||
if login_submission["type"] in (
|
||||
|
@ -302,7 +291,6 @@ class LoginRestServlet(RestServlet):
|
|||
ratelimit: bool = True,
|
||||
auth_provider_id: Optional[str] = None,
|
||||
should_issue_refresh_token: bool = False,
|
||||
auth_provider_session_id: Optional[str] = None,
|
||||
) -> LoginResponse:
|
||||
"""Called when we've successfully authed the user and now need to
|
||||
actually login them in (e.g. create devices). This gets called on
|
||||
|
@ -318,10 +306,10 @@ class LoginRestServlet(RestServlet):
|
|||
create_non_existent_users: Whether to create the user if they don't
|
||||
exist. Defaults to False.
|
||||
ratelimit: Whether to ratelimit the login request.
|
||||
auth_provider_id: The SSO IdP the user used, if any.
|
||||
auth_provider_id: The SSO IdP the user used, if any (just used for the
|
||||
prometheus metrics).
|
||||
should_issue_refresh_token: True if this login should issue
|
||||
a refresh token alongside the access token.
|
||||
auth_provider_session_id: The session ID got during login from the SSO IdP.
|
||||
|
||||
Returns:
|
||||
result: Dictionary of account information after successful login.
|
||||
|
@ -354,7 +342,6 @@ class LoginRestServlet(RestServlet):
|
|||
initial_display_name,
|
||||
auth_provider_id=auth_provider_id,
|
||||
should_issue_refresh_token=should_issue_refresh_token,
|
||||
auth_provider_session_id=auth_provider_session_id,
|
||||
)
|
||||
|
||||
result = LoginResponse(
|
||||
|
@ -400,7 +387,6 @@ class LoginRestServlet(RestServlet):
|
|||
self.auth_handler._sso_login_callback,
|
||||
auth_provider_id=res.auth_provider_id,
|
||||
should_issue_refresh_token=should_issue_refresh_token,
|
||||
auth_provider_session_id=res.auth_provider_session_id,
|
||||
)
|
||||
|
||||
async def _do_jwt_login(
|
||||
|
@ -462,7 +448,9 @@ def _get_auth_flow_dict_for_idp(idp: SsoIdentityProvider) -> JsonDict:
|
|||
|
||||
|
||||
class RefreshTokenServlet(RestServlet):
|
||||
PATTERNS = (re.compile("^/_matrix/client/v1/refresh$"),)
|
||||
PATTERNS = client_patterns(
|
||||
"/org.matrix.msc2918.refresh_token/refresh$", releases=(), unstable=True
|
||||
)
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
self._auth_handler = hs.get_auth_handler()
|
||||
|
@ -470,7 +458,6 @@ class RefreshTokenServlet(RestServlet):
|
|||
self.refreshable_access_token_lifetime = (
|
||||
hs.config.registration.refreshable_access_token_lifetime
|
||||
)
|
||||
self.refresh_token_lifetime = hs.config.registration.refresh_token_lifetime
|
||||
|
||||
async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
|
||||
refresh_submission = parse_json_object_from_request(request)
|
||||
|
@ -480,32 +467,21 @@ class RefreshTokenServlet(RestServlet):
|
|||
if not isinstance(token, str):
|
||||
raise SynapseError(400, "Invalid param: refresh_token", Codes.INVALID_PARAM)
|
||||
|
||||
now = self._clock.time_msec()
|
||||
access_valid_until_ms = None
|
||||
if self.refreshable_access_token_lifetime is not None:
|
||||
access_valid_until_ms = now + self.refreshable_access_token_lifetime
|
||||
refresh_valid_until_ms = None
|
||||
if self.refresh_token_lifetime is not None:
|
||||
refresh_valid_until_ms = now + self.refresh_token_lifetime
|
||||
|
||||
(
|
||||
access_token,
|
||||
refresh_token,
|
||||
actual_access_token_expiry,
|
||||
) = await self._auth_handler.refresh_token(
|
||||
token, access_valid_until_ms, refresh_valid_until_ms
|
||||
valid_until_ms = (
|
||||
self._clock.time_msec() + self.refreshable_access_token_lifetime
|
||||
)
|
||||
access_token, refresh_token = await self._auth_handler.refresh_token(
|
||||
token, valid_until_ms
|
||||
)
|
||||
expires_in_ms = valid_until_ms - self._clock.time_msec()
|
||||
return (
|
||||
200,
|
||||
{
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
"expires_in_ms": expires_in_ms,
|
||||
},
|
||||
)
|
||||
|
||||
response: Dict[str, Union[str, int]] = {
|
||||
"access_token": access_token,
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
|
||||
# expires_in_ms is only present if the token expires
|
||||
if actual_access_token_expiry is not None:
|
||||
response["expires_in_ms"] = actual_access_token_expiry - now
|
||||
|
||||
return 200, response
|
||||
|
||||
|
||||
class SsoRedirectServlet(RestServlet):
|
||||
|
@ -513,7 +489,7 @@ class SsoRedirectServlet(RestServlet):
|
|||
re.compile(
|
||||
"^"
|
||||
+ CLIENT_API_PREFIX
|
||||
+ "/(r0|v3)/login/sso/redirect/(?P<idp_id>[A-Za-z0-9_.~-]+)$"
|
||||
+ "/r0/login/sso/redirect/(?P<idp_id>[A-Za-z0-9_.~-]+)$"
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ from synapse.http.server import HttpServer, finish_request, respond_with_html
|
|||
from synapse.http.servlet import (
|
||||
RestServlet,
|
||||
assert_params_in_dict,
|
||||
parse_boolean,
|
||||
parse_json_object_from_request,
|
||||
parse_string,
|
||||
)
|
||||
|
@ -419,7 +420,7 @@ class RegisterRestServlet(RestServlet):
|
|||
self.password_policy_handler = hs.get_password_policy_handler()
|
||||
self.clock = hs.get_clock()
|
||||
self._registration_enabled = self.hs.config.registration.enable_registration
|
||||
self._refresh_tokens_enabled = (
|
||||
self._msc2918_enabled = (
|
||||
hs.config.registration.refreshable_access_token_lifetime is not None
|
||||
)
|
||||
|
||||
|
@ -445,15 +446,14 @@ class RegisterRestServlet(RestServlet):
|
|||
f"Do not understand membership kind: {kind}",
|
||||
)
|
||||
|
||||
# Check if the clients wishes for this registration to issue a refresh
|
||||
# token.
|
||||
client_requested_refresh_tokens = body.get("refresh_token", False)
|
||||
if not isinstance(client_requested_refresh_tokens, bool):
|
||||
raise SynapseError(400, "`refresh_token` should be true or false.")
|
||||
|
||||
should_issue_refresh_token = (
|
||||
self._refresh_tokens_enabled and client_requested_refresh_tokens
|
||||
)
|
||||
if self._msc2918_enabled:
|
||||
# Check if this registration should also issue a refresh token, as
|
||||
# per MSC2918
|
||||
should_issue_refresh_token = parse_boolean(
|
||||
request, name="org.matrix.msc2918.refresh_token", default=False
|
||||
)
|
||||
else:
|
||||
should_issue_refresh_token = False
|
||||
|
||||
# Pull out the provided username and do basic sanity checks early since
|
||||
# the auth layer will store these in sessions.
|
||||
|
|
|
@ -224,14 +224,18 @@ class RelationPaginationServlet(RestServlet):
|
|||
)
|
||||
|
||||
now = self.clock.time_msec()
|
||||
# Do not bundle aggregations when retrieving the original event because
|
||||
# we want the content before relations are applied to it.
|
||||
# We set bundle_relations to False when retrieving the original
|
||||
# event because we want the content before relations were applied to
|
||||
# it.
|
||||
original_event = await self._event_serializer.serialize_event(
|
||||
event, now, bundle_aggregations=False
|
||||
event, now, bundle_relations=False
|
||||
)
|
||||
# Similarly, we don't allow relations to be applied to relations, so we
|
||||
# return the original relations without any aggregations on top of them
|
||||
# here.
|
||||
serialized_events = await self._event_serializer.serialize_events(
|
||||
events, now, bundle_relations=False
|
||||
)
|
||||
# The relations returned for the requested event do include their
|
||||
# bundled aggregations.
|
||||
serialized_events = await self._event_serializer.serialize_events(events, now)
|
||||
|
||||
return_value = pagination_chunk.to_dict()
|
||||
return_value["chunk"] = serialized_events
|
||||
|
|
|
@ -716,7 +716,10 @@ class RoomEventContextServlet(RestServlet):
|
|||
results["events_after"], time_now
|
||||
)
|
||||
results["state"] = await self._event_serializer.serialize_events(
|
||||
results["state"], time_now
|
||||
results["state"],
|
||||
time_now,
|
||||
# No need to bundle aggregations for state events
|
||||
bundle_relations=False,
|
||||
)
|
||||
|
||||
return 200, results
|
||||
|
@ -1067,62 +1070,6 @@ def register_txn_path(
|
|||
)
|
||||
|
||||
|
||||
class TimestampLookupRestServlet(RestServlet):
|
||||
"""
|
||||
API endpoint to fetch the `event_id` of the closest event to the given
|
||||
timestamp (`ts` query parameter) in the given direction (`dir` query
|
||||
parameter).
|
||||
|
||||
Useful for cases like jump to date so you can start paginating messages from
|
||||
a given date in the archive.
|
||||
|
||||
`ts` is a timestamp in milliseconds where we will find the closest event in
|
||||
the given direction.
|
||||
|
||||
`dir` can be `f` or `b` to indicate forwards and backwards in time from the
|
||||
given timestamp.
|
||||
|
||||
GET /_matrix/client/unstable/org.matrix.msc3030/rooms/<roomID>/timestamp_to_event?ts=<timestamp>&dir=<direction>
|
||||
{
|
||||
"event_id": ...
|
||||
}
|
||||
"""
|
||||
|
||||
PATTERNS = (
|
||||
re.compile(
|
||||
"^/_matrix/client/unstable/org.matrix.msc3030"
|
||||
"/rooms/(?P<room_id>[^/]*)/timestamp_to_event$"
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, hs: "HomeServer"):
|
||||
super().__init__()
|
||||
self._auth = hs.get_auth()
|
||||
self._store = hs.get_datastore()
|
||||
self.timestamp_lookup_handler = hs.get_timestamp_lookup_handler()
|
||||
|
||||
async def on_GET(
|
||||
self, request: SynapseRequest, room_id: str
|
||||
) -> Tuple[int, JsonDict]:
|
||||
requester = await self._auth.get_user_by_req(request)
|
||||
await self._auth.check_user_in_room(room_id, requester.user.to_string())
|
||||
|
||||
timestamp = parse_integer(request, "ts", required=True)
|
||||
direction = parse_string(request, "dir", default="f", allowed_values=["f", "b"])
|
||||
|
||||
(
|
||||
event_id,
|
||||
origin_server_ts,
|
||||
) = await self.timestamp_lookup_handler.get_event_for_timestamp(
|
||||
requester, room_id, timestamp, direction
|
||||
)
|
||||
|
||||
return 200, {
|
||||
"event_id": event_id,
|
||||
"origin_server_ts": origin_server_ts,
|
||||
}
|
||||
|
||||
|
||||
class RoomSpaceSummaryRestServlet(RestServlet):
|
||||
PATTERNS = (
|
||||
re.compile(
|
||||
|
@ -1193,7 +1140,7 @@ class RoomSpaceSummaryRestServlet(RestServlet):
|
|||
class RoomHierarchyRestServlet(RestServlet):
|
||||
PATTERNS = (
|
||||
re.compile(
|
||||
"^/_matrix/client/(v1|unstable/org.matrix.msc2946)"
|
||||
"^/_matrix/client/unstable/org.matrix.msc2946"
|
||||
"/rooms/(?P<room_id>[^/]*)/hierarchy$"
|
||||
),
|
||||
)
|
||||
|
@ -1221,7 +1168,7 @@ class RoomHierarchyRestServlet(RestServlet):
|
|||
)
|
||||
|
||||
return 200, await self._room_summary_handler.get_room_hierarchy(
|
||||
requester,
|
||||
requester.user.to_string(),
|
||||
room_id,
|
||||
suggested_only=parse_boolean(request, "suggested_only", default=False),
|
||||
max_depth=max_depth,
|
||||
|
@ -1292,8 +1239,6 @@ def register_servlets(
|
|||
RoomAliasListServlet(hs).register(http_server)
|
||||
SearchRestServlet(hs).register(http_server)
|
||||
RoomCreateRestServlet(hs).register(http_server)
|
||||
if hs.config.experimental.msc3030_enabled:
|
||||
TimestampLookupRestServlet(hs).register(http_server)
|
||||
|
||||
# Some servlets only get registered for the main process.
|
||||
if not is_worker:
|
||||
|
|
|
@ -520,9 +520,9 @@ class SyncRestServlet(RestServlet):
|
|||
return self._event_serializer.serialize_events(
|
||||
events,
|
||||
time_now=time_now,
|
||||
# Don't bother to bundle aggregations if the timeline is unlimited,
|
||||
# as clients will have all the necessary information.
|
||||
bundle_aggregations=room.timeline.limited,
|
||||
# We don't bundle "live" events, as otherwise clients
|
||||
# will end up double counting annotations.
|
||||
bundle_relations=False,
|
||||
token_id=token_id,
|
||||
event_format=event_formatter,
|
||||
only_event_fields=only_fields,
|
||||
|
|
|
@ -43,75 +43,47 @@ GetPathMethod = TypeVar(
|
|||
)
|
||||
|
||||
|
||||
def _wrap_with_jail_check(relative: bool) -> Callable[[GetPathMethod], GetPathMethod]:
|
||||
def _wrap_with_jail_check(func: GetPathMethod) -> GetPathMethod:
|
||||
"""Wraps a path-returning method to check that the returned path(s) do not escape
|
||||
the media store directory.
|
||||
|
||||
The path-returning method may return either a single path, or a list of paths.
|
||||
|
||||
The check is not expected to ever fail, unless `func` is missing a call to
|
||||
`_validate_path_component`, or `_validate_path_component` is buggy.
|
||||
|
||||
Args:
|
||||
relative: A boolean indicating whether the wrapped method returns paths relative
|
||||
to the media store directory.
|
||||
func: The `MediaFilePaths` method to wrap. The method may return either a single
|
||||
path, or a list of paths. Returned paths may be either absolute or relative.
|
||||
|
||||
Returns:
|
||||
A method which will wrap a path-returning method, adding a check to ensure that
|
||||
the returned path(s) lie within the media store directory. The check will raise
|
||||
a `ValueError` if it fails.
|
||||
The method, wrapped with a check to ensure that the returned path(s) lie within
|
||||
the media store directory. Raises a `ValueError` if the check fails.
|
||||
"""
|
||||
|
||||
def _wrap_with_jail_check_inner(func: GetPathMethod) -> GetPathMethod:
|
||||
@functools.wraps(func)
|
||||
def _wrapped(
|
||||
self: "MediaFilePaths", *args: Any, **kwargs: Any
|
||||
) -> Union[str, List[str]]:
|
||||
path_or_paths = func(self, *args, **kwargs)
|
||||
@functools.wraps(func)
|
||||
def _wrapped(
|
||||
self: "MediaFilePaths", *args: Any, **kwargs: Any
|
||||
) -> Union[str, List[str]]:
|
||||
path_or_paths = func(self, *args, **kwargs)
|
||||
|
||||
if isinstance(path_or_paths, list):
|
||||
paths_to_check = path_or_paths
|
||||
else:
|
||||
paths_to_check = [path_or_paths]
|
||||
if isinstance(path_or_paths, list):
|
||||
paths_to_check = path_or_paths
|
||||
else:
|
||||
paths_to_check = [path_or_paths]
|
||||
|
||||
for path in paths_to_check:
|
||||
# Construct the path that will ultimately be used.
|
||||
# We cannot guess whether `path` is relative to the media store
|
||||
# directory, since the media store directory may itself be a relative
|
||||
# path.
|
||||
if relative:
|
||||
path = os.path.join(self.base_path, path)
|
||||
normalized_path = os.path.normpath(path)
|
||||
for path in paths_to_check:
|
||||
# path may be an absolute or relative path, depending on the method being
|
||||
# wrapped. When "appending" an absolute path, `os.path.join` discards the
|
||||
# previous path, which is desired here.
|
||||
normalized_path = os.path.normpath(os.path.join(self.real_base_path, path))
|
||||
if (
|
||||
os.path.commonpath([normalized_path, self.real_base_path])
|
||||
!= self.real_base_path
|
||||
):
|
||||
raise ValueError(f"Invalid media store path: {path!r}")
|
||||
|
||||
# Now that `normpath` has eliminated `../`s and `./`s from the path,
|
||||
# `os.path.commonpath` can be used to check whether it lies within the
|
||||
# media store directory.
|
||||
if (
|
||||
os.path.commonpath([normalized_path, self.normalized_base_path])
|
||||
!= self.normalized_base_path
|
||||
):
|
||||
# The path resolves to outside the media store directory,
|
||||
# or `self.base_path` is `.`, which is an unlikely configuration.
|
||||
raise ValueError(f"Invalid media store path: {path!r}")
|
||||
return path_or_paths
|
||||
|
||||
# Note that `os.path.normpath`/`abspath` has a subtle caveat:
|
||||
# `a/b/c/../c` will normalize to `a/b/c`, but the former refers to a
|
||||
# different path if `a/b/c` is a symlink. That is, the check above is
|
||||
# not perfect and may allow a certain restricted subset of untrustworthy
|
||||
# paths through. Since the check above is secondary to the main
|
||||
# `_validate_path_component` checks, it's less important for it to be
|
||||
# perfect.
|
||||
#
|
||||
# As an alternative, `os.path.realpath` will resolve symlinks, but
|
||||
# proves problematic if there are symlinks inside the media store.
|
||||
# eg. if `url_store/` is symlinked to elsewhere, its canonical path
|
||||
# won't match that of the main media store directory.
|
||||
|
||||
return path_or_paths
|
||||
|
||||
return cast(GetPathMethod, _wrapped)
|
||||
|
||||
return _wrap_with_jail_check_inner
|
||||
return cast(GetPathMethod, _wrapped)
|
||||
|
||||
|
||||
ALLOWED_CHARACTERS = set(
|
||||
|
@ -155,7 +127,9 @@ class MediaFilePaths:
|
|||
|
||||
def __init__(self, primary_base_path: str):
|
||||
self.base_path = primary_base_path
|
||||
self.normalized_base_path = os.path.normpath(self.base_path)
|
||||
|
||||
# The media store directory, with all symlinks resolved.
|
||||
self.real_base_path = os.path.realpath(primary_base_path)
|
||||
|
||||
# Refuse to initialize if paths cannot be validated correctly for the current
|
||||
# platform.
|
||||
|
@ -166,7 +140,7 @@ class MediaFilePaths:
|
|||
# for certain homeservers there, since ":"s aren't allowed in paths.
|
||||
assert os.name == "posix"
|
||||
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def local_media_filepath_rel(self, media_id: str) -> str:
|
||||
return os.path.join(
|
||||
"local_content",
|
||||
|
@ -177,7 +151,7 @@ class MediaFilePaths:
|
|||
|
||||
local_media_filepath = _wrap_in_base_path(local_media_filepath_rel)
|
||||
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def local_media_thumbnail_rel(
|
||||
self, media_id: str, width: int, height: int, content_type: str, method: str
|
||||
) -> str:
|
||||
|
@ -193,7 +167,7 @@ class MediaFilePaths:
|
|||
|
||||
local_media_thumbnail = _wrap_in_base_path(local_media_thumbnail_rel)
|
||||
|
||||
@_wrap_with_jail_check(relative=False)
|
||||
@_wrap_with_jail_check
|
||||
def local_media_thumbnail_dir(self, media_id: str) -> str:
|
||||
"""
|
||||
Retrieve the local store path of thumbnails of a given media_id
|
||||
|
@ -211,7 +185,7 @@ class MediaFilePaths:
|
|||
_validate_path_component(media_id[4:]),
|
||||
)
|
||||
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def remote_media_filepath_rel(self, server_name: str, file_id: str) -> str:
|
||||
return os.path.join(
|
||||
"remote_content",
|
||||
|
@ -223,7 +197,7 @@ class MediaFilePaths:
|
|||
|
||||
remote_media_filepath = _wrap_in_base_path(remote_media_filepath_rel)
|
||||
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def remote_media_thumbnail_rel(
|
||||
self,
|
||||
server_name: str,
|
||||
|
@ -249,7 +223,7 @@ class MediaFilePaths:
|
|||
# Legacy path that was used to store thumbnails previously.
|
||||
# Should be removed after some time, when most of the thumbnails are stored
|
||||
# using the new path.
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def remote_media_thumbnail_rel_legacy(
|
||||
self, server_name: str, file_id: str, width: int, height: int, content_type: str
|
||||
) -> str:
|
||||
|
@ -264,7 +238,6 @@ class MediaFilePaths:
|
|||
_validate_path_component(file_name),
|
||||
)
|
||||
|
||||
@_wrap_with_jail_check(relative=False)
|
||||
def remote_media_thumbnail_dir(self, server_name: str, file_id: str) -> str:
|
||||
return os.path.join(
|
||||
self.base_path,
|
||||
|
@ -275,7 +248,7 @@ class MediaFilePaths:
|
|||
_validate_path_component(file_id[4:]),
|
||||
)
|
||||
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def url_cache_filepath_rel(self, media_id: str) -> str:
|
||||
if NEW_FORMAT_ID_RE.match(media_id):
|
||||
# Media id is of the form <DATE><RANDOM_STRING>
|
||||
|
@ -295,7 +268,7 @@ class MediaFilePaths:
|
|||
|
||||
url_cache_filepath = _wrap_in_base_path(url_cache_filepath_rel)
|
||||
|
||||
@_wrap_with_jail_check(relative=False)
|
||||
@_wrap_with_jail_check
|
||||
def url_cache_filepath_dirs_to_delete(self, media_id: str) -> List[str]:
|
||||
"The dirs to try and remove if we delete the media_id file"
|
||||
if NEW_FORMAT_ID_RE.match(media_id):
|
||||
|
@ -317,7 +290,7 @@ class MediaFilePaths:
|
|||
),
|
||||
]
|
||||
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def url_cache_thumbnail_rel(
|
||||
self, media_id: str, width: int, height: int, content_type: str, method: str
|
||||
) -> str:
|
||||
|
@ -345,7 +318,7 @@ class MediaFilePaths:
|
|||
|
||||
url_cache_thumbnail = _wrap_in_base_path(url_cache_thumbnail_rel)
|
||||
|
||||
@_wrap_with_jail_check(relative=True)
|
||||
@_wrap_with_jail_check
|
||||
def url_cache_thumbnail_directory_rel(self, media_id: str) -> str:
|
||||
# Media id is of the form <DATE><RANDOM_STRING>
|
||||
# E.g.: 2017-09-28-fsdRDt24DS234dsf
|
||||
|
@ -368,7 +341,7 @@ class MediaFilePaths:
|
|||
url_cache_thumbnail_directory_rel
|
||||
)
|
||||
|
||||
@_wrap_with_jail_check(relative=False)
|
||||
@_wrap_with_jail_check
|
||||
def url_cache_thumbnail_dirs_to_delete(self, media_id: str) -> List[str]:
|
||||
"The dirs to try and remove if we delete the media_id thumbnails"
|
||||
# Media id is of the form <DATE><RANDOM_STRING>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue