コード例 #1
0
    def test_delete_on_bind(self):
        """Tests that 3PID invite tokens are deleted upon delivery after a successful
        bind.
        """
        self.sydent.run()

        # The 3PID we're working with.
        medium = "email"
        address = "*****@*****.**"

        # Mock post_json_get_nothing so the /onBind call doesn't fail.
        async def post_json_get_nothing(uri, post_json, opts):
            return Response((b"HTTP", 1, 1), 200, b"OK", None, None)

        FederationHttpClient.post_json_get_nothing = Mock(
            side_effect=post_json_get_nothing, )

        # Manually insert an invite token, we'll check later that it's been deleted.
        join_token_store = JoinTokenStore(self.sydent)
        join_token_store.storeToken(
            medium,
            address,
            "!someroom:example.com",
            "@jane:example.com",
            "sometoken",
        )

        # Make sure the token still exists and can be retrieved.
        tokens = join_token_store.getTokens(medium, address)
        self.assertEqual(len(tokens), 1, tokens)

        # Bind the 3PID
        self.sydent.threepidBinder.addBinding(
            medium,
            address,
            "@john:example.com",
        )

        # Give Sydent some time to call /onBind and delete the token.
        self.sydent.reactor.advance(1000)

        cur = self.sydent.db.cursor()

        # Manually retrieve the tokens for this 3PID. We don't use getTokens because it
        # filters out sent tokens, so would return nothing even if the token hasn't been
        # deleted.
        res = cur.execute(
            "SELECT medium, address, room_id, sender, token FROM invite_tokens"
            " WHERE medium = ? AND address = ?",
            (
                medium,
                address,
            ),
        )
        rows = res.fetchall()

        # Check that we didn't get any result.
        self.assertEqual(len(rows), 0, rows)
コード例 #2
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"],
                )
コード例 #3
0
ファイル: bind.py プロジェクト: jzillioux/sydent
    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"],
                )
コード例 #4
0
ファイル: bind.py プロジェクト: micromingle/sydent
    def addBinding(self, medium, address, mxid):
        """Binds the given 3pid to the given mxid.

        It's assumed that we have somehow validated that the given user owns
        the given 3pid

        Args:
            medium (str): the type of 3pid
            address (str): the 3pid
            mxid (str): the mxid to bind it to
        """
        localAssocStore = LocalAssociationStore(self.sydent)

        createdAt = time_msec()
        expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS
        assoc = ThreepidAssociation(medium, address, mxid, createdAt,
                                    createdAt, expires)

        localAssocStore.addOrUpdateAssociation(assoc)

        self.sydent.pusher.doLocalPush()

        joinTokenStore = JoinTokenStore(self.sydent)
        pendingJoinTokens = joinTokenStore.getTokens(medium, address)
        invites = []
        for token in pendingJoinTokens:
            token["mxid"] = mxid
            token["signed"] = {
                "mxid": mxid,
                "token": token["token"],
            }
            token["signed"] = signedjson.sign.sign_json(
                token["signed"], self.sydent.server_name,
                self.sydent.keyring.ed25519)
            invites.append(token)
        if invites:
            assoc.extra_fields["invites"] = invites
            joinTokenStore.markTokensAsSent(medium, address)

        signer = Signer(self.sydent)
        sgassoc = signer.signedThreePidAssociation(assoc)

        self._notify(sgassoc, 0)

        return sgassoc
コード例 #5
0
ファイル: bind.py プロジェクト: matrix-org/sydent
    def addBinding(self, medium, address, mxid):
        """Binds the given 3pid to the given mxid.

        It's assumed that we have somehow validated that the given user owns
        the given 3pid

        Args:
            medium (str): the type of 3pid
            address (str): the 3pid
            mxid (str): the mxid to bind it to
        """
        localAssocStore = LocalAssociationStore(self.sydent)

        createdAt = time_msec()
        expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS
        assoc = ThreepidAssociation(medium, address, mxid, createdAt, createdAt, expires)

        localAssocStore.addOrUpdateAssociation(assoc)

        self.sydent.pusher.doLocalPush()

        joinTokenStore = JoinTokenStore(self.sydent)
        pendingJoinTokens = joinTokenStore.getTokens(medium, address)
        invites = []
        for token in pendingJoinTokens:
            token["mxid"] = mxid
            token["signed"] = {
                "mxid": mxid,
                "token": token["token"],
            }
            token["signed"] = signedjson.sign.sign_json(token["signed"], self.sydent.server_name, self.sydent.keyring.ed25519)
            invites.append(token)
        if invites:
            assoc.extra_fields["invites"] = invites
            joinTokenStore.markTokensAsSent(medium, address)

        signer = Signer(self.sydent)
        sgassoc = signer.signedThreePidAssociation(assoc)

        self._notify(sgassoc, 0)

        return sgassoc
