class QuarantineMediaInRoom(RestServlet): """Quarantines all media in a room so that no one can download it via this server. """ PATTERNS = ( admin_patterns("/room/(?P<room_id>[^/]+)/media/quarantine") + # This path kept around for legacy reasons admin_patterns("/quarantine_media/(?P<room_id>[^/]+)")) def __init__(self, hs: "HomeServer"): self.store = hs.get_datastore() self.auth = hs.get_auth() async def on_POST(self, request: Request, room_id: str) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) logging.info("Quarantining room: %s", room_id) # Quarantine all media in this room num_quarantined = await self.store.quarantine_media_ids_in_room( room_id, requester.user.to_string()) return 200, {"num_quarantined": num_quarantined}
def register(self, json_resource: HttpServer) -> None: PATTERN = "/send_server_notice" json_resource.register_paths("POST", admin_patterns(PATTERN + "$"), self.on_POST, self.__class__.__name__) json_resource.register_paths( "PUT", admin_patterns(PATTERN + "/(?P<txn_id>[^/]*)$"), self.on_PUT, self.__class__.__name__, )
class RoomRestServlet(RestServlet): """Get room details. TODO: Add on_POST to allow room creation without joining the room """ PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]+)$") def __init__(self, hs: "HomeServer"): self.hs = hs self.auth = hs.get_auth() self.store = hs.get_datastore() async def on_GET(self, request: SynapseRequest, room_id: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) ret = await self.store.get_room_with_stats(room_id) if not ret: raise NotFoundError("Room not found") members = await self.store.get_users_in_room(room_id) ret["joined_local_devices"] = await self.store.count_devices_by_users( members) return (200, ret)
class RoomStateRestServlet(RestServlet): """ Get full state within a room. """ PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]*)/state$") def __init__(self, hs: "HomeServer"): self.auth = hs.get_auth() self.store = hs.get_datastore() self.clock = hs.get_clock() self._event_serializer = hs.get_event_client_serializer() async def on_GET(self, request: SynapseRequest, room_id: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) ret = await self.store.get_room(room_id) if not ret: raise NotFoundError("Room not found") 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 = self._event_serializer.serialize_events( events.values(), now) ret = {"state": room_state} return HTTPStatus.OK, ret
class DeleteRoomStatusByRoomIdRestServlet(RestServlet): """Get the status of the delete room background task.""" PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]*)/delete_status$", "v2") def __init__(self, hs: "HomeServer"): self._auth = hs.get_auth() self._pagination_handler = hs.get_pagination_handler() async def on_GET(self, request: SynapseRequest, room_id: str) -> Tuple[int, JsonDict]: 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, )) delete_ids = self._pagination_handler.get_delete_ids_by_room(room_id) if delete_ids is None: raise NotFoundError("No delete task for room_id '%s' found" % room_id) response = [] for delete_id in delete_ids: delete = self._pagination_handler.get_delete_status(delete_id) if delete: response += [{ "delete_id": delete_id, **delete.asdict(), }] return HTTPStatus.OK, {"results": cast(JsonDict, response)}
class ShadowBanRestServlet(RestServlet): """An admin API for shadow-banning a user. A shadow-banned users receives successful responses to their client-server API requests, but the events are not propagated into rooms. Shadow-banning a user should be used as a tool of last resort and may lead to confusing or broken behaviour for the client. Example: POST /_synapse/admin/v1/users/@test:example.com/shadow_ban {} 200 OK {} """ PATTERNS = admin_patterns("/users/(?P<user_id>[^/]*)/shadow_ban") def __init__(self, hs: "HomeServer"): self.hs = hs self.store = hs.get_datastore() self.auth = hs.get_auth() async def on_POST(self, request, user_id): await assert_requester_is_admin(self.auth, request) if not self.hs.is_mine_id(user_id): raise SynapseError(400, "Only local users can be shadow-banned") await self.store.set_shadow_banned(UserID.from_string(user_id), True) return 200, {}
class PurgeRoomServlet(RestServlet): """Servlet which will remove all trace of a room from the database POST /_synapse/admin/v1/purge_room { "room_id": "!room:id" } returns: {} """ PATTERNS = admin_patterns("/purge_room$") def __init__(self, hs: "HomeServer"): self.hs = hs self.auth = hs.get_auth() self.pagination_handler = hs.get_pagination_handler() async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) body = parse_json_object_from_request(request) assert_params_in_dict(body, ("room_id", )) await self.pagination_handler.purge_room(body["room_id"]) return 200, {}
class ShutdownRoomRestServlet(RestServlet): """Shuts down a room by removing all local users from the room and blocking all future invites and joins to the room. Any local aliases will be repointed to a new room created by `new_room_user_id` and kicked users will be auto joined to the new room. """ PATTERNS = admin_patterns("/shutdown_room/(?P<room_id>[^/]+)") def __init__(self, hs: "HomeServer"): self.hs = hs self.auth = hs.get_auth() self.room_shutdown_handler = hs.get_room_shutdown_handler() async def on_POST(self, request: SynapseRequest, room_id: str) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) content = parse_json_object_from_request(request) assert_params_in_dict(content, ["new_room_user_id"]) ret = await self.room_shutdown_handler.shutdown_room( room_id=room_id, new_room_user_id=content["new_room_user_id"], new_room_name=content.get("room_name"), message=content.get("message"), requester_user_id=requester.user.to_string(), block=True, ) return (200, ret)
class DeleteRoomRestServlet(RestServlet): """Delete a room from server. It is a combination and improvement of shutdown and purge room. Shuts down a room by removing all local users from the room. Blocking all future invites and joins to the room is optional. If desired any local aliases will be repointed to a new room created by `new_room_user_id` and kicked users will be auto- joined to the new room. If 'purge' is true, it will remove all traces of a room from the database. """ PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]+)/delete$") def __init__(self, hs: "HomeServer"): self.hs = hs self.auth = hs.get_auth() self.room_shutdown_handler = hs.get_room_shutdown_handler() self.pagination_handler = hs.get_pagination_handler() async def on_POST( self, request: SynapseRequest, room_id: str ) -> Tuple[int, JsonDict]: return await _delete_room( request, room_id, self.auth, self.room_shutdown_handler, self.pagination_handler, )
class AccountValidityRenewServlet(RestServlet): PATTERNS = admin_patterns("/account_validity/validity$") def __init__(self, hs): """ Args: hs (synapse.server.HomeServer): server """ self.hs = hs self.account_activity_handler = hs.get_account_validity_handler() self.auth = hs.get_auth() async def on_POST(self, request): await assert_requester_is_admin(self.auth, request) body = parse_json_object_from_request(request) if "user_id" not in body: raise SynapseError(400, "Missing property 'user_id' in the request body") expiration_ts = await self.account_activity_handler.renew_account_for_user( body["user_id"], body.get("expiration_ts"), not body.get("enable_renewal_emails", True), ) res = {"expiration_ts": expiration_ts} return 200, res
class WhoisRestServlet(RestServlet): path_regex = "/whois/(?P<user_id>[^/]*)$" PATTERNS = ( admin_patterns(path_regex) + # URL for spec reason # https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-admin-whois-userid client_patterns("/admin" + path_regex, v1=True) ) def __init__(self, hs): self.hs = hs self.auth = hs.get_auth() self.admin_handler = hs.get_admin_handler() async def on_GET(self, request, user_id): target_user = UserID.from_string(user_id) requester = await self.auth.get_user_by_req(request) auth_user = requester.user if target_user != auth_user: await assert_user_is_admin(self.auth, auth_user) if not self.hs.is_mine(target_user): raise SynapseError(400, "Can only whois a local user") ret = await self.admin_handler.get_whois(target_user) return 200, ret
class BackgroundUpdateRestServlet(RestServlet): """Fetch information about background updates""" PATTERNS = admin_patterns("/background_updates/status$") def __init__(self, hs: "HomeServer"): self._auth = hs.get_auth() self._data_stores = hs.get_datastores() async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self._auth, request) # We need to check that all configured databases have updates enabled. # (They *should* all be in sync.) enabled = all(db.updates.enabled for db in self._data_stores.databases) current_updates = {} for db in self._data_stores.databases: update = db.updates.get_current_update() if not update: continue current_updates[db.name()] = { "name": update.name, "total_item_count": update.total_item_count, "total_duration_ms": update.total_duration_ms, "average_items_per_ms": update.average_items_per_ms(), } return HTTPStatus.OK, { "enabled": enabled, "current_updates": current_updates }
class PurgeMediaCacheRestServlet(RestServlet): PATTERNS = admin_patterns("/purge_media_cache$") def __init__(self, hs: "HomeServer"): self.media_repository = hs.get_media_repository() self.auth = hs.get_auth() async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) before_ts = parse_integer(request, "before_ts", required=True) logger.info("before_ts: %r", before_ts) if before_ts < 0: raise SynapseError( HTTPStatus.BAD_REQUEST, "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, "Query parameter before_ts you provided is from the year 1970. " + "Double check that you are providing a timestamp in milliseconds.", errcode=Codes.INVALID_PARAM, ) ret = await self.media_repository.delete_old_remote_media(before_ts) return HTTPStatus.OK, ret
class DevicesRestServlet(RestServlet): """ Retrieve the given user's devices """ PATTERNS = admin_patterns("/users/(?P<user_id>[^/]*)/devices$", "v2") def __init__(self, hs: "HomeServer"): self.auth = hs.get_auth() self.device_handler = hs.get_device_handler() self.store = hs.get_datastore() self.is_mine = hs.is_mine async def on_GET(self, request: SynapseRequest, user_id: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(user_id) if not self.is_mine(target_user): raise SynapseError(HTTPStatus.BAD_REQUEST, "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)}
class RoomMembersRestServlet(RestServlet): """ Get members list of a room. """ PATTERNS = admin_patterns("/rooms/(?P<room_id>[^/]+)/members") def __init__(self, hs: "HomeServer"): self.hs = hs self.auth = hs.get_auth() self.store = hs.get_datastore() async def on_GET( self, request: SynapseRequest, room_id: str ) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) ret = await self.store.get_room(room_id) if not ret: raise NotFoundError("Room not found") members = await self.store.get_users_in_room(room_id) ret = {"members": members, "total": len(members)} return 200, ret
class RoomEventContextServlet(RestServlet): """ Provide the context for an event. This API is designed to be used when system administrators wish to look at an abuse report and understand what happened during and immediately prior to this event. """ PATTERNS = admin_patterns( "/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$") def __init__(self, hs): super().__init__() self.clock = hs.get_clock() self.room_context_handler = hs.get_room_context_handler() self._event_serializer = hs.get_event_client_serializer() self.auth = hs.get_auth() async def on_GET(self, request, room_id, event_id): requester = await self.auth.get_user_by_req(request, allow_guest=False) await assert_user_is_admin(self.auth, requester.user) limit = parse_integer(request, "limit", default=10) # picking the API shape for symmetry with /messages filter_str = parse_string(request, b"filter", encoding="utf-8") if filter_str: filter_json = urlparse.unquote(filter_str) event_filter = Filter( json_decoder.decode(filter_json)) # type: Optional[Filter] else: event_filter = None results = await self.room_context_handler.get_event_context( requester, room_id, event_id, limit, event_filter, use_admin_priviledge=True, ) if not results: 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( results["events_before"], time_now) results["event"] = await self._event_serializer.serialize_event( results["event"], time_now) results[ "events_after"] = await self._event_serializer.serialize_events( results["events_after"], time_now) results["state"] = await self._event_serializer.serialize_events( results["state"], time_now) return 200, results
class AccountValidityRenewServlet(RestServlet): PATTERNS = admin_patterns("/account_validity/validity$") def __init__(self, hs: "HomeServer"): self.account_activity_handler = hs.get_account_validity_handler() self.auth = hs.get_auth() async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) if self.account_activity_handler.on_legacy_admin_request_callback: expiration_ts = await (self.account_activity_handler. on_legacy_admin_request_callback(request)) else: body = parse_json_object_from_request(request) if "user_id" not in body: raise SynapseError( HTTPStatus.BAD_REQUEST, "Missing property 'user_id' in the request body", ) expiration_ts = await self.account_activity_handler.renew_account_for_user( body["user_id"], body.get("expiration_ts"), not body.get("enable_renewal_emails", True), ) res = {"expiration_ts": expiration_ts} return HTTPStatus.OK, res
class WhoisRestServlet(RestServlet): path_regex = "/whois/(?P<user_id>[^/]*)$" PATTERNS = [ *admin_patterns(path_regex), # URL for spec reason # https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-admin-whois-userid *client_patterns("/admin" + path_regex, v1=True), ] def __init__(self, hs: "HomeServer"): self.auth = hs.get_auth() self.admin_handler = hs.get_admin_handler() self.is_mine = hs.is_mine async def on_GET(self, request: SynapseRequest, user_id: str) -> Tuple[int, JsonDict]: target_user = UserID.from_string(user_id) requester = await self.auth.get_user_by_req(request) auth_user = requester.user if target_user != auth_user: await assert_user_is_admin(self.auth, auth_user) if not self.is_mine(target_user): raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only whois a local user") ret = await self.admin_handler.get_whois(target_user) return HTTPStatus.OK, ret
class AccountDataRestServlet(RestServlet): """Retrieve the given user's account data""" PATTERNS = admin_patterns("/users/(?P<user_id>[^/]*)/accountdata") def __init__(self, hs: "HomeServer"): self._auth = hs.get_auth() self._store = hs.get_datastore() self._is_mine_id = hs.is_mine_id async def on_GET(self, request: SynapseRequest, user_id: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self._auth, request) if not self._is_mine_id(user_id): raise SynapseError(HTTPStatus.BAD_REQUEST, "Can only look up local users") if not await self._store.get_user_by_id(user_id): raise NotFoundError("User not found") global_data, by_room_data = await self._store.get_account_data_for_user( user_id) return HTTPStatus.OK, { "account_data": { "global": global_data, "rooms": by_room_data, }, }
class UserMembershipRestServlet(RestServlet): """ Get room list of an user. """ PATTERNS = admin_patterns("/users/(?P<user_id>[^/]+)/joined_rooms$") def __init__(self, hs): self.is_mine = hs.is_mine self.auth = hs.get_auth() self.store = hs.get_datastore() async def on_GET(self, request, user_id): await assert_requester_is_admin(self.auth, request) if not self.is_mine(UserID.from_string(user_id)): raise SynapseError(400, "Can only lookup local users") user = await self.store.get_user_by_id(user_id) if user is None: raise NotFoundError("Unknown user") room_ids = await self.store.get_rooms_for_user(user_id) ret = {"joined_rooms": list(room_ids), "total": len(room_ids)} return 200, ret
class DeactivateAccountRestServlet(RestServlet): PATTERNS = admin_patterns("/deactivate/(?P<target_user_id>[^/]*)") def __init__(self, hs): self._deactivate_account_handler = hs.get_deactivate_account_handler() self.auth = hs.get_auth() async def on_POST(self, request, target_user_id): await assert_requester_is_admin(self.auth, request) body = parse_json_object_from_request(request, allow_empty_body=True) erase = body.get("erase", False) if not isinstance(erase, bool): raise SynapseError( HTTPStatus.BAD_REQUEST, "Param 'erase' must be a boolean, if given", Codes.BAD_JSON, ) UserID.from_string(target_user_id) result = await self._deactivate_account_handler.deactivate_account( target_user_id, erase ) if result: id_server_unbind_result = "success" else: id_server_unbind_result = "no-support" return 200, {"id_server_unbind_result": id_server_unbind_result}
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_datastores().main 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 DeleteMediaByID(RestServlet): """Delete local media by a given ID. Removes it from this server. """ PATTERNS = admin_patterns( "/media/(?P<server_name>[^/]+)/(?P<media_id>[^/]+)") def __init__(self, hs: "HomeServer"): self.store = hs.get_datastore() self.auth = hs.get_auth() self.server_name = hs.hostname self.media_repository = hs.get_media_repository() async def on_DELETE(self, request: Request, server_name: str, media_id: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) if self.server_name != server_name: raise SynapseError(400, "Can only delete local media") if await self.store.get_local_media(media_id) is None: raise NotFoundError("Unknown media") logging.info("Deleting local media by ID: %s", media_id) deleted_media, total = await self.media_repository.delete_local_media( media_id) return 200, {"deleted_media": deleted_media, "total": total}
class QuarantineMediaByID(RestServlet): """Quarantines local or remote media by a given ID so that no one can download it via this server. """ PATTERNS = admin_patterns( "/media/quarantine/(?P<server_name>[^/]+)/(?P<media_id>[^/]+)") def __init__(self, hs: "HomeServer"): self.store = hs.get_datastore() self.auth = hs.get_auth() async def on_POST(self, request: Request, server_name: str, media_id: str) -> Tuple[int, JsonDict]: requester = await self.auth.get_user_by_req(request) await assert_user_is_admin(self.auth, requester.user) logging.info("Quarantining local media by ID: %s/%s", server_name, media_id) # Quarantine this media id await self.store.quarantine_media_by_id(server_name, media_id, requester.user.to_string()) return 200, {}
class DevicesRestServlet(RestServlet): """ Retrieve the given user's devices """ PATTERNS = admin_patterns("/users/(?P<user_id>[^/]*)/devices$", "v2") def __init__(self, hs): """ Args: hs (synapse.server.HomeServer): server """ self.hs = hs self.auth = hs.get_auth() self.device_handler = hs.get_device_handler() self.store = hs.get_datastore() async def on_GET(self, request, user_id): await assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(user_id) if not self.hs.is_mine(target_user): 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 200, {"devices": devices, "total": len(devices)}
class DeviceRestServlet(RestServlet): """ Get, update or delete the given user's device """ PATTERNS = admin_patterns( "/users/(?P<user_id>[^/]*)/devices/(?P<device_id>[^/]*)$", "v2") def __init__(self, hs): super().__init__() self.hs = hs self.auth = hs.get_auth() self.device_handler = hs.get_device_handler() self.store = hs.get_datastore() async def on_GET(self, request, user_id, device_id): await assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(user_id) if not self.hs.is_mine(target_user): 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") device = await self.device_handler.get_device(target_user.to_string(), device_id) return 200, device async def on_DELETE(self, request, user_id, device_id): await assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(user_id) if not self.hs.is_mine(target_user): 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 200, {} async def on_PUT(self, request, user_id, device_id): await assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(user_id) if not self.hs.is_mine(target_user): 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") body = parse_json_object_from_request(request, allow_empty_body=True) await self.device_handler.update_device(target_user.to_string(), device_id, body) return 200, {}
class DeleteDevicesRestServlet(RestServlet): """ API for bulk deletion of devices. Accepts a JSON object with a devices key which lists the device_ids to delete. """ PATTERNS = admin_patterns("/users/(?P<user_id>[^/]*)/delete_devices$", "v2") def __init__(self, hs): self.hs = hs self.auth = hs.get_auth() self.device_handler = hs.get_device_handler() self.store = hs.get_datastore() async def on_POST(self, request, user_id): await assert_requester_is_admin(self.auth, request) target_user = UserID.from_string(user_id) if not self.hs.is_mine(target_user): 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") body = parse_json_object_from_request(request, allow_empty_body=False) assert_params_in_dict(body, ["devices"]) await self.device_handler.delete_devices(target_user.to_string(), body["devices"]) return 200, {}
class UserMediaRestServlet(RestServlet): """ Gets information about all uploaded local media for a specific `user_id`. Example: http://localhost:8008/_synapse/admin/v1/users/ @user:server/media Args: The parameters `from` and `limit` are required for pagination. By default, a `limit` of 100 is used. Returns: A list of media and an integer representing the total number of media that exist given for this user """ PATTERNS = admin_patterns("/users/(?P<user_id>[^/]+)/media$") def __init__(self, hs): self.is_mine = hs.is_mine self.auth = hs.get_auth() self.store = hs.get_datastore() async def on_GET(self, request: SynapseRequest, user_id: str) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self.auth, request) if not self.is_mine(UserID.from_string(user_id)): raise SynapseError(400, "Can only lookup local users") user = await self.store.get_user_by_id(user_id) if user is None: raise NotFoundError("Unknown user") start = parse_integer(request, "from", default=0) limit = parse_integer(request, "limit", default=100) if start < 0: raise SynapseError( 400, "Query parameter from must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) if limit < 0: raise SynapseError( 400, "Query parameter limit must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) media, total = await self.store.get_local_media_by_user_paginate( start, limit, user_id) ret = {"media": media, "total": total} if (start + limit) < total: ret["next_token"] = start + len(media) return 200, ret
class BackgroundUpdateStartJobRestServlet(RestServlet): """Allows to start specific background updates""" PATTERNS = admin_patterns("/background_updates/start_job$") def __init__(self, hs: "HomeServer"): self._auth = hs.get_auth() self._store = hs.get_datastores().main async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]: await assert_requester_is_admin(self._auth, request) body = parse_json_object_from_request(request) assert_params_in_dict(body, ["job_name"]) job_name = body["job_name"] if job_name == "populate_stats_process_rooms": jobs = [("populate_stats_process_rooms", "{}", "")] elif job_name == "regenerate_directory": jobs = [ ("populate_user_directory_createtables", "{}", ""), ( "populate_user_directory_process_rooms", "{}", "populate_user_directory_createtables", ), ( "populate_user_directory_process_users", "{}", "populate_user_directory_process_rooms", ), ( "populate_user_directory_cleanup", "{}", "populate_user_directory_process_users", ), ] else: raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid job_name") try: await self._store.db_pool.simple_insert_many( table="background_updates", keys=("update_name", "progress_json", "depends_on"), values=jobs, desc=f"admin_api_run_{job_name}", ) except self._store.db_pool.engine.module.IntegrityError: raise SynapseError( HTTPStatus.BAD_REQUEST, "Job %s is already in queue of background updates." % (job_name, ), ) self._store.db_pool.updates.start_doing_background_updates() return HTTPStatus.OK, {}
class UsersRestServletV2(RestServlet): PATTERNS = admin_patterns("/users$", "v2") """Get request to list all local users. This needs user to have administrator access in Synapse. GET /_synapse/admin/v2/users?from=0&limit=10&guests=false returns: 200 OK with list of users 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 `user_id` can be used to filter by user id. The parameter `name` can be used to filter by user id or display name. The parameter `guests` can be used to exclude guest users. The parameter `deactivated` can be used to include deactivated users. """ def __init__(self, hs: "HomeServer"): self.hs = hs self.store = hs.get_datastore() self.auth = hs.get_auth() self.admin_handler = hs.get_admin_handler() 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( 400, "Query parameter from must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) if limit < 0: raise SynapseError( 400, "Query parameter limit must be a string representing a positive integer.", errcode=Codes.INVALID_PARAM, ) user_id = parse_string(request, "user_id", default=None) name = parse_string(request, "name", default=None) guests = parse_boolean(request, "guests", default=True) deactivated = parse_boolean(request, "deactivated", default=False) users, total = await self.store.get_users_paginate( start, limit, user_id, name, guests, deactivated ) ret = {"users": users, "total": total} if (start + limit) < total: ret["next_token"] = str(start + len(users)) return 200, ret