Example #1
0
    def pushUpdates(self, sgAssocs):
        globalAssocStore = GlobalAssociationStore(self.sydent)
        for localId in sgAssocs:
            if localId > self.lastId:
                assocObj = threePidAssocFromDict(sgAssocs[localId])

                if assocObj.mxid is not None:
                    # Assign a lookup_hash to this association
                    str_to_hash = ' '.join([
                        assocObj.address, assocObj.medium,
                        self.hashing_store.get_lookup_pepper()
                    ], )
                    assocObj.lookup_hash = sha256_and_url_safe_base64(
                        str_to_hash)

                    # We can probably skip verification for the local peer (although it could
                    # be good as a sanity check)
                    globalAssocStore.addAssociation(
                        assocObj, json.dumps(sgAssocs[localId]),
                        self.sydent.server_name, localId)
                else:
                    globalAssocStore.removeAssociation(assocObj.medium,
                                                       assocObj.address)

        d = defer.succeed(True)
        return d
Example #2
0
    def pushUpdates(self, sgAssocs):
        globalAssocStore = GlobalAssociationStore(self.sydent)
        for localId in sgAssocs:
            if localId > self.lastId:
                assocObj = threePidAssocFromDict(sgAssocs[localId])

                # We can probably skip verification for the local peer (although it could be good as a sanity check)
                globalAssocStore.addAssociation(assocObj, json.dumps(sgAssocs[localId]),
                                                self.sydent.server_name, localId)

        d = twisted.internet.defer.succeed(True)
        return d
Example #3
0
    def pushUpdates(self, sgAssocs):
        globalAssocStore = GlobalAssociationStore(self.sydent)
        for localId in sgAssocs:
            if localId > self.lastId:
                assocObj = threePidAssocFromDict(sgAssocs[localId])
                if assocObj.mxid is not None:
                    # We can probably skip verification for the local peer (although it could be good as a sanity check)
                    globalAssocStore.addAssociation(assocObj, json.dumps(sgAssocs[localId]),
                                                    self.sydent.server_name, localId)
                else:
                    globalAssocStore.removeAssociation(assocObj.medium, assocObj.address)

        d = defer.succeed(True)
        return d
Example #4
0
    def pushUpdates(self, sgAssocs):
        globalAssocStore = GlobalAssociationStore(self.sydent)
        for localId in sgAssocs:
            if localId > self.lastId:
                assocObj = threePidAssocFromDict(sgAssocs[localId])

                # We can probably skip verification for the local peer (although it could be good as a sanity check)
                globalAssocStore.addAssociation(assocObj,
                                                json.dumps(sgAssocs[localId]),
                                                self.sydent.server_name,
                                                localId)

        d = twisted.internet.defer.succeed(True)
        return d
Example #5
0
    def pushUpdates(self, sgAssocs):
        globalAssocStore = GlobalAssociationStore(self.sydent)
        for localId in sgAssocs:
            if localId > self.lastId:
                assocObj = threePidAssocFromDict(sgAssocs[localId])
                if assocObj.mxid is not None:
                    # We can probably skip verification for the local peer (although it could be good as a sanity check)
                    globalAssocStore.addAssociation(
                        assocObj, json.dumps(sgAssocs[localId]),
                        self.sydent.server_name, localId)
                else:
                    globalAssocStore.removeAssociation(assocObj.medium,
                                                       assocObj.address)

        d = defer.succeed(True)
        return d
