Example #1
0
def register_txn_path(servlet, regex_string, http_server, with_get=False):
    """Registers a transaction-based path.

    This registers two paths:
        PUT regex_string/$txnid
        POST regex_string

    Args:
        regex_string (str): The regex string to register. Must NOT have a
        trailing $ as this string will be appended to.
        http_server : The http_server to register paths with.
        with_get: True to also register respective GET paths for the PUTs.
    """
    http_server.register_paths(
        "POST",
        client_patterns(regex_string + "$", v1=True),
        servlet.on_POST,
        servlet.__class__.__name__,
    )
    http_server.register_paths(
        "PUT",
        client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
        servlet.on_PUT,
        servlet.__class__.__name__,
    )
    if with_get:
        http_server.register_paths(
            "GET",
            client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
            servlet.on_GET,
            servlet.__class__.__name__,
        )
Example #2
0
    def register(self, http_server: HttpServer) -> None:
        # /room/$roomid/state/$eventtype
        no_state_key = "/rooms/(?P<room_id>[^/]*)/state/(?P<event_type>[^/]*)$"

        # /room/$roomid/state/$eventtype/$statekey
        state_key = ("/rooms/(?P<room_id>[^/]*)/state/"
                     "(?P<event_type>[^/]*)/(?P<state_key>[^/]*)$")

        http_server.register_paths(
            "GET",
            client_patterns(state_key, v1=True),
            self.on_GET,
            self.__class__.__name__,
        )
        http_server.register_paths(
            "PUT",
            client_patterns(state_key, v1=True),
            self.on_PUT,
            self.__class__.__name__,
        )
        http_server.register_paths(
            "GET",
            client_patterns(no_state_key, v1=True),
            self.on_GET_no_state_key,
            self.__class__.__name__,
        )
        http_server.register_paths(
            "PUT",
            client_patterns(no_state_key, v1=True),
            self.on_PUT_no_state_key,
            self.__class__.__name__,
        )
Example #3
0
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
Example #4
0
class ClientDirectoryListServer(RestServlet):
    PATTERNS = client_patterns("/directory/list/room/(?P<room_id>[^/]*)$",
                               v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.store = hs.get_datastores().main
        self.directory_handler = hs.get_directory_handler()
        self.auth = hs.get_auth()

    async def on_GET(self, request: Request,
                     room_id: str) -> Tuple[int, JsonDict]:
        room = await self.store.get_room(room_id)
        if room is None:
            raise NotFoundError("Unknown room")

        return 200, {
            "visibility": "public" if room["is_public"] else "private"
        }

    async def on_PUT(self, request: SynapseRequest,
                     room_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)

        content = parse_json_object_from_request(request)
        visibility = content.get("visibility", "public")

        await self.directory_handler.edit_published_room_list(
            requester, room_id, visibility)

        return 200, {}
Example #5
0
class RoomEventServlet(RestServlet):
    PATTERNS = client_patterns(
        "/rooms/(?P<room_id>[^/]*)/event/(?P<event_id>[^/]*)$", v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.clock = hs.get_clock()
        self.event_handler = hs.get_event_handler()
        self._event_serializer = hs.get_event_client_serializer()
        self.auth = hs.get_auth()

    async def on_GET(self, request: SynapseRequest, room_id: str,
                     event_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        try:
            event = await self.event_handler.get_event(requester.user, room_id,
                                                       event_id)
        except AuthError:
            # This endpoint is supposed to return a 404 when the requester does
            # not have permission to access the event
            # https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-r0-rooms-roomid-event-eventid
            raise SynapseError(404,
                               "Event not found.",
                               errcode=Codes.NOT_FOUND)

        time_now = self.clock.time_msec()
        if event:
            event_dict = await self._event_serializer.serialize_event(
                event, time_now)
            return 200, event_dict

        raise SynapseError(404, "Event not found.", errcode=Codes.NOT_FOUND)
Example #6
0
class RefreshTokenServlet(RestServlet):
    PATTERNS = client_patterns("/org.matrix.msc2918.refresh_token/refresh$",
                               releases=(),
                               unstable=True)

    def __init__(self, hs: "HomeServer"):
        self._auth_handler = hs.get_auth_handler()
        self._clock = hs.get_clock()
        self.access_token_lifetime = hs.config.registration.access_token_lifetime

    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        refresh_submission = parse_json_object_from_request(request)

        assert_params_in_dict(refresh_submission, ["refresh_token"])
        token = refresh_submission["refresh_token"]
        if not isinstance(token, str):
            raise SynapseError(400, "Invalid param: refresh_token",
                               Codes.INVALID_PARAM)

        valid_until_ms = self._clock.time_msec() + self.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,
            },
        )
Example #7
0
class ProfileRestServlet(RestServlet):
    PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)", v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.profile_handler = hs.get_profile_handler()
        self.auth = hs.get_auth()

    async def on_GET(
        self, request: SynapseRequest, user_id: str
    ) -> Tuple[int, JsonDict]:
        requester_user = None

        if self.hs.config.server.require_auth_for_profile_requests:
            requester = await self.auth.get_user_by_req(request)
            requester_user = requester.user

        user = UserID.from_string(user_id)

        await self.profile_handler.check_profile_query_allowed(user, requester_user)

        displayname = await self.profile_handler.get_displayname(user)
        avatar_url = await self.profile_handler.get_avatar_url(user)

        ret = {}
        if displayname is not None:
            ret["displayname"] = displayname
        if avatar_url is not None:
            ret["avatar_url"] = avatar_url

        return 200, ret
