Beispiel #1
0
    async def try_unbind_threepid_with_id_server(self, mxid: str,
                                                 threepid: dict,
                                                 id_server: str) -> bool:
        """Removes a binding from an identity server

        Args:
            mxid: Matrix user ID of binding to be removed
            threepid: Dict with medium & address of binding to be removed
            id_server: Identity server to unbind from

        Raises:
            SynapseError: On any of the following conditions
                - the supplied id_server is not a valid identity server name
                - we failed to contact the supplied identity server

        Returns:
            True on success, otherwise False if the identity
            server doesn't support unbinding
        """

        if not valid_id_server_location(id_server):
            raise SynapseError(
                400,
                "id_server must be a valid hostname with optional port and path components",
            )

        url = "https://%s/_matrix/identity/api/v1/3pid/unbind" % (id_server, )
        url_bytes = b"/_matrix/identity/api/v1/3pid/unbind"

        content = {
            "mxid": mxid,
            "threepid": {
                "medium": threepid["medium"],
                "address": threepid["address"]
            },
        }

        # we abuse the federation http client to sign the request, but we have to send it
        # using the normal http client since we don't want the SRV lookup and want normal
        # 'browser-like' HTTPS.
        auth_headers = self.federation_http_client.build_auth_headers(
            destination=None,
            method=b"POST",
            url_bytes=url_bytes,
            content=content,
            destination_is=id_server.encode("ascii"),
        )
        headers = {b"Authorization": auth_headers}

        try:
            # Use the blacklisting http client as this call is only to identity servers
            # provided by a client
            await self.blacklisting_http_client.post_json_get_json(
                url, content, headers)
            changed = True
        except HttpResponseException as e:
            changed = False
            if e.code in (400, 404, 501):
                # The remote server probably doesn't support unbinding (yet)
                logger.warning("Received %d response while unbinding threepid",
                               e.code)
            else:
                logger.error(
                    "Failed to unbind threepid on identity server: %s", e)
                raise SynapseError(500, "Failed to contact identity server")
        except RequestTimedOutError:
            raise SynapseError(500, "Timed out contacting identity server")

        await self.store.remove_user_bound_threepid(
            user_id=mxid,
            medium=threepid["medium"],
            address=threepid["address"],
            id_server=id_server,
        )

        return changed
Beispiel #2
0
    async def bind_threepid(
        self,
        client_secret: str,
        sid: str,
        mxid: str,
        id_server: str,
        id_access_token: Optional[str] = None,
        use_v2: bool = True,
    ) -> JsonDict:
        """Bind a 3PID to an identity server

        Args:
            client_secret: A unique secret provided by the client
            sid: The ID of the validation session
            mxid: The MXID to bind the 3PID to
            id_server: The domain of the identity server to query
            id_access_token: The access token to authenticate to the identity
                server with, if necessary. Required if use_v2 is true
            use_v2: Whether to use v2 Identity Service API endpoints. Defaults to True

        Raises:
            SynapseError: On any of the following conditions
                - the supplied id_server is not a valid identity server name
                - we failed to contact the supplied identity server

        Returns:
            The response from the identity server
        """
        logger.debug("Proxying threepid bind request for %s to %s", mxid,
                     id_server)

        # If an id_access_token is not supplied, force usage of v1
        if id_access_token is None:
            use_v2 = False

        if not valid_id_server_location(id_server):
            raise SynapseError(
                400,
                "id_server must be a valid hostname with optional port and path components",
            )

        # Decide which API endpoint URLs to use
        headers = {}
        bind_data = {"sid": sid, "client_secret": client_secret, "mxid": mxid}
        if use_v2:
            bind_url = "https://%s/_matrix/identity/v2/3pid/bind" % (
                id_server, )
            headers["Authorization"] = create_id_access_token_header(
                id_access_token)  # type: ignore
        else:
            bind_url = "https://%s/_matrix/identity/api/v1/3pid/bind" % (
                id_server, )

        try:
            # Use the blacklisting http client as this call is only to identity servers
            # provided by a client
            data = await self.blacklisting_http_client.post_json_get_json(
                bind_url, bind_data, headers=headers)

            # Remember where we bound the threepid
            await self.store.add_user_bound_threepid(
                user_id=mxid,
                medium=data["medium"],
                address=data["address"],
                id_server=id_server,
            )

            return data
        except HttpResponseException as e:
            if e.code != 404 or not use_v2:
                logger.error("3PID bind failed with Matrix error: %r", e)
                raise e.to_synapse_error()
        except RequestTimedOutError:
            raise SynapseError(500, "Timed out contacting identity server")
        except CodeMessageException as e:
            data = json_decoder.decode(e.msg)  # XXX WAT?
            return data

        logger.info("Got 404 when POSTing JSON %s, falling back to v1 URL",
                    bind_url)
        res = await self.bind_threepid(client_secret,
                                       sid,
                                       mxid,
                                       id_server,
                                       id_access_token,
                                       use_v2=False)
        return res