Example #6
0
    def pushUpdates(self, sgAssocs: SignedAssociations) -> "Deferred[bool]":
        """
        Saves the given associations in the global associations store. Only stores an
        association if its ID is greater than the last seen ID.

        :param sgAssocs: The associations to save.

        :return: A deferred that succeeds with the value `True`.
        """
        globalAssocStore = GlobalAssociationStore(self.sydent)
        for localId in sgAssocs:
            if localId > self.lastId:
                assocObj = threePidAssocFromDict(sgAssocs[localId])

                # ensure we are casefolding email addresses
                assocObj.address = normalise_address(assocObj.address,
                                                     assocObj.medium)

                if assocObj.mxid is not None:
                    # Assign a lookup_hash to this association
                    pepper = self.hashing_store.get_lookup_pepper()
                    if not pepper:
                        raise RuntimeError("No lookup_pepper in the database.")
                    str_to_hash = " ".join([
                        assocObj.address,
                        assocObj.medium,
                        pepper,
                    ], )
                    assocObj.lookup_hash = sha256_and_url_safe_base64(
                        str_to_hash)

                    # We can probably skip verification for the local peer (although it could
                    # be good as a sanity check)
                    globalAssocStore.addAssociation(
                        assocObj,
                        json.dumps(sgAssocs[localId]),
                        self.sydent.config.general.server_name,
                        localId,
                    )
                else:
                    globalAssocStore.removeAssociation(assocObj.medium,
                                                       assocObj.address)

        d = defer.succeed(True)
        return d
Example #7
0
    def pushUpdates(self, sgAssocs):
        """
        Saves the given associations in the global associations store. Only stores an
        association if its ID is greater than the last seen ID.

        :param sgAssocs: The associations to save.
        :type sgAssocs: dict[int, dict[str, any]]

        :return: True
        :rtype: twisted.internet.defer.Deferred[bool]
        """
        globalAssocStore = GlobalAssociationStore(self.sydent)
        for localId in sgAssocs:
            if localId > self.lastId:
                assocObj = threePidAssocFromDict(sgAssocs[localId])

                if assocObj.mxid is not None:
                    # Assign a lookup_hash to this association
                    str_to_hash = u' '.join([
                        assocObj.address, assocObj.medium,
                        self.hashing_store.get_lookup_pepper()
                    ], )
                    assocObj.lookup_hash = sha256_and_url_safe_base64(
                        str_to_hash)

                    # We can probably skip verification for the local peer (although it could
                    # be good as a sanity check)
                    globalAssocStore.addAssociation(
                        assocObj, json.dumps(sgAssocs[localId]),
                        self.sydent.server_name, localId)
                else:
                    globalAssocStore.removeAssociation(assocObj.medium,
                                                       assocObj.address)

        d = defer.succeed(True)
        return d
Example #8
0
    def render_POST(self, request):
        peerCert = request.transport.getPeerCertificate()
        peerCertCn = peerCert.get_subject().commonName

        peerStore = PeerStore(self.sydent)

        peer = peerStore.getPeerByName(peerCertCn)

        if not peer:
            logger.warn(
                "Got connection from %s but no peer found by that name",
                peerCertCn)
            request.setResponseCode(403)
            return {
                'errcode': 'M_UNKNOWN_PEER',
                'error': 'This peer is not known to this server'
            }

        logger.info("Push connection made from peer %s", peer.servername)

        if not request.requestHeaders.hasHeader('Content-Type') or \
                request.requestHeaders.getRawHeaders('Content-Type')[0] != 'application/json':
            logger.warn(
                "Peer %s made push connection with non-JSON content (type: %s)",
                peer.servername,
                request.requestHeaders.getRawHeaders('Content-Type')[0])
            return {
                'errcode': 'M_NOT_JSON',
                'error': 'This endpoint expects JSON'
            }

        try:
            inJson = json.load(request.content)
        except ValueError:
            logger.warn("Peer %s made push connection with malformed JSON",
                        peer.servername)
            return {'errcode': 'M_BAD_JSON', 'error': 'Malformed JSON'}

        if 'sgAssocs' not in inJson:
            logger.warn(
                "Peer %s made push connection with no 'sgAssocs' key in JSON",
                peer.servername)
            return {
                'errcode': 'M_BAD_JSON',
                'error': 'No "sgAssocs" key in JSON'
            }

        failedIds = []

        globalAssocsStore = GlobalAssociationStore(self.sydent)

        for originId, sgAssoc in inJson['sgAssocs'].items():
            try:
                peer.verifyMessage(sgAssoc)
                logger.debug(
                    "Signed association from %s with origin ID %s verified",
                    peer.servername, originId)

                # Don't bother adding if one has already failed: we add all of them or none so we're only going to
                # roll back the transaction anyway (but we continue to try & verify the rest so we can give a
                # complete list of the ones that don't verify)
                if len(failedIds) > 0:
                    continue

                assocObj = threePidAssocFromDict(sgAssoc)

                if assocObj.mxid is not None:
                    globalAssocsStore.addAssociation(assocObj,
                                                     json.dumps(sgAssoc),
                                                     peer.servername,
                                                     originId,
                                                     commit=False)
                else:
                    logger.info(
                        "Incoming deletion: removing associations for %s / %s",
                        assocObj.medium, assocObj.address)
                    globalAssocsStore.removeAssociation(
                        assocObj.medium, assocObj.address)
                logger.info("Stored association origin ID %s from %s",
                            originId, peer.servername)
            except:
                failedIds.append(originId)
                logger.warn(
                    "Failed to verify signed association from %s with origin ID %s",
                    peer.servername, originId)
                twisted.python.log.err()

        if len(failedIds) > 0:
            self.sydent.db.rollback()
            request.setResponseCode(400)
            return {
                'errcode': 'M_VERIFICATION_FAILED',
                'error': 'Verification failed for one or more associations',
                'failed_ids': failedIds
            }
        else:
            self.sydent.db.commit()
            return {'success': True}