Example #8
0
class ClientAppserviceDirectoryListServer(RestServlet):
    PATTERNS = client_patterns(
        "/directory/list/appservice/(?P<network_id>[^/]*)/(?P<room_id>[^/]*)$",
        v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.store = hs.get_datastores().main
        self.directory_handler = hs.get_directory_handler()
        self.auth = hs.get_auth()

    async def on_PUT(self, request: SynapseRequest, network_id: str,
                     room_id: str) -> Tuple[int, JsonDict]:
        content = parse_json_object_from_request(request)
        visibility = content.get("visibility", "public")
        return await self._edit(request, network_id, room_id, visibility)

    async def on_DELETE(self, request: SynapseRequest, network_id: str,
                        room_id: str) -> Tuple[int, JsonDict]:
        return await self._edit(request, network_id, room_id, "private")

    async def _edit(self, request: SynapseRequest, network_id: str,
                    room_id: str, visibility: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)
        if not requester.app_service:
            raise AuthError(
                403,
                "Only appservices can edit the appservice published room list")

        await self.directory_handler.edit_published_appservice_room_list(
            requester.app_service.id, network_id, room_id, visibility)

        return 200, {}
Example #9
0
def register_txn_path(
    servlet: RestServlet,
    regex_string: str,
    http_server: HttpServer,
    with_get: bool = False,
) -> None:
    """Registers a transaction-based path.

    This registers two paths:
        PUT regex_string/$txnid
        POST regex_string

    Args:
        regex_string: The regex string to register. Must NOT have a
            trailing $ as this string will be appended to.
        http_server: The http_server to register paths with.
        with_get: True to also register respective GET paths for the PUTs.
    """
    on_POST = getattr(servlet, "on_POST", None)
    on_PUT = getattr(servlet, "on_PUT", None)
    if on_POST is None or on_PUT is None:
        raise RuntimeError(
            "on_POST and on_PUT must exist when using register_txn_path")
    http_server.register_paths(
        "POST",
        client_patterns(regex_string + "$", v1=True),
        on_POST,
        servlet.__class__.__name__,
    )
    http_server.register_paths(
        "PUT",
        client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
        on_PUT,
        servlet.__class__.__name__,
    )
    on_GET = getattr(servlet, "on_GET", None)
    if with_get:
        if on_GET is None:
            raise RuntimeError(
                "register_txn_path called with with_get = True, but no on_GET method exists"
            )
        http_server.register_paths(
            "GET",
            client_patterns(regex_string + "/(?P<txn_id>[^/]*)$", v1=True),
            on_GET,
            servlet.__class__.__name__,
        )
Example #10
0
class RelationPaginationServlet(RestServlet):
    """API to paginate relations on an event by topological ordering, optionally
    filtered by relation type and event type.
    """

    PATTERNS = client_patterns(
        "/rooms/(?P<room_id>[^/]*)/relations/(?P<parent_id>[^/]*)"
        "(/(?P<relation_type>[^/]*)(/(?P<event_type>[^/]*))?)?$",
        releases=("v1", ),
    )

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.auth = hs.get_auth()
        self.store = hs.get_datastores().main
        self._relations_handler = hs.get_relations_handler()

    async def on_GET(
        self,
        request: SynapseRequest,
        room_id: str,
        parent_id: str,
        relation_type: Optional[str] = None,
        event_type: Optional[str] = None,
    ) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request, allow_guest=True)

        limit = parse_integer(request, "limit", default=5)
        direction = parse_string(request,
                                 "org.matrix.msc3715.dir",
                                 default="b",
                                 allowed_values=["f", "b"])
        from_token_str = parse_string(request, "from")
        to_token_str = parse_string(request, "to")

        # Return the relations
        from_token = None
        if from_token_str:
            from_token = await StreamToken.from_string(self.store,
                                                       from_token_str)
        to_token = None
        if to_token_str:
            to_token = await StreamToken.from_string(self.store, to_token_str)

        result = await self._relations_handler.get_relations(
            requester=requester,
            event_id=parent_id,
            room_id=room_id,
            relation_type=relation_type,
            event_type=event_type,
            limit=limit,
            direction=direction,
            from_token=from_token,
            to_token=to_token,
        )

        return 200, result
