1# Copyright 2014-2016 OpenMarket Ltd 2# Copyright 2018-2019 New Vector Ltd 3# Copyright 2020, 2021 The Matrix.org Foundation C.I.C. 4 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17 18import logging 19import platform 20from http import HTTPStatus 21from typing import TYPE_CHECKING, Optional, Tuple 22 23import synapse 24from synapse.api.errors import Codes, NotFoundError, SynapseError 25from synapse.http.server import HttpServer, JsonResource 26from synapse.http.servlet import RestServlet, parse_json_object_from_request 27from synapse.http.site import SynapseRequest 28from synapse.rest.admin._base import admin_patterns, assert_requester_is_admin 29from synapse.rest.admin.background_updates import ( 30 BackgroundUpdateEnabledRestServlet, 31 BackgroundUpdateRestServlet, 32 BackgroundUpdateStartJobRestServlet, 33) 34from synapse.rest.admin.devices import ( 35 DeleteDevicesRestServlet, 36 DeviceRestServlet, 37 DevicesRestServlet, 38) 39from synapse.rest.admin.event_reports import ( 40 EventReportDetailRestServlet, 41 EventReportsRestServlet, 42) 43from synapse.rest.admin.federation import ( 44 DestinationsRestServlet, 45 ListDestinationsRestServlet, 46) 47from synapse.rest.admin.groups import DeleteGroupAdminRestServlet 48from synapse.rest.admin.media import ListMediaInRoom, register_servlets_for_media_repo 49from synapse.rest.admin.registration_tokens import ( 50 ListRegistrationTokensRestServlet, 51 NewRegistrationTokenRestServlet, 52 RegistrationTokenRestServlet, 53) 54from synapse.rest.admin.rooms import ( 55 BlockRoomRestServlet, 56 DeleteRoomStatusByDeleteIdRestServlet, 57 DeleteRoomStatusByRoomIdRestServlet, 58 ForwardExtremitiesRestServlet, 59 JoinRoomAliasServlet, 60 ListRoomRestServlet, 61 MakeRoomAdminRestServlet, 62 RoomEventContextServlet, 63 RoomMembersRestServlet, 64 RoomRestServlet, 65 RoomRestV2Servlet, 66 RoomStateRestServlet, 67) 68from synapse.rest.admin.server_notice_servlet import SendServerNoticeServlet 69from synapse.rest.admin.statistics import UserMediaStatisticsRestServlet 70from synapse.rest.admin.username_available import UsernameAvailableRestServlet 71from synapse.rest.admin.users import ( 72 AccountDataRestServlet, 73 AccountValidityRenewServlet, 74 DeactivateAccountRestServlet, 75 PushersRestServlet, 76 RateLimitRestServlet, 77 ResetPasswordRestServlet, 78 SearchUsersRestServlet, 79 ShadowBanRestServlet, 80 UserAdminServlet, 81 UserMembershipRestServlet, 82 UserRegisterServlet, 83 UserRestServletV2, 84 UsersRestServletV2, 85 UserTokenRestServlet, 86 WhoisRestServlet, 87) 88from synapse.types import JsonDict, RoomStreamToken 89from synapse.util.versionstring import get_version_string 90 91if TYPE_CHECKING: 92 from synapse.server import HomeServer 93 94logger = logging.getLogger(__name__) 95 96 97class VersionServlet(RestServlet): 98 PATTERNS = admin_patterns("/server_version$") 99 100 def __init__(self, hs: "HomeServer"): 101 self.res = { 102 "server_version": get_version_string(synapse), 103 "python_version": platform.python_version(), 104 } 105 106 def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: 107 return HTTPStatus.OK, self.res 108 109 110class PurgeHistoryRestServlet(RestServlet): 111 PATTERNS = admin_patterns( 112 "/purge_history/(?P<room_id>[^/]*)(/(?P<event_id>[^/]*))?$" 113 ) 114 115 def __init__(self, hs: "HomeServer"): 116 self.pagination_handler = hs.get_pagination_handler() 117 self.store = hs.get_datastore() 118 self.auth = hs.get_auth() 119 120 async def on_POST( 121 self, request: SynapseRequest, room_id: str, event_id: Optional[str] 122 ) -> Tuple[int, JsonDict]: 123 await assert_requester_is_admin(self.auth, request) 124 125 body = parse_json_object_from_request(request, allow_empty_body=True) 126 127 delete_local_events = bool(body.get("delete_local_events", False)) 128 129 # establish the topological ordering we should keep events from. The 130 # user can provide an event_id in the URL or the request body, or can 131 # provide a timestamp in the request body. 132 if event_id is None: 133 event_id = body.get("purge_up_to_event_id") 134 135 if event_id is not None: 136 event = await self.store.get_event(event_id) 137 138 if event.room_id != room_id: 139 raise SynapseError(HTTPStatus.BAD_REQUEST, "Event is for wrong room.") 140 141 # RoomStreamToken expects [int] not Optional[int] 142 assert event.internal_metadata.stream_ordering is not None 143 room_token = RoomStreamToken( 144 event.depth, event.internal_metadata.stream_ordering 145 ) 146 token = await room_token.to_string(self.store) 147 148 logger.info("[purge] purging up to token %s (event_id %s)", token, event_id) 149 elif "purge_up_to_ts" in body: 150 ts = body["purge_up_to_ts"] 151 if not isinstance(ts, int): 152 raise SynapseError( 153 HTTPStatus.BAD_REQUEST, 154 "purge_up_to_ts must be an int", 155 errcode=Codes.BAD_JSON, 156 ) 157 158 stream_ordering = await self.store.find_first_stream_ordering_after_ts(ts) 159 160 r = await self.store.get_room_event_before_stream_ordering( 161 room_id, stream_ordering 162 ) 163 if not r: 164 logger.warning( 165 "[purge] purging events not possible: No event found " 166 "(received_ts %i => stream_ordering %i)", 167 ts, 168 stream_ordering, 169 ) 170 raise SynapseError( 171 HTTPStatus.NOT_FOUND, 172 "there is no event to be purged", 173 errcode=Codes.NOT_FOUND, 174 ) 175 (stream, topo, _event_id) = r 176 token = "t%d-%d" % (topo, stream) 177 logger.info( 178 "[purge] purging up to token %s (received_ts %i => " 179 "stream_ordering %i)", 180 token, 181 ts, 182 stream_ordering, 183 ) 184 else: 185 raise SynapseError( 186 HTTPStatus.BAD_REQUEST, 187 "must specify purge_up_to_event_id or purge_up_to_ts", 188 errcode=Codes.BAD_JSON, 189 ) 190 191 purge_id = self.pagination_handler.start_purge_history( 192 room_id, token, delete_local_events=delete_local_events 193 ) 194 195 return HTTPStatus.OK, {"purge_id": purge_id} 196 197 198class PurgeHistoryStatusRestServlet(RestServlet): 199 PATTERNS = admin_patterns("/purge_history_status/(?P<purge_id>[^/]*)$") 200 201 def __init__(self, hs: "HomeServer"): 202 self.pagination_handler = hs.get_pagination_handler() 203 self.auth = hs.get_auth() 204 205 async def on_GET( 206 self, request: SynapseRequest, purge_id: str 207 ) -> Tuple[int, JsonDict]: 208 await assert_requester_is_admin(self.auth, request) 209 210 purge_status = self.pagination_handler.get_purge_status(purge_id) 211 if purge_status is None: 212 raise NotFoundError("purge id '%s' not found" % purge_id) 213 214 return HTTPStatus.OK, purge_status.asdict() 215 216 217######################################################################################## 218# 219# please don't add more servlets here: this file is already long and unwieldy. Put 220# them in separate files within the 'admin' package. 221# 222######################################################################################## 223 224 225class AdminRestResource(JsonResource): 226 """The REST resource which gets mounted at /_synapse/admin""" 227 228 def __init__(self, hs: "HomeServer"): 229 JsonResource.__init__(self, hs, canonical_json=False) 230 register_servlets(hs, self) 231 232 233def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: 234 """ 235 Register all the admin servlets. 236 """ 237 register_servlets_for_client_rest_resource(hs, http_server) 238 BlockRoomRestServlet(hs).register(http_server) 239 ListRoomRestServlet(hs).register(http_server) 240 RoomStateRestServlet(hs).register(http_server) 241 RoomRestServlet(hs).register(http_server) 242 RoomRestV2Servlet(hs).register(http_server) 243 RoomMembersRestServlet(hs).register(http_server) 244 DeleteRoomStatusByDeleteIdRestServlet(hs).register(http_server) 245 DeleteRoomStatusByRoomIdRestServlet(hs).register(http_server) 246 JoinRoomAliasServlet(hs).register(http_server) 247 VersionServlet(hs).register(http_server) 248 UserAdminServlet(hs).register(http_server) 249 UserMembershipRestServlet(hs).register(http_server) 250 UserTokenRestServlet(hs).register(http_server) 251 UserRestServletV2(hs).register(http_server) 252 UsersRestServletV2(hs).register(http_server) 253 DeviceRestServlet(hs).register(http_server) 254 DevicesRestServlet(hs).register(http_server) 255 DeleteDevicesRestServlet(hs).register(http_server) 256 UserMediaStatisticsRestServlet(hs).register(http_server) 257 EventReportDetailRestServlet(hs).register(http_server) 258 EventReportsRestServlet(hs).register(http_server) 259 AccountDataRestServlet(hs).register(http_server) 260 PushersRestServlet(hs).register(http_server) 261 MakeRoomAdminRestServlet(hs).register(http_server) 262 ShadowBanRestServlet(hs).register(http_server) 263 ForwardExtremitiesRestServlet(hs).register(http_server) 264 RoomEventContextServlet(hs).register(http_server) 265 RateLimitRestServlet(hs).register(http_server) 266 UsernameAvailableRestServlet(hs).register(http_server) 267 ListRegistrationTokensRestServlet(hs).register(http_server) 268 NewRegistrationTokenRestServlet(hs).register(http_server) 269 RegistrationTokenRestServlet(hs).register(http_server) 270 DestinationsRestServlet(hs).register(http_server) 271 ListDestinationsRestServlet(hs).register(http_server) 272 273 # Some servlets only get registered for the main process. 274 if hs.config.worker.worker_app is None: 275 SendServerNoticeServlet(hs).register(http_server) 276 BackgroundUpdateEnabledRestServlet(hs).register(http_server) 277 BackgroundUpdateRestServlet(hs).register(http_server) 278 BackgroundUpdateStartJobRestServlet(hs).register(http_server) 279 280 281def register_servlets_for_client_rest_resource( 282 hs: "HomeServer", http_server: HttpServer 283) -> None: 284 """Register only the servlets which need to be exposed on /_matrix/client/xxx""" 285 WhoisRestServlet(hs).register(http_server) 286 PurgeHistoryStatusRestServlet(hs).register(http_server) 287 DeactivateAccountRestServlet(hs).register(http_server) 288 PurgeHistoryRestServlet(hs).register(http_server) 289 ResetPasswordRestServlet(hs).register(http_server) 290 SearchUsersRestServlet(hs).register(http_server) 291 UserRegisterServlet(hs).register(http_server) 292 DeleteGroupAdminRestServlet(hs).register(http_server) 293 AccountValidityRenewServlet(hs).register(http_server) 294 295 # Load the media repo ones if we're using them. Otherwise load the servlets which 296 # don't need a media repo (typically readonly admin APIs). 297 if hs.config.media.can_load_media_repo: 298 register_servlets_for_media_repo(hs, http_server) 299 else: 300 ListMediaInRoom(hs).register(http_server) 301 302 # don't add more things here: new servlets should only be exposed on 303 # /_synapse/admin so should not go here. Instead register them in AdminRestResource. 304