Esempio n. 1
0
    def _notify(self, assoc, attempt):
        mxid = assoc["mxid"]
        domain = mxid.split(":")[-1]
        server = yield self._pickServer(domain)

        post_url = "https://%s/_matrix/federation/v1/3pid/onbind" % (
            server,
        )

        logger.info("Making bind callback to: %s", post_url)

        # Make a POST to the chosen Synapse server
        http_client = FederationHttpClient(self.sydent)
        try:
            response = yield http_client.post_json_get_nothing(post_url, assoc, {})
        except Exception as e:
            self._notifyErrback(assoc, attempt, e)
            return

        # If the request failed, try again with exponential backoff
        if response.code != 200:
            self._notifyErrback(
                assoc, attempt, "Non-OK error code received (%d)" % response.code
            )
        else:
            logger.info("Successfully notified on bind for %s" % (mxid,))
Esempio n. 2
0
    def _getKeysForServer(self, server_name):
        """Get the signing key data from a homeserver.

        :param server_name: The name of the server to request the keys from.
        :type server_name: unicode

        :return: The verification keys returned by the server.
        :rtype: twisted.internet.defer.Deferred[dict[unicode, dict[unicode, unicode]]]
        """

        if server_name in self.cache:
            cached = self.cache[server_name]
            now = int(time.time() * 1000)
            if cached['valid_until_ts'] > now:
                defer.returnValue(self.cache[server_name]['verify_keys'])

        client = FederationHttpClient(self.sydent)
        result = yield client.get_json("matrix://%s/_matrix/key/v2/server/" %
                                       server_name)
        if 'verify_keys' not in result:
            raise SignatureVerifyException("No key found in response")

        if 'valid_until_ts' in result:
            # Don't cache anything without a valid_until_ts or we wouldn't
            # know when to expire it.
            logger.info("Got keys for %s: caching until %s", server_name,
                        result['valid_until_ts'])
            self.cache[server_name] = result

        defer.returnValue(result['verify_keys'])
Esempio n. 3
0
    def _notify(self, assoc, attempt):
        """
        Sends data about a new association (and, if necessary, the associated invites)
        to the associated MXID's homeserver.

        :param assoc: The association to send down to the homeserver.
        :type assoc: dict[str, any]
        :param attempt: The number of previous attempts to send this association.
        :type attempt: int
        """
        mxid = assoc["mxid"]
        mxid_parts = mxid.split(":", 1)
        if len(mxid_parts) != 2:
            logger.error(
                "Can't notify on bind for unparseable mxid %s. Not retrying.",
                assoc["mxid"],
            )
            return

        post_url = "matrix://%s/_matrix/federation/v1/3pid/onbind" % (
            mxid_parts[1], )

        logger.info("Making bind callback to: %s", post_url)

        # Make a POST to the chosen Synapse server
        http_client = FederationHttpClient(self.sydent)
        try:
            response = yield http_client.post_json_get_nothing(
                post_url, assoc, {})
        except Exception as e:
            self._notifyErrback(assoc, attempt, e)
            return

        # If the request failed, try again with exponential backoff
        if response.code != 200:
            self._notifyErrback(
                assoc, attempt,
                "Non-OK error code received (%d)" % response.code)
        else:
            logger.info("Successfully notified on bind for %s" % (mxid, ))

            # Skip the deletion step if instructed so by the config.
            if not self.sydent.delete_tokens_on_bind:
                return

            # Only remove sent tokens when they've been successfully sent.
            try:
                joinTokenStore = JoinTokenStore(self.sydent)
                joinTokenStore.deleteTokens(assoc["medium"], assoc["address"])
                logger.info(
                    "Successfully deleted invite for %s from the store",
                    assoc["address"],
                )
            except Exception as e:
                logger.exception(
                    "Couldn't remove invite for %s from the store",
                    assoc["address"],
                )
Esempio n. 4
0
    def _notify(self, assoc, attempt):
        mxid = assoc["mxid"]
        mxid_parts = mxid.split(":", 1)
        if len(mxid_parts) != 2:
            logger.error(
                "Can't notify on bind for unparseable mxid %s. Not retrying.",
                assoc["mxid"],
            )
            return

        post_url = "matrix://%s/_matrix/federation/v1/3pid/onbind" % (
            mxid_parts[1], )

        logger.info("Making bind callback to: %s", post_url)

        # Make a POST to the chosen Synapse server
        http_client = FederationHttpClient(self.sydent)
        try:
            response = yield http_client.post_json_get_nothing(
                post_url, assoc, {})
        except Exception as e:
            self._notifyErrback(assoc, attempt, e)
            return

        # If the request failed, try again with exponential backoff
        if response.code != 200:
            self._notifyErrback(
                assoc, attempt,
                "Non-OK error code received (%d)" % response.code)
        else:
            logger.info("Successfully notified on bind for %s" % (mxid, ))

            # Only remove sent tokens when they've been successfully sent.
            try:
                joinTokenStore = JoinTokenStore(self.sydent)
                joinTokenStore.deleteTokens(assoc["medium"], assoc["address"])
                logger.info(
                    "Successfully deleted invite for %s from the store",
                    assoc["address"],
                )
            except Exception as e:
                logger.exception(
                    "Couldn't remove invite for %s from the store",
                    assoc["address"],
                )