Example #11
0
class DeviceRestServlet(RestServlet):
    PATTERNS = client_patterns("/devices/(?P<device_id>[^/]*)$")

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.auth = hs.get_auth()
        self.device_handler = hs.get_device_handler()
        self.auth_handler = hs.get_auth_handler()

    async def on_GET(self, request: SynapseRequest,
                     device_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        device = await self.device_handler.get_device(
            requester.user.to_string(), device_id)
        if device is None:
            raise NotFoundError("No device found")
        return 200, device

    @interactive_auth_handler
    async def on_DELETE(self, request: SynapseRequest,
                        device_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)

        try:
            body = parse_json_object_from_request(request)

        except errors.SynapseError as e:
            if e.errcode == errors.Codes.NOT_JSON:
                # deal with older clients which didn't pass a JSON dict
                # the same as those that pass an empty dict
                body = {}
            else:
                raise

        await self.auth_handler.validate_user_via_ui_auth(
            requester,
            request,
            body,
            "remove a device from your account",
            # Users might call this multiple times in a row while cleaning up
            # devices, allow a single UI auth session to be re-used.
            can_skip_ui_auth=True,
        )

        await self.device_handler.delete_device(requester.user.to_string(),
                                                device_id)
        return 200, {}

    async def on_PUT(self, request: SynapseRequest,
                     device_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request, allow_guest=True)

        body = parse_json_object_from_request(request)
        await self.device_handler.update_device(requester.user.to_string(),
                                                device_id, body)
        return 200, {}
Example #12
0
class ProfileDisplaynameRestServlet(RestServlet):
    PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/displayname",
                               v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.profile_handler = hs.get_profile_handler()
        self.auth = hs.get_auth()

    async def on_GET(self, request: SynapseRequest,
                     user_id: str) -> Tuple[int, JsonDict]:
        requester_user = None

        if self.hs.config.server.require_auth_for_profile_requests:
            requester = await self.auth.get_user_by_req(request)
            requester_user = requester.user

        if not UserID.is_valid(user_id):
            raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid user id",
                               Codes.INVALID_PARAM)

        user = UserID.from_string(user_id)
        await self.profile_handler.check_profile_query_allowed(
            user, requester_user)

        displayname = await self.profile_handler.get_displayname(user)

        ret = {}
        if displayname is not None:
            ret["displayname"] = displayname

        return 200, ret

    async def on_PUT(self, request: SynapseRequest,
                     user_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        user = UserID.from_string(user_id)
        is_admin = await self.auth.is_server_admin(requester.user)

        content = parse_json_object_from_request(request)

        try:
            new_name = content["displayname"]
        except Exception:
            raise SynapseError(
                code=400,
                msg="Unable to parse name",
                errcode=Codes.BAD_JSON,
            )

        await self.profile_handler.set_displayname(user, requester, new_name,
                                                   is_admin)

        return 200, {}
Example #13
0
class ClaimDehydratedDeviceServlet(RestServlet):
    """Claim a dehydrated device.

    POST /org.matrix.msc2697.v2/dehydrated_device/claim
    Content-Type: application/json

    {
      "device_id": "dehydrated_device_id"
    }

    HTTP/1.1 200 OK
    Content-Type: application/json

    {
      "success": true,
    }

    """

    PATTERNS = client_patterns(
        "/org.matrix.msc2697.v2/dehydrated_device/claim", releases=()
    )

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.auth = hs.get_auth()
        self.device_handler = hs.get_device_handler()

    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)

        submission = parse_json_object_from_request(request)

        if "device_id" not in submission:
            raise errors.SynapseError(
                400,
                "device_id missing",
                errcode=errors.Codes.MISSING_PARAM,
            )
        elif not isinstance(submission["device_id"], str):
            raise errors.SynapseError(
                400,
                "device_id must be a string",
                errcode=errors.Codes.INVALID_PARAM,
            )

        result = await self.device_handler.rehydrate_device(
            requester.user.to_string(),
            self.auth.get_access_token_from_request(request),
            submission["device_id"],
        )

        return 200, result