Example #9
0
    def render_POST(self, request):
        peerCert = request.transport.getPeerCertificate()
        peerCertCn = peerCert.get_subject().commonName

        peerStore = PeerStore(self.sydent)

        peer = peerStore.getPeerByName(peerCertCn)

        if not peer:
            logger.warn(
                "Got connection from %s but no peer found by that name",
                peerCertCn)
            raise MatrixRestError(403, 'M_UNKNOWN_PEER',
                                  'This peer is not known to this server')

        logger.info("Push connection made from peer %s", peer.servername)

        if not request.requestHeaders.hasHeader('Content-Type') or \
                request.requestHeaders.getRawHeaders('Content-Type')[0] != 'application/json':
            logger.warn(
                "Peer %s made push connection with non-JSON content (type: %s)",
                peer.servername,
                request.requestHeaders.getRawHeaders('Content-Type')[0])
            raise MatrixRestError(400, 'M_NOT_JSON',
                                  'This endpoint expects JSON')

        try:
            # json.loads doesn't allow bytes in Python 3.5
            inJson = json.loads(request.content.read().decode("UTF-8"))
        except ValueError:
            logger.warn("Peer %s made push connection with malformed JSON",
                        peer.servername)
            raise MatrixRestError(400, 'M_BAD_JSON', 'Malformed JSON')

        if 'sgAssocs' not in inJson:
            logger.warn(
                "Peer %s made push connection with no 'sgAssocs' key in JSON",
                peer.servername)
            raise MatrixRestError(400, 'M_BAD_JSON',
                                  'No "sgAssocs" key in JSON')

        failedIds = []

        globalAssocsStore = GlobalAssociationStore(self.sydent)

        # Ensure items are pulled out of the dictionary in order of origin_id.
        sg_assocs = inJson.get('sgAssocs', {})
        sg_assocs = sorted(sg_assocs.items(), key=lambda k: int(k[0]))

        for originId, sgAssoc in sg_assocs:
            try:
                peer.verifySignedAssociation(sgAssoc)
                logger.debug(
                    "Signed association from %s with origin ID %s verified",
                    peer.servername, originId)

                # Don't bother adding if one has already failed: we add all of them or none so
                # we're only going to roll back the transaction anyway (but we continue to try
                # & verify the rest so we can give a complete list of the ones that don't
                # verify)
                if len(failedIds) > 0:
                    continue

                assocObj = threePidAssocFromDict(sgAssoc)

                if assocObj.mxid is not None:
                    # Calculate the lookup hash with our own pepper for this association
                    str_to_hash = u' '.join([
                        assocObj.address, assocObj.medium,
                        self.hashing_store.get_lookup_pepper()
                    ], )
                    assocObj.lookup_hash = sha256_and_url_safe_base64(
                        str_to_hash)

                    # Add this association
                    globalAssocsStore.addAssociation(assocObj,
                                                     json.dumps(sgAssoc),
                                                     peer.servername,
                                                     originId,
                                                     commit=False)
                else:
                    logger.info(
                        "Incoming deletion: removing associations for %s / %s",
                        assocObj.medium, assocObj.address)
                    globalAssocsStore.removeAssociation(
                        assocObj.medium, assocObj.address)
                logger.info("Stored association origin ID %s from %s",
                            originId, peer.servername)
            except:
                failedIds.append(originId)
                logger.warn(
                    "Failed to verify signed association from %s with origin ID %s",
                    peer.servername, originId)
                twisted.python.log.err()

        if len(failedIds) > 0:
            self.sydent.db.rollback()
            request.setResponseCode(400)
            return {
                'errcode': 'M_VERIFICATION_FAILED',
                'error': 'Verification failed for one or more associations',
                'failed_ids': failedIds
            }
        else:
            self.sydent.db.commit()
            return {'success': True}