コード例 #6
0
class BlindlySignStuffServlet(Resource):
    isLeaf = True

    def __init__(self, syd):
        self.server_name = syd.server_name
        self.tokenStore = JoinTokenStore(syd)

    def render_POST(self, request):
        send_cors(request)
        err, args = get_args(request, ("private_key", "token", "mxid"))
        if err:
            return json.dumps(err)

        private_key_base64 = args['private_key']
        token = args['token']
        mxid = args['mxid']

        sender = self.tokenStore.getSenderForToken(token)
        if sender is None:
            request.setResponseCode(404)
            return json.dumps({
                "errcode": "M_UNRECOGNIZED",
                "error": "Didn't recognize token",
            })

        to_sign = {
            "mxid": mxid,
            "sender": sender,
            "token": token,
        }
        try:
            private_key = signedjson.key.decode_signing_key_base64(
                "ed25519",
                "0",
                private_key_base64
            )
            signed = signedjson.sign.sign_json(
                to_sign,
                self.server_name,
                private_key
            )
        except:
            return json.dumps({
                "errcode": "M_UNKNOWN",
            })

        return json.dumps(signed)

    @jsonwrap
    def render_OPTIONS(self, request):
        send_cors(request)
        request.setResponseCode(200)
        return {}
コード例 #7
0
    def addBinding(self, valSessionId, clientSecret, mxid):
        valSessionStore = ThreePidValSessionStore(self.sydent)
        localAssocStore = LocalAssociationStore(self.sydent)

        s = valSessionStore.getValidatedSession(valSessionId, clientSecret)

        createdAt = time_msec()
        expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS

        assoc = ThreepidAssociation(s.medium, s.address, mxid, createdAt,
                                    createdAt, expires)

        localAssocStore.addOrUpdateAssociation(assoc)

        self.sydent.pusher.doLocalPush()

        joinTokenStore = JoinTokenStore(self.sydent)
        pendingJoinTokens = joinTokenStore.getTokens(s.medium, s.address)
        invites = []
        for token in pendingJoinTokens:
            token["mxid"] = mxid
            token["signed"] = {
                "mxid": mxid,
                "token": token["token"],
            }
            token["signed"] = signedjson.sign.sign_json(
                token["signed"], self.sydent.server_name,
                self.sydent.keyring.ed25519)
            invites.append(token)
        if invites:
            assoc.extra_fields["invites"] = invites
            joinTokenStore.markTokensAsSent(s.medium, s.address)

        assocSigner = AssociationSigner(self.sydent)
        sgassoc = assocSigner.signedThreePidAssociation(assoc)

        self._notify(sgassoc, 0)

        return sgassoc
コード例 #8
0
class EphemeralPubkeyIsValidServlet(Resource):
    isLeaf = True

    def __init__(self, syd):
        self.joinTokenStore = JoinTokenStore(syd)

    @jsonwrap
    def render_GET(self, request):
        args = get_args(request, ("public_key",))
        publicKey = args["public_key"]

        return {
            'valid': self.joinTokenStore.validateEphemeralPublicKey(publicKey),
        }
コード例 #9
0
ファイル: bind.py プロジェクト: flootr/sydent
    def addBinding(self, valSessionId, clientSecret, mxid):
        valSessionStore = ThreePidValSessionStore(self.sydent)
        localAssocStore = LocalAssociationStore(self.sydent)

        s = valSessionStore.getValidatedSession(valSessionId, clientSecret)

        createdAt = time_msec()
        expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS

        assoc = ThreepidAssociation(s.medium, s.address, mxid, createdAt, createdAt, expires)

        localAssocStore.addOrUpdateAssociation(assoc)

        self.sydent.pusher.doLocalPush()

        joinTokenStore = JoinTokenStore(self.sydent)
        pendingJoinTokens = joinTokenStore.getTokens(s.medium, s.address)
        invites = []
        for token in pendingJoinTokens:
            token["mxid"] = mxid
            token["signed"] = {
                "mxid": mxid,
                "token": token["token"],
            }
            token["signed"] = signedjson.sign.sign_json(token["signed"], self.sydent.server_name, self.sydent.keyring.ed25519)
            invites.append(token)
        if invites:
            assoc.extra_fields["invites"] = invites
            joinTokenStore.markTokensAsSent(s.medium, s.address)

        assocSigner = AssociationSigner(self.sydent)
        sgassoc = assocSigner.signedThreePidAssociation(assoc)

        self._notify(sgassoc, 0)

        return sgassoc