Example #14
0
class SsoRedirectServlet(RestServlet):
    PATTERNS = list(client_patterns("/login/(cas|sso)/redirect$", v1=True)) + [
        re.compile("^" + CLIENT_API_PREFIX +
                   "/r0/login/sso/redirect/(?P<idp_id>[A-Za-z0-9_.~-]+)$")
    ]

    def __init__(self, hs: "HomeServer"):
        # make sure that the relevant handlers are instantiated, so that they
        # register themselves with the main SSOHandler.
        _load_sso_handlers(hs)
        self._sso_handler = hs.get_sso_handler()
        self._public_baseurl = hs.config.server.public_baseurl

    async def on_GET(self,
                     request: SynapseRequest,
                     idp_id: Optional[str] = None) -> None:
        if not self._public_baseurl:
            raise SynapseError(400, "SSO requires a valid public_baseurl")

        # if this isn't the expected hostname, redirect to the right one, so that we
        # get our cookies back.
        requested_uri = get_request_uri(request)
        baseurl_bytes = self._public_baseurl.encode("utf-8")
        if not requested_uri.startswith(baseurl_bytes):
            # swap out the incorrect base URL for the right one.
            #
            # The idea here is to redirect from
            #    https://foo.bar/whatever/_matrix/...
            # to
            #    https://public.baseurl/_matrix/...
            #
            i = requested_uri.index(b"/_matrix")
            new_uri = baseurl_bytes[:-1] + requested_uri[i:]
            logger.info(
                "Requested URI %s is not canonical: redirecting to %s",
                requested_uri.decode("utf-8", errors="replace"),
                new_uri.decode("utf-8", errors="replace"),
            )
            request.redirect(new_uri)
            finish_request(request)
            return

        args: Dict[bytes, List[bytes]] = request.args  # type: ignore
        client_redirect_url = parse_bytes_from_args(args,
                                                    "redirectUrl",
                                                    required=True)
        sso_url = await self._sso_handler.handle_redirect_request(
            request,
            client_redirect_url,
            idp_id,
        )
        logger.info("Redirecting to %s", sso_url)
        request.redirect(sso_url)
        finish_request(request)