Example #10
0
    def render_POST(self, request: Request) -> JsonDict:
        # Cast safety: This request has an ISSLTransport because this servlet
        # is a resource under the ReplicationHttpsServer and nowhere else.
        request.transport = cast(ISSLTransport, request.transport)
        peerCert = cast(X509, request.transport.getPeerCertificate())
        peerCertCn = peerCert.get_subject().commonName

        peerStore = PeerStore(self.sydent)

        peer = peerStore.getPeerByName(peerCertCn)

        if not peer:
            logger.warning(
                "Got connection from %s but no peer found by that name",
                peerCertCn)
            raise MatrixRestError(403, "M_UNKNOWN_PEER",
                                  "This peer is not known to this server")

        logger.info("Push connection made from peer %s", peer.servername)

        if (not request.requestHeaders.hasHeader("Content-Type")
                # Type safety: the hasHeader call returned True, so getRawHeaders()
                # returns a nonempty list.
                or request.requestHeaders.getRawHeaders("Content-Type")[
                    0]  # type: ignore[index]
                != "application/json"):
            logger.warning(
                "Peer %s made push connection with non-JSON content (type: %s)",
                peer.servername,
                # Type safety: the hasHeader call returned True, so getRawHeaders()
                # returns a nonempty list.
                request.requestHeaders.getRawHeaders("Content-Type")
                [0],  # type: ignore[index]
            )
            raise MatrixRestError(400, "M_NOT_JSON",
                                  "This endpoint expects JSON")

        try:
            # json.loads doesn't allow bytes in Python 3.5
            inJson = json_decoder.decode(
                request.content.read().decode("UTF-8"))
        except ValueError:
            logger.warning("Peer %s made push connection with malformed JSON",
                           peer.servername)
            raise MatrixRestError(400, "M_BAD_JSON", "Malformed JSON")

        if "sgAssocs" not in inJson:
            logger.warning(
                "Peer %s made push connection with no 'sgAssocs' key in JSON",
                peer.servername,
            )
            raise MatrixRestError(400, "M_BAD_JSON",
                                  'No "sgAssocs" key in JSON')

        failedIds: List[int] = []

        globalAssocsStore = GlobalAssociationStore(self.sydent)

        # Ensure items are pulled out of the dictionary in order of origin_id.
        sg_assocs_raw: SignedAssociations = inJson.get("sgAssocs", {})
        sg_assocs = sorted(sg_assocs_raw.items(), key=lambda k: int(k[0]))

        for originId, sgAssoc in sg_assocs:
            try:
                peer.verifySignedAssociation(sgAssoc)
                logger.debug(
                    "Signed association from %s with origin ID %s verified",
                    peer.servername,
                    originId,
                )

                # Don't bother adding if one has already failed: we add all of them or none so
                # we're only going to roll back the transaction anyway (but we continue to try
                # & verify the rest so we can give a complete list of the ones that don't
                # verify)
                if len(failedIds) > 0:
                    continue

                assocObj = threePidAssocFromDict(sgAssoc)

                # ensure we are casefolding email addresses before hashing/storing
                assocObj.address = normalise_address(assocObj.address,
                                                     assocObj.medium)

                if assocObj.mxid is not None:
                    # Calculate the lookup hash with our own pepper for this association
                    pepper = self.hashing_store.get_lookup_pepper()
                    assert pepper is not None
                    str_to_hash = " ".join(
                        [assocObj.address, assocObj.medium, pepper], )
                    assocObj.lookup_hash = sha256_and_url_safe_base64(
                        str_to_hash)

                    # Add this association
                    globalAssocsStore.addAssociation(
                        assocObj,
                        json.dumps(sgAssoc),
                        peer.servername,
                        originId,
                        commit=False,
                    )
                else:
                    logger.info(
                        "Incoming deletion: removing associations for %s / %s",
                        assocObj.medium,
                        assocObj.address,
                    )
                    globalAssocsStore.removeAssociation(
                        assocObj.medium, assocObj.address)
                logger.info("Stored association origin ID %s from %s",
                            originId, peer.servername)
            except Exception:
                failedIds.append(originId)
                logger.warning(
                    "Failed to verify signed association from %s with origin ID %s",
                    peer.servername,
                    originId,
                )
                twisted.python.log.err()

        if len(failedIds) > 0:
            self.sydent.db.rollback()
            request.setResponseCode(400)
            return {
                "errcode": "M_VERIFICATION_FAILED",
                "error": "Verification failed for one or more associations",
                "failed_ids": failedIds,
            }
        else:
            self.sydent.db.commit()
            return {"success": True}