コード例 #10
0
ファイル: pubkeyservlets.py プロジェクト: matrix-org/sydent
class EphemeralPubkeyIsValidServlet(Resource):
    isLeaf = True

    def __init__(self, syd):
        self.joinTokenStore = JoinTokenStore(syd)

    def render_GET(self, request):
        err, args = get_args(request, ("public_key",))
        if err:
            return json.dumps(err)
        publicKey = args["public_key"]

        return json.dumps({
            'valid': self.joinTokenStore.validateEphemeralPublicKey(publicKey),
        })
コード例 #11
0
class EphemeralPubkeyIsValidServlet(Resource):
    isLeaf = True

    def __init__(self, syd):
        self.joinTokenStore = JoinTokenStore(syd)

    def render_GET(self, request):
        err = require_args(request, ("public_key", ))
        if err:
            return json.dumps(err)
        publicKey = request.args["public_key"][0]

        return json.dumps({
            'valid':
            self.joinTokenStore.validateEphemeralPublicKey(publicKey),
        })
コード例 #12
0
class BlindlySignStuffServlet(Resource):
    isLeaf = True

    def __init__(self, syd: "Sydent", require_auth: bool = False) -> None:
        self.sydent = syd
        self.server_name = syd.config.general.server_name
        self.tokenStore = JoinTokenStore(syd)
        self.require_auth = require_auth

    @jsonwrap
    def render_POST(self, request: Request) -> JsonDict:
        send_cors(request)

        if self.require_auth:
            authV2(self.sydent, request)

        args = get_args(request, ("private_key", "token", "mxid"))

        private_key_base64 = args["private_key"]
        token = args["token"]
        mxid = args["mxid"]

        sender = self.tokenStore.getSenderForToken(token)
        if sender is None:
            raise MatrixRestError(404, "M_UNRECOGNIZED", "Didn't recognize token")

        to_sign = {
            "mxid": mxid,
            "sender": sender,
            "token": token,
        }
        try:
            private_key = signedjson.key.decode_signing_key_base64(
                "ed25519", "0", private_key_base64
            )
            signed: JsonDict = signedjson.sign.sign_json(
                to_sign, self.server_name, private_key
            )
        except Exception:
            logger.exception("signing failed")
            raise MatrixRestError(500, "M_UNKNOWN", "Internal Server Error")

        return signed

    def render_OPTIONS(self, request: Request) -> bytes:
        send_cors(request)
        return b""
コード例 #13
0
class BlindlySignStuffServlet(Resource):
    isLeaf = True

    def __init__(self, syd):
        self.server_name = syd.server_name
        self.tokenStore = JoinTokenStore(syd)

    def render_POST(self, request):
        send_cors(request)
        err, args = get_args(request, ("private_key", "token", "mxid"))
        if err:
            return json.dumps(err)

        private_key_base64 = args['private_key']
        token = args['token']
        mxid = args['mxid']

        sender = self.tokenStore.getSenderForToken(token)
        if sender is None:
            request.setResponseCode(404)
            return json.dumps({
                "errcode": "M_UNRECOGNIZED",
                "error": "Didn't recognize token",
            })

        to_sign = {
            "mxid": mxid,
            "sender": sender,
            "token": token,
        }
        try:
            private_key = signedjson.key.decode_signing_key_base64(
                "ed25519", "0", private_key_base64)
            signed = signedjson.sign.sign_json(to_sign, self.server_name,
                                               private_key)
        except:
            return json.dumps({
                "errcode": "M_UNKNOWN",
            })

        return json.dumps(signed)

    @jsonwrap
    def render_OPTIONS(self, request):
        send_cors(request)
        request.setResponseCode(200)
        return {}
