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