Example #11
0
    def render_POST(self, request):
        peerCert = request.transport.getPeerCertificate()
        peerCertCn = peerCert.get_subject().commonName

        peerStore = PeerStore(self.sydent)

        peer = peerStore.getPeerByName(peerCertCn)

        if not peer:
            logger.warn("Got connection from %s but no peer found by that name", peerCertCn)
            request.setResponseCode(403)
            return {'errcode': 'M_UNKNOWN_PEER', 'error': 'This peer is not known to this server'}

        logger.info("Push connection made from peer %s", peer.servername)

        if not request.requestHeaders.hasHeader('Content-Type') or \
                request.requestHeaders.getRawHeaders('Content-Type')[0] != 'application/json':
            logger.warn("Peer %s made push connection with non-JSON content (type: %s)",
                        peer.servername, request.requestHeaders.getRawHeaders('Content-Type')[0])
            return {'errcode': 'M_NOT_JSON', 'error': 'This endpoint expects JSON'}

        try:
            inJson = json.load(request.content)
        except ValueError:
            logger.warn("Peer %s made push connection with malformed JSON", peer.servername)
            return {'errcode': 'M_BAD_JSON', 'error': 'Malformed JSON'}

        if 'sgAssocs' not in inJson:
            logger.warn("Peer %s made push connection with no 'sgAssocs' key in JSON", peer.servername)
            return {'errcode': 'M_BAD_JSON', 'error': 'No "sgAssocs" key in JSON'}

        failedIds = []

        globalAssocsStore = GlobalAssociationStore(self.sydent)

        for originId,sgAssoc in inJson['sgAssocs'].items():
            try:
                peer.verifySignedAssociation(sgAssoc)
                logger.debug("Signed association from %s with origin ID %s verified", peer.servername, originId)

                # Don't bother adding if one has already failed: we add all of them or none so we're only going to
                # roll back the transaction anyway (but we continue to try & verify the rest so we can give a
                # complete list of the ones that don't verify)
                if len(failedIds) > 0:
                    continue

                assocObj = threePidAssocFromDict(sgAssoc)

                if assocObj.mxid is not None:
                    globalAssocsStore.addAssociation(assocObj, json.dumps(sgAssoc), peer.servername, originId, commit=False)
                else:
                    logger.info("Incoming deletion: removing associations for %s / %s", assocObj.medium, assocObj.address)
                    globalAssocsStore.removeAssociation(assocObj.medium, assocObj.address)
                logger.info("Stored association origin ID %s from %s", originId, peer.servername)
            except:
                failedIds.append(originId)
                logger.warn("Failed to verify signed association from %s with origin ID %s",
                            peer.servername, originId)
                twisted.python.log.err()

        if len(failedIds) > 0:
            self.sydent.db.rollback()
            request.setResponseCode(400)
            return {'errcode': 'M_VERIFICATION_FAILED', 'error': 'Verification failed for one or more associations',
                    'failed_ids':failedIds}
        else:
            self.sydent.db.commit()
            return {'success':True}