コード例 #14
0
class BlindlySignStuffServlet(Resource):
    isLeaf = True

    def __init__(self, syd):
        self.sydent = syd
        self.server_name = syd.server_name
        self.tokenStore = JoinTokenStore(syd)

    @jsonwrap
    def render_POST(self, request):
        send_cors(request)

        authIfV2(self.sydent, request)

        args = get_args(request, ("private_key", "token", "mxid"))

        private_key_base64 = args['private_key']
        token = args['token']
        mxid = args['mxid']

        sender = self.tokenStore.getSenderForToken(token)
        if sender is None:
            raise MatrixRestError(404, "M_UNRECOGNIZED",
                                  "Didn't recognize token")

        to_sign = {
            "mxid": mxid,
            "sender": sender,
            "token": token,
        }
        try:
            private_key = signedjson.key.decode_signing_key_base64(
                "ed25519", "0", private_key_base64)
            signed = signedjson.sign.sign_json(to_sign, self.server_name,
                                               private_key)
        except:
            logger.exception("signing failed")
            raise MatrixRestError(500, "M_UNKNOWN", "Internal Server Error")

        return signed

    def render_OPTIONS(self, request):
        send_cors(request)
        request.setResponseCode(200)
        return b''
コード例 #15
0
ファイル: pubkeyservlets.py プロジェクト: matrix-org/sydent
 def __init__(self, syd):
     self.joinTokenStore = JoinTokenStore(syd)
コード例 #16
0
ファイル: bind.py プロジェクト: eachchat/sydent
    def addBinding(self, medium: str, address: str,
                   mxid: str) -> Dict[str, Any]:
        """
        Binds the given 3pid to the given mxid.

        It's assumed that we have somehow validated that the given user owns
        the given 3pid

        :param medium: The medium of the 3PID to bind.
        :param address: The address of the 3PID to bind.
        :param mxid: The MXID to bind the 3PID to.

        :return: The signed association.
        """

        # ensure we casefold email address before storing
        normalised_address = normalise_address(address, medium)

        localAssocStore = LocalAssociationStore(self.sydent)

        # Fill out the association details
        createdAt = time_msec()
        expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS

        # Hash the medium + address and store that hash for the purposes of
        # later lookups
        lookup_pepper = self.hashing_store.get_lookup_pepper()
        assert lookup_pepper is not None
        str_to_hash = " ".join([normalised_address, medium, lookup_pepper], )
        lookup_hash = sha256_and_url_safe_base64(str_to_hash)

        assoc = ThreepidAssociation(
            medium,
            normalised_address,
            lookup_hash,
            mxid,
            createdAt,
            createdAt,
            expires,
        )

        localAssocStore.addOrUpdateAssociation(assoc)

        self.sydent.pusher.doLocalPush()

        joinTokenStore = JoinTokenStore(self.sydent)
        pendingJoinTokens = joinTokenStore.getTokens(medium,
                                                     normalised_address)
        invites = []
        # Widen the value type to Any: we're going to set the signed key
        # to point to a dict, but pendingJoinTokens yields Dict[str, str]
        token: Dict[str, Any]
        for token in pendingJoinTokens:
            token["mxid"] = mxid
            presigned = {
                "mxid": mxid,
                "token": token["token"],
            }
            token["signed"] = signedjson.sign.sign_json(
                presigned,
                self.sydent.config.general.server_name,
                self.sydent.keyring.ed25519,
            )
            invites.append(token)
        if invites:
            assoc.extra_fields["invites"] = invites
            joinTokenStore.markTokensAsSent(medium, normalised_address)

        signer = Signer(self.sydent)
        sgassoc = signer.signedThreePidAssociation(assoc)

        defer.ensureDeferred(self._notify(sgassoc, 0))

        return sgassoc