Example #15
0
class ProfileAvatarURLRestServlet(RestServlet):
    PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/avatar_url", v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.profile_handler = hs.get_profile_handler()
        self.auth = hs.get_auth()

    async def on_GET(
        self, request: SynapseRequest, user_id: str
    ) -> Tuple[int, JsonDict]:
        requester_user = None

        if self.hs.config.server.require_auth_for_profile_requests:
            requester = await self.auth.get_user_by_req(request)
            requester_user = requester.user

        user = UserID.from_string(user_id)

        await self.profile_handler.check_profile_query_allowed(user, requester_user)

        avatar_url = await self.profile_handler.get_avatar_url(user)

        ret = {}
        if avatar_url is not None:
            ret["avatar_url"] = avatar_url

        return 200, ret

    async def on_PUT(
        self, request: SynapseRequest, user_id: str
    ) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)
        user = UserID.from_string(user_id)
        is_admin = await self.auth.is_server_admin(requester.user)

        content = parse_json_object_from_request(request)
        try:
            new_avatar_url = content["avatar_url"]
        except KeyError:
            raise SynapseError(
                400, "Missing key 'avatar_url'", errcode=Codes.MISSING_PARAM
            )

        await self.profile_handler.set_avatar_url(
            user, requester, new_avatar_url, is_admin
        )

        return 200, {}

    def on_OPTIONS(self, request: SynapseRequest, user_id: str) -> Tuple[int, JsonDict]:
        return 200, {}
Example #16
0
class RoomTypingRestServlet(RestServlet):
    PATTERNS = client_patterns(
        "/rooms/(?P<room_id>[^/]*)/typing/(?P<user_id>[^/]*)$", v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.presence_handler = hs.get_presence_handler()
        self.auth = hs.get_auth()

        # If we're not on the typing writer instance we should scream if we get
        # requests.
        self._is_typing_writer = (hs.get_instance_name()
                                  in hs.config.worker.writers.typing)

    async def on_PUT(self, request: SynapseRequest, room_id: str,
                     user_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)

        if not self._is_typing_writer:
            raise Exception(
                "Got /typing request on instance that is not typing writer")

        room_id = urlparse.unquote(room_id)
        target_user = UserID.from_string(urlparse.unquote(user_id))

        content = parse_json_object_from_request(request)

        await self.presence_handler.bump_presence_active_time(requester.user)

        # Limit timeout to stop people from setting silly typing timeouts.
        timeout = min(content.get("timeout", 30000), 120000)

        # Defer getting the typing handler since it will raise on workers.
        typing_handler = self.hs.get_typing_writer_handler()

        try:
            if content["typing"]:
                await typing_handler.started_typing(
                    target_user=target_user,
                    requester=requester,
                    room_id=room_id,
                    timeout=timeout,
                )
            else:
                await typing_handler.stopped_typing(target_user=target_user,
                                                    requester=requester,
                                                    room_id=room_id)
        except ShadowBanError:
            # Pretend this worked without error.
            pass

        return 200, {}
Example #17
0
class JoinedRoomsRestServlet(RestServlet):
    PATTERNS = client_patterns("/joined_rooms$", v1=True)

    def __init__(self, hs):
        super().__init__()
        self.store = hs.get_datastore()
        self.auth = hs.get_auth()

    async def on_GET(self, request):
        requester = await self.auth.get_user_by_req(request, allow_guest=True)

        room_ids = await self.store.get_rooms_for_user(requester.user.to_string())
        return 200, {"joined_rooms": list(room_ids)}
Example #18
0
class RoomEventContextServlet(RestServlet):
    PATTERNS = client_patterns(
        "/rooms/(?P<room_id>[^/]*)/context/(?P<event_id>[^/]*)$", v1=True)

    def __init__(self, hs: "HomeServer"):
        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: SynapseRequest, room_id: str,
                     event_id: str) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request, allow_guest=True)

        limit = parse_integer(request, "limit", default=10)

        # picking the API shape for symmetry with /messages
        filter_str = parse_string(request, "filter", encoding="utf-8")
        if filter_str:
            filter_json = urlparse.unquote(filter_str)
            event_filter: Optional[Filter] = Filter(
                json_decoder.decode(filter_json))
        else:
            event_filter = None

        results = await self.room_context_handler.get_event_context(
            requester, room_id, event_id, limit, event_filter)

        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,
            # No need to bundle aggregations for state events
            bundle_aggregations=False,
        )

        return 200, results
