示例#1
0
 def on_message_download_success(self, uuid: str) -> None:
     """
     Called when a message has downloaded.
     """
     self.session.commit()  # Needed to flush stale data.
     message = storage.get_message(self.session, uuid)
     self.message_ready.emit(message.source.uuid, message.uuid, message.content)
示例#2
0
def test_get_message(mocker, session):
    source = factory.Source()
    message = factory.Message(source=source)
    session.add(source)
    session.add(message)

    result = get_message(session, message.uuid)

    assert result == message
示例#3
0
    def on_message_download_failure(self, exception: DownloadException) -> None:
        """
        Called when a message fails to download.
        """
        logger.info('Failed to download message: {}'.format(exception))

        if isinstance(exception, DownloadChecksumMismatchException):
            # Keep resubmitting the job if the download is corrupted.
            logger.warning('Failure due to checksum mismatch, retrying {}'.format(exception.uuid))
            self._submit_download_job(exception.object_type, exception.uuid)

        self.session.commit()
        try:
            message = storage.get_message(self.session, exception.uuid)
            self.message_download_failed.emit(message.source.uuid, message.uuid, str(message))
        except Exception as e:
            logger.error(f"Could not emit message_download_failed: {e}")
示例#4
0
def test_sync_delete_race(homedir, mocker, session_maker, session):
    """
    Test a race between sync and source deletion (#797).

    The original failure scenario:
      0. New source submits message 1.
      1. Sync occurs in client. Journalist sees message 1.
      2. Source submits message 2.
      3. Journalist simultaneously deletes the source while the sync
         begins. Deletion completes as it occurs independently of the
         sync, but by this time the sync has collected the list of new
         messages, which includes message 2.
      4. Source is gone, yet the logic in the sync will attempt to add
         message 2 which corresponds to a source that is deleted.
    """

    source = factory.RemoteSource()
    message1 = make_remote_message(source.uuid)

    sources = [source]
    submissions = [message1]
    replies = []

    update_local_storage(session, sources, submissions, replies, homedir)

    assert source_exists(session, source.uuid)
    get_message(session, message1.uuid)

    message2 = make_remote_message(source.uuid, file_counter=2)
    submissions = [message1, message2]

    class Deleter(QThread):
        def __init__(self, source_uuid):
            super().__init__()
            self.source_uuid = source_uuid

        def run(self):
            session = db.make_session_maker(homedir)()
            session.begin(subtransactions=True)
            delete_local_source_by_uuid(session, self.source_uuid, homedir)
            session.commit()
            self.exit()

    deleter = Deleter(source.uuid)

    def delayed_update_messages(remote_submissions, local_submissions, session,
                                data_dir):
        assert source_exists(session, source.uuid)
        deleter.start()
        time.sleep(1)

        # This next assert should fail if transactions are working, as
        # the source should still be visible in this session -- it's
        # only been deleted in the Deleter's session. If transactions
        # are *not* working, the deletion will be visible here.
        assert source_exists(session, source.uuid) is False
        update_messages(remote_submissions, local_submissions, session,
                        data_dir)

    mocker.patch("securedrop_client.storage.update_messages",
                 delayed_update_messages)

    # simulate update_local_storage being called as part of the sync operation
    update_local_storage(session, sources, [message1, message2], [], homedir)

    assert source_exists(session, source.uuid) is False
    with pytest.raises(NoResultFound):
        get_message(session, message1.uuid)

    assert source_exists(session, source.uuid) is False
    with pytest.raises(NoResultFound):
        get_message(session, message1.uuid)
        get_message(session, message2.uuid)
示例#5
0
 def on_message_download_success(self, uuid: str) -> None:
     """
     Called when a message has downloaded.
     """
     message = storage.get_message(self.session, uuid)
     self.message_ready.emit(message.uuid, message.content)