コード例 #17
0
    def render_POST(self, request):
        send_cors(request)
        err = require_args(request, ("medium", "address", "room_id", "sender",))
        if err:
            return json.dumps(err)
        medium = request.args["medium"][0]
        address = request.args["address"][0]
        roomId = request.args["room_id"][0]
        sender = request.args["sender"][0]

        globalAssocStore = GlobalAssociationStore(self.sydent)
        mxid = globalAssocStore.getMxid(medium, address)
        if mxid:
            request.setResponseCode(400)
            return json.dumps({
                "errcode": "THREEPID_IN_USE",
                "error": "Binding already known",
                "mxid": mxid,
            })

        if medium != "email":
            request.setResponseCode(400)
            return json.dumps({
                "errcode": "M_UNRECOGNIZED",
                "error": "Didn't understand medium '%s'" % (medium,),
            })

        token = self._randomString(128)

        tokenStore = JoinTokenStore(self.sydent)

        ephemeralPrivateKey = nacl.signing.SigningKey.generate()
        ephemeralPublicKey = ephemeralPrivateKey.verify_key

        ephemeralPrivateKeyBase64 = encode_base64(ephemeralPrivateKey.encode(), True)
        ephemeralPublicKeyBase64 = encode_base64(ephemeralPublicKey.encode(), True)

        tokenStore.storeEphemeralPublicKey(ephemeralPublicKeyBase64)
        tokenStore.storeToken(medium, address, roomId, sender, token)

        substitutions = {}
        for key, values in request.args.items():
            if len(values) == 1 and type(values[0]) == str:
                substitutions[key] = values[0]
        substitutions["token"] = token

        required = [
            'sender_display_name',
            'token',
            'room_name',
            'bracketed_room_name',
            'room_avatar_url',
            'sender_display_name',
            'guest_user_id',
            'guest_access_token',
        ]
        for k in required:
            substitutions.setdefault(k, '')

        substitutions["ephemeral_private_key"] = ephemeralPrivateKeyBase64
        if substitutions["room_name"] != '':
            substitutions["bracketed_room_name"] = "(%s)" % substitutions["room_name"]

        subject_header = Header(self.sydent.cfg.get('email', 'email.invite.subject', raw=True) % substitutions, 'utf8')
        substitutions["subject_header_value"] = subject_header.encode()

        sendEmail(self.sydent, "email.invite_template", address, substitutions)

        pubKey = self.sydent.keyring.ed25519.verify_key
        pubKeyBase64 = encode_base64(pubKey.encode())

        baseUrl = "%s/_matrix/identity/api/v1" % (self.sydent.cfg.get('http', 'client_http_base'),)

        keysToReturn = []
        keysToReturn.append({
            "public_key": pubKeyBase64,
            "key_validity_url": baseUrl + "/pubkey/isvalid",
        })
        keysToReturn.append({
            "public_key": ephemeralPublicKeyBase64,
            "key_validity_url": baseUrl + "/pubkey/ephemeral/isvalid",
        })

        resp = {
            "token": token,
            "public_key": pubKeyBase64,
            "public_keys": keysToReturn,
            "display_name": self.redact(address),
        }

        return json.dumps(resp)
コード例 #18
0
ファイル: pubkeyservlets.py プロジェクト: matrix-org/sydent
 def __init__(self, syd: "Sydent") -> None:
     self.joinTokenStore = JoinTokenStore(syd)
コード例 #19
0
 def __init__(self, syd):
     self.server_name = syd.server_name
     self.tokenStore = JoinTokenStore(syd)
コード例 #20
0
 def __init__(self, syd):
     self.joinTokenStore = JoinTokenStore(syd)