Example #19
0
class RoomMemberListRestServlet(RestServlet):
    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/members$", v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.message_handler = hs.get_message_handler()
        self.auth = hs.get_auth()
        self.store = hs.get_datastores().main

    @cancellable
    async def on_GET(self, request: SynapseRequest,
                     room_id: str) -> Tuple[int, JsonDict]:
        # TODO support Pagination stream API (limit/tokens)
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        handler = self.message_handler

        # request the state as of a given event, as identified by a stream token,
        # for consistency with /messages etc.
        # useful for getting the membership in retrospect as of a given /sync
        # response.
        at_token_string = parse_string(request, "at")
        if at_token_string is None:
            at_token = None
        else:
            at_token = await StreamToken.from_string(self.store,
                                                     at_token_string)

        # let you filter down on particular memberships.
        # XXX: this may not be the best shape for this API - we could pass in a filter
        # instead, except filters aren't currently aware of memberships.
        # See https://github.com/matrix-org/matrix-doc/issues/1337 for more details.
        membership = parse_string(request, "membership")
        not_membership = parse_string(request, "not_membership")

        events = await handler.get_state_events(
            room_id=room_id,
            user_id=requester.user.to_string(),
            at_token=at_token,
            state_filter=StateFilter.from_types([(EventTypes.Member, None)]),
        )

        chunk = []

        for event in events:
            if (membership and event["content"].get("membership") != membership
                ) or (not_membership and event["content"].get("membership")
                      == not_membership):
                continue
            chunk.append(event)

        return 200, {"chunk": chunk}
Example #20
0
class DevicesRestServlet(RestServlet):
    PATTERNS = client_patterns("/devices$")

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.auth = hs.get_auth()
        self.device_handler = hs.get_device_handler()

    async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        devices = await self.device_handler.get_devices_by_user(
            requester.user.to_string())
        return 200, {"devices": devices}
Example #21
0
 def register(self, http_server: HttpServer) -> None:
     super().register(http_server)
     if self._msc2858_enabled:
         # expose additional endpoint for MSC2858 support: backwards-compat support
         # for clients which don't yet support the stable endpoints.
         http_server.register_paths(
             "GET",
             client_patterns(
                 "/org.matrix.msc2858/login/sso/redirect/(?P<idp_id>[A-Za-z0-9_.~-]+)$",
                 releases=(),
                 unstable=True,
             ),
             self.on_GET,
             self.__class__.__name__,
         )
Example #22
0
class LegacyPushersRemoveRestServlet(UnsubscribeResource, RestServlet):
    """
    A servlet to handle legacy "email unsubscribe" links, forwarding requests to the ``UnsubscribeResource``

    This should be kept for some time, so unsubscribe links in past emails stay valid.
    """

    PATTERNS = client_patterns("/pushers/remove$",
                               releases=[],
                               v1=False,
                               unstable=True)

    async def on_GET(self, request: SynapseRequest) -> None:
        # Forward the request to the UnsubscribeResource
        await self._async_render(request)
Example #23
0
class RoomInitialSyncRestServlet(RestServlet):
    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/initialSync$", v1=True)

    def __init__(self, hs):
        super().__init__()
        self.initial_sync_handler = hs.get_initial_sync_handler()
        self.auth = hs.get_auth()
        self.store = hs.get_datastore()

    async def on_GET(self, request, room_id):
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        pagination_config = await PaginationConfig.from_request(self.store, request)
        content = await self.initial_sync_handler.room_initial_sync(
            room_id=room_id, requester=requester, pagin_config=pagination_config
        )
        return 200, content
Example #24
0
class JoinedRoomMemberListRestServlet(RestServlet):
    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/joined_members$", v1=True)

    def __init__(self, hs):
        super().__init__()
        self.message_handler = hs.get_message_handler()
        self.auth = hs.get_auth()

    async def on_GET(self, request, room_id):
        requester = await self.auth.get_user_by_req(request)

        users_with_profile = await self.message_handler.get_joined_members(
            requester, room_id
        )

        return 200, {"joined": users_with_profile}
Example #25
0
class PushersRestServlet(RestServlet):
    PATTERNS = client_patterns("/pushers$", v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.auth = hs.get_auth()

    async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)
        user = requester.user

        pushers = await self.hs.get_datastore().get_pushers_by_user_id(user.to_string())

        filtered_pushers = [p.as_dict() for p in pushers]

        return 200, {"pushers": filtered_pushers}