Esempio n. 5
0
class RegisterServlet(Resource):
    isLeaf = True

    def __init__(self, syd):
        self.sydent = syd
        self.client = FederationHttpClient(self.sydent)

    @deferjsonwrap
    @defer.inlineCallbacks
    def render_POST(self, request):
        """
        Register with the Identity Server
        """
        send_cors(request)

        args = get_args(request, ('matrix_server_name', 'access_token'))

        result = yield self.client.get_json(
            "matrix://%s/_matrix/federation/v1/openid/userinfo?access_token=%s"
            % (
                args['matrix_server_name'],
                urllib.quote(args['access_token']),
            ), )
        if 'sub' not in result:
            raise Exception("Invalid response from Homeserver")

        user_id = result['sub']
        tok = yield issueToken(self.sydent, user_id)

        # XXX: `token` is correct for the spec, but we released with `access_token`
        # for a substantial amount of time. Serve both to make spec-compliant clients
        # happy.
        defer.returnValue({
            "access_token": tok,
            "token": tok,
        })

    def render_OPTIONS(self, request):
        send_cors(request)
        request.setResponseCode(200)
        return b''
Esempio n. 6
0
    def _getKeysForServer(self, server_name):
        """Get the signing key data from a home server.
        """

        if server_name in self.cache:
            cached = self.cache[server_name]
            now = int(time.time() * 1000)
            if cached['valid_until_ts'] > now:
                defer.returnValue(self.cache[server_name]['verify_keys'])

        client = FederationHttpClient(self.sydent)
        result = yield client.get_json("matrix://%s/_matrix/key/v2/server/" % server_name)
        if 'verify_keys' not in result:
            raise SignatureVerifyException("No key found in response")

        if 'valid_until_ts' in result:
            # Don't cache anything without a valid_until_ts or we wouldn't
            # know when to expire it.
            logger.info("Got keys for %s: caching until %s", server_name, result['valid_until_ts'])
            self.cache[server_name] = result

        defer.returnValue(result['verify_keys'])
Esempio n. 7
0
    async def _getKeysForServer(self, server_name: str) -> VerifyKeys:
        """Get the signing key data from a homeserver.

        :param server_name: The name of the server to request the keys from.

        :return: The verification keys returned by the server.
        """

        if server_name in self.cache:
            cached = self.cache[server_name]
            now = int(time.time() * 1000)
            if cached.valid_until_ts > now:
                return cached.verify_keys

        client = FederationHttpClient(self.sydent)
        # Cast safety: we have validation logic below which checks that
        # - `verify_keys` is present
        # - `valid_until_ts` is an integer if present
        # - cached entries always have a `valid_until_ts` key
        # and we don't use any of the other fields on GetKeyResponse.
        # The only use of the cache is in this function, and only to read
        # the two fields mentioned above.
        result = await client.get_json(
            "matrix://%s/_matrix/key/v2/server/" % server_name, 1024 * 50)
        if "verify_keys" not in result:
            raise SignatureVerifyException("No key found in response")

        if not isinstance(result["verify_keys"], dict):
            raise SignatureVerifyException(
                f"Invalid type for verify_keys: expected dict, got {result['verify_keys']}"
            )

        keys_to_remove = []
        for key_name, key_dict in result["verify_keys"].items():
            if "key" not in key_dict:
                logger.warning("Ignoring key %s with no 'key'", key_name)
                keys_to_remove.append(key_name)
            elif not isinstance(key_dict["key"], str):
                raise SignatureVerifyException(
                    f"Invalid type for verify_keys/{key_name}/key: "
                    f"expected str, got {key_dict['key']}")
        for key_name in keys_to_remove:
            del result["verify_keys"][key_name]

        # We've now verified that verify_keys has the correct type.
        verify_keys: VerifyKeys = result["verify_keys"]

        if "valid_until_ts" in result:
            if not isinstance(result["valid_until_ts"], int):
                raise SignatureVerifyException(
                    "Invalid valid_until_ts received, must be an integer")

            # Don't cache anything without a valid_until_ts or we wouldn't
            # know when to expire it.

            logger.info(
                "Got keys for %s: caching until %d",
                server_name,
                result["valid_until_ts"],
            )
            self.cache[server_name] = CachedVerificationKeys(
                verify_keys, result["valid_until_ts"])

        return verify_keys
Esempio n. 8
0
 def __init__(self, syd):
     self.sydent = syd
     self.client = FederationHttpClient(self.sydent)
Esempio n. 9
0
 def __init__(self, syd: "Sydent") -> None:
     self.sydent = syd
     self.client = FederationHttpClient(self.sydent)