コード例 #21
0
    def render_POST(self, request: Request) -> JsonDict:
        send_cors(request)

        args = get_args(
            request,
            (
                "medium",
                "address",
                "room_id",
                "sender",
            ),
        )
        medium = args["medium"]
        address = args["address"]
        roomId = args["room_id"]
        sender = args["sender"]

        # ensure we are casefolding email address before storing
        normalised_address = normalise_address(address, medium)

        verified_sender = None
        if self.require_auth:
            account = authV2(self.sydent, request)
            verified_sender = sender
            if account.userId != sender:
                raise MatrixRestError(403, "M_UNAUTHORIZED",
                                      "'sender' doesn't match")

        globalAssocStore = GlobalAssociationStore(self.sydent)
        mxid = globalAssocStore.getMxid(medium, normalised_address)
        if mxid:
            request.setResponseCode(400)
            return {
                "errcode": "M_THREEPID_IN_USE",
                "error": "Binding already known",
                "mxid": mxid,
            }

        if medium != "email":
            request.setResponseCode(400)
            return {
                "errcode": "M_UNRECOGNIZED",
                "error": "Didn't understand medium '%s'" % (medium, ),
            }

        if not (0 < len(address) <= MAX_EMAIL_ADDRESS_LENGTH):
            request.setResponseCode(400)
            return {
                "errcode": "M_INVALID_PARAM",
                "error": "Invalid email provided"
            }

        token = self._randomString(128)

        tokenStore = JoinTokenStore(self.sydent)

        ephemeralPrivateKey = nacl.signing.SigningKey.generate()
        ephemeralPublicKey = ephemeralPrivateKey.verify_key

        ephemeralPrivateKeyBase64 = encode_base64(ephemeralPrivateKey.encode(),
                                                  True)
        ephemeralPublicKeyBase64 = encode_base64(ephemeralPublicKey.encode(),
                                                 True)

        tokenStore.storeEphemeralPublicKey(ephemeralPublicKeyBase64)
        tokenStore.storeToken(medium, normalised_address, roomId, sender,
                              token)

        # Variables to substitute in the template.
        substitutions = {}
        # Include all arguments sent via the request.
        for k, v in args.items():
            if isinstance(v, str):
                substitutions[k] = v
        substitutions["token"] = token

        # Substitutions that the template requires, but are optional to provide
        # to the API.
        extra_substitutions = [
            "sender_display_name",
            "token",
            "room_name",
            "bracketed_room_name",
            "room_avatar_url",
            "sender_avatar_url",
            "guest_user_id",
            "guest_access_token",
        ]
        for k in extra_substitutions:
            substitutions.setdefault(k, "")

        # For MSC3288 room type, prefer the stable field, but fallback to the
        # unstable field.
        if "room_type" not in substitutions:
            substitutions["room_type"] = substitutions.get(
                "org.matrix.msc3288.room_type", "")

        substitutions["bracketed_verified_sender"] = ""
        if verified_sender:
            substitutions["bracketed_verified_sender"] = "(%s) " % (
                verified_sender, )

        substitutions["ephemeral_private_key"] = ephemeralPrivateKeyBase64
        if substitutions["room_name"] != "":
            substitutions[
                "bracketed_room_name"] = "(%s) " % substitutions["room_name"]

        substitutions[
            "web_client_location"] = self.sydent.config.email.default_web_client_location
        if "org.matrix.web_client_location" in substitutions:
            substitutions["web_client_location"] = substitutions[
                "org.matrix.web_client_location"]

        if substitutions["room_type"] == "m.space":
            subject = self.sydent.config.email.invite_subject_space % substitutions
        else:
            subject = self.sydent.config.email.invite_subject % substitutions

        substitutions["subject_header_value"] = Header(subject,
                                                       "utf8").encode()

        brand = self.sydent.brand_from_request(request)

        # self.sydent.config.email.invite_template is deprecated
        if self.sydent.config.email.invite_template is None:
            templateFile = self.sydent.get_branded_template(
                brand,
                "invite_template.eml",
            )
        else:
            templateFile = self.sydent.config.email.invite_template

        try:
            sendEmail(self.sydent, templateFile, normalised_address,
                      substitutions)
        except EmailAddressException:
            request.setResponseCode(HTTPStatus.BAD_REQUEST)
            return {
                "errcode": "M_INVALID_EMAIL",
                "error": "Invalid email address"
            }

        pubKey = self.sydent.keyring.ed25519.verify_key
        pubKeyBase64 = encode_base64(pubKey.encode())

        baseUrl = "%s/_matrix/identity/api/v1" % (
            self.sydent.config.http.server_http_url_base, )

        keysToReturn = []
        keysToReturn.append({
            "public_key": pubKeyBase64,
            "key_validity_url": baseUrl + "/pubkey/isvalid",
        })
        keysToReturn.append({
            "public_key":
            ephemeralPublicKeyBase64,
            "key_validity_url":
            baseUrl + "/pubkey/ephemeral/isvalid",
        })

        resp = {
            "token": token,
            "public_key": pubKeyBase64,
            "public_keys": keysToReturn,
            "display_name": self.redact_email_address(address),
        }

        return resp
コード例 #22
0
 def __init__(self, syd: "Sydent", require_auth: bool = False) -> None:
     self.sydent = syd
     self.server_name = syd.config.general.server_name
     self.tokenStore = JoinTokenStore(syd)
     self.require_auth = require_auth
コード例 #23
0
 def __init__(self, syd):
     self.server_name = syd.server_name
     self.tokenStore = JoinTokenStore(syd)