Example #26
0
class DeleteDevicesRestServlet(RestServlet):
    """
    API for bulk deletion of devices. Accepts a JSON object with a devices
    key which lists the device_ids to delete. Requires user interactive auth.
    """

    PATTERNS = client_patterns("/delete_devices")

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.auth = hs.get_auth()
        self.device_handler = hs.get_device_handler()
        self.auth_handler = hs.get_auth_handler()

    @interactive_auth_handler
    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(request)

        try:
            body = parse_json_object_from_request(request)
        except errors.SynapseError as e:
            if e.errcode == errors.Codes.NOT_JSON:
                # DELETE
                # deal with older clients which didn't pass a JSON dict
                # the same as those that pass an empty dict
                body = {}
            else:
                raise e

        assert_params_in_dict(body, ["devices"])

        await self.auth_handler.validate_user_via_ui_auth(
            requester,
            request,
            body,
            "remove device(s) from your account",
            # Users might call this multiple times in a row while cleaning up
            # devices, allow a single UI auth session to be re-used.
            can_skip_ui_auth=True,
        )

        await self.device_handler.delete_devices(
            requester.user.to_string(), body["devices"]
        )
        return 200, {}
Example #27
0
class CancellableRestServlet(RestServlet):
    """A `RestServlet` with a mix of cancellable and uncancellable handlers."""

    PATTERNS = client_patterns("/sleep$")

    def __init__(self, hs: HomeServer):
        super().__init__()
        self.clock = hs.get_clock()

    @cancellable
    async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        await self.clock.sleep(1.0)
        return HTTPStatus.OK, {"result": True}

    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        await self.clock.sleep(1.0)
        return HTTPStatus.OK, {"result": True}
Example #28
0
class VoipRestServlet(RestServlet):
    PATTERNS = client_patterns("/voip/turnServer$", v1=True)

    def __init__(self, hs: "HomeServer"):
        super().__init__()
        self.hs = hs
        self.auth = hs.get_auth()

    async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        requester = await self.auth.get_user_by_req(
            request, self.hs.config.turn_allow_guests)

        turnUris = self.hs.config.turn_uris
        turnSecret = self.hs.config.turn_shared_secret
        turnUsername = self.hs.config.turn_username
        turnPassword = self.hs.config.turn_password
        userLifetime = self.hs.config.turn_user_lifetime

        if turnUris and turnSecret and userLifetime:
            expiry = (self.hs.get_clock().time_msec() + userLifetime) / 1000
            username = "******" % (expiry, requester.user.to_string())

            mac = hmac.new(turnSecret.encode(),
                           msg=username.encode(),
                           digestmod=hashlib.sha1)
            # We need to use standard padded base64 encoding here
            # encode_base64 because we need to add the standard padding to get the
            # same result as the TURN server.
            password = base64.b64encode(mac.digest()).decode("ascii")

        elif turnUris and turnUsername and turnPassword and userLifetime:
            username = turnUsername
            password = turnPassword

        else:
            return 200, {}

        return (
            200,
            {
                "username": username,
                "password": password,
                "ttl": userLifetime / 1000,
                "uris": turnUris,
            },
        )
Example #29
0
class SearchRestServlet(RestServlet):
    PATTERNS = client_patterns("/search$", v1=True)

    def __init__(self, hs):
        super().__init__()
        self.search_handler = hs.get_search_handler()
        self.auth = hs.get_auth()

    async def on_POST(self, request):
        requester = await self.auth.get_user_by_req(request)

        content = parse_json_object_from_request(request)

        batch = parse_string(request, "next_batch")
        results = await self.search_handler.search(requester.user, content, batch)

        return 200, results
Example #30
0
class RoomStateRestServlet(RestServlet):
    PATTERNS = client_patterns("/rooms/(?P<room_id>[^/]*)/state$", v1=True)

    def __init__(self, hs):
        super().__init__()
        self.message_handler = hs.get_message_handler()
        self.auth = hs.get_auth()

    async def on_GET(self, request, room_id):
        requester = await self.auth.get_user_by_req(request, allow_guest=True)
        # Get all the current state for this room
        events = await self.message_handler.get_state_events(
            room_id=room_id,
            user_id=requester.user.to_string(),
            is_guest=requester.is_guest,
        )
        return 200, events