コード例 #24
0
    def addBinding(self, medium, address, mxid):
        """
        Binds the given 3pid to the given mxid.

        It's assumed that we have somehow validated that the given user owns
        the given 3pid

        :param medium: The medium of the 3PID to bind.
        :type medium: unicode
        :param address: The address of the 3PID to bind.
        :type address: unicode
        :param mxid: The MXID to bind the 3PID to.
        :type mxid: unicode

        :return: The signed association.
        :rtype: dict[str, any]
        """
        localAssocStore = LocalAssociationStore(self.sydent)

        # Fill out the association details
        createdAt = time_msec()
        expires = createdAt + ThreepidBinder.THREEPID_ASSOCIATION_LIFETIME_MS

        # Hash the medium + address and store that hash for the purposes of
        # later lookups
        str_to_hash = u' '.join(
            [address, medium,
             self.hashing_store.get_lookup_pepper()], )
        lookup_hash = sha256_and_url_safe_base64(str_to_hash)

        assoc = ThreepidAssociation(
            medium,
            address,
            lookup_hash,
            mxid,
            createdAt,
            createdAt,
            expires,
        )

        localAssocStore.addOrUpdateAssociation(assoc)

        self.sydent.pusher.doLocalPush()

        joinTokenStore = JoinTokenStore(self.sydent)
        pendingJoinTokens = joinTokenStore.getTokens(medium, address)
        invites = []
        for token in pendingJoinTokens:
            token["mxid"] = mxid
            token["signed"] = {
                "mxid": mxid,
                "token": token["token"],
            }
            token["signed"] = signedjson.sign.sign_json(
                token["signed"], self.sydent.server_name,
                self.sydent.keyring.ed25519)
            invites.append(token)
        if invites:
            assoc.extra_fields["invites"] = invites
            joinTokenStore.markTokensAsSent(medium, address)

        signer = Signer(self.sydent)
        sgassoc = signer.signedThreePidAssociation(assoc)

        self._notify(sgassoc, 0)

        return sgassoc
コード例 #25
0
    def render_POST(self, request):
        send_cors(request)
        err, args = get_args(request, ("medium", "address", "room_id", "sender",))
        if err:
            return json.dumps(err)
        medium = args["medium"]
        address = args["address"]
        roomId = args["room_id"]
        sender = args["sender"]

        globalAssocStore = GlobalAssociationStore(self.sydent)
        mxid = globalAssocStore.getMxid(medium, address)
        if mxid:
            request.setResponseCode(400)
            return json.dumps({
                "errcode": "THREEPID_IN_USE",
                "error": "Binding already known",
                "mxid": mxid,
            })

        if medium != "email":
            request.setResponseCode(400)
            return json.dumps({
                "errcode": "M_UNRECOGNIZED",
                "error": "Didn't understand medium '%s'" % (medium,),
            })

        token = self._randomString(128)

        tokenStore = JoinTokenStore(self.sydent)

        ephemeralPrivateKey = nacl.signing.SigningKey.generate()
        ephemeralPublicKey = ephemeralPrivateKey.verify_key

        ephemeralPrivateKeyBase64 = encode_base64(ephemeralPrivateKey.encode(), True)
        ephemeralPublicKeyBase64 = encode_base64(ephemeralPublicKey.encode(), True)

        tokenStore.storeEphemeralPublicKey(ephemeralPublicKeyBase64)
        tokenStore.storeToken(medium, address, roomId, sender, token)

        substitutions = {}
        for key, values in request.args.items():
            if len(values) == 1 and type(values[0]) == str:
                substitutions[key] = values[0]
        substitutions["token"] = token

        required = [
            'sender_display_name',
            'token',
            'room_name',
            'bracketed_room_name',
            'room_avatar_url',
            'sender_display_name',
            'guest_user_id',
            'guest_access_token',
        ]
        for k in required:
            substitutions.setdefault(k, '')

        substitutions["ephemeral_private_key"] = ephemeralPrivateKeyBase64
        if substitutions["room_name"] != '':
            substitutions["bracketed_room_name"] = "(%s)" % substitutions["room_name"]

        subject_header = Header(self.sydent.cfg.get('email', 'email.invite.subject', raw=True) % substitutions, 'utf8')
        substitutions["subject_header_value"] = subject_header.encode()

        sendEmail(self.sydent, "email.invite_template", address, substitutions)

        pubKey = self.sydent.keyring.ed25519.verify_key
        pubKeyBase64 = encode_base64(pubKey.encode())

        baseUrl = "%s/_matrix/identity/api/v1" % (self.sydent.cfg.get('http', 'client_http_base'),)

        keysToReturn = []
        keysToReturn.append({
            "public_key": pubKeyBase64,
            "key_validity_url": baseUrl + "/pubkey/isvalid",
        })
        keysToReturn.append({
            "public_key": ephemeralPublicKeyBase64,
            "key_validity_url": baseUrl + "/pubkey/ephemeral/isvalid",
        })

        resp = {
            "token": token,
            "public_key": pubKeyBase64,
            "public_keys": keysToReturn,
            "display_name": self.redact(address),
        }

        return json.dumps(resp)
コード例 #26
0
    def render_POST(self, request):
        send_cors(request)

        authIfV2(self.sydent, request)

        args = get_args(request, ("medium", "address", "room_id", "sender",))
        medium = args["medium"]
        address = args["address"]
        roomId = args["room_id"]
        sender = args["sender"]

        globalAssocStore = GlobalAssociationStore(self.sydent)
        mxid = globalAssocStore.getMxid(medium, address)
        if mxid:
            request.setResponseCode(400)
            return {
                "errcode": "M_THREEPID_IN_USE",
                "error": "Binding already known",
                "mxid": mxid,
            }

        if medium != "email":
            request.setResponseCode(400)
            return {
                "errcode": "M_UNRECOGNIZED",
                "error": "Didn't understand medium '%s'" % (medium,),
            }

        token = self._randomString(128)

        tokenStore = JoinTokenStore(self.sydent)

        ephemeralPrivateKey = nacl.signing.SigningKey.generate()
        ephemeralPublicKey = ephemeralPrivateKey.verify_key

        ephemeralPrivateKeyBase64 = encode_base64(ephemeralPrivateKey.encode(), True)
        ephemeralPublicKeyBase64 = encode_base64(ephemeralPublicKey.encode(), True)

        tokenStore.storeEphemeralPublicKey(ephemeralPublicKeyBase64)
        tokenStore.storeToken(medium, address, roomId, sender, token)

        # Variables to substitute in the template.
        substitutions = {}
        # Include all arguments sent via the request.
        for k, v in args.items():
            if isinstance(v, string_types):
                substitutions[k] = v
        substitutions["token"] = token

        # Substitutions that the template requires, but are optional to provide
        # to the API.
        extra_substitutions = [
            'sender_display_name',
            'token',
            'room_name',
            'bracketed_room_name',
            'room_avatar_url',
            'sender_avatar_url',
            'guest_user_id',
            'guest_access_token',
        ]
        for k in extra_substitutions:
            substitutions.setdefault(k, '')

        substitutions["ephemeral_private_key"] = ephemeralPrivateKeyBase64
        if substitutions["room_name"] != '':
            substitutions["bracketed_room_name"] = "(%s)" % substitutions["room_name"]

        substitutions["web_client_location"] = self.sydent.default_web_client_location
        if 'org.matrix.web_client_location' in substitutions:
            substitutions["web_client_location"] = substitutions.pop("org.matrix.web_client_location")

        subject_header = Header(self.sydent.cfg.get('email', 'email.invite.subject', raw=True) % substitutions, 'utf8')
        substitutions["subject_header_value"] = subject_header.encode()

        brand = self.sydent.brand_from_request(request)
        templateFile = self.sydent.get_branded_template(
            brand,
            "invite_template.eml",
            ('email', 'email.invite_template'),
        )

        sendEmail(self.sydent, templateFile, address, substitutions)

        pubKey = self.sydent.keyring.ed25519.verify_key
        pubKeyBase64 = encode_base64(pubKey.encode())

        baseUrl = "%s/_matrix/identity/api/v1" % (self.sydent.cfg.get('http', 'client_http_base'),)

        keysToReturn = []
        keysToReturn.append({
            "public_key": pubKeyBase64,
            "key_validity_url": baseUrl + "/pubkey/isvalid",
        })
        keysToReturn.append({
            "public_key": ephemeralPublicKeyBase64,
            "key_validity_url": baseUrl + "/pubkey/ephemeral/isvalid",
        })

        resp = {
            "token": token,
            "public_key": pubKeyBase64,
            "public_keys": keysToReturn,
            "display_name": self.redact_email_address(address),
        }

        return resp