示例#1
0
def test_find_new_messages(mocker, session):
    source = factory.Source()
    message_not_downloaded = factory.Message(source=source,
                                             is_downloaded=False,
                                             is_decrypted=None,
                                             content=None)
    message_decrypt_not_attempted = factory.Message(source=source,
                                                    is_downloaded=True,
                                                    is_decrypted=None,
                                                    content=None)
    message_decrypt_failed = factory.Message(source=source,
                                             is_downloaded=True,
                                             is_decrypted=False,
                                             content=None)
    message_decrypt_success = factory.Message(source=source,
                                              is_downloaded=True,
                                              is_decrypted=True,
                                              content="teehee")
    session.add(source)
    session.add(message_decrypt_not_attempted)
    session.add(message_not_downloaded)
    session.add(message_decrypt_failed)
    session.add(message_decrypt_success)
    session.commit()

    messages = find_new_messages(session)
    assert len(messages) == 3

    for message in messages:
        assert message.is_downloaded is False or message.is_decrypted is not True
示例#2
0
def test_MessageDownloadJob_no_download_or_decrypt(mocker, homedir, session,
                                                   session_maker):
    """
    Test that an already-downloaded message successfully decrypts. Use the `homedir` fixture to get
    a GPG keyring.
    """
    message_is_decrypted_false = factory.Message(source=factory.Source(),
                                                 is_downloaded=True,
                                                 is_decrypted=False,
                                                 content=None)
    message_is_decrypted_none = factory.Message(source=factory.Source(),
                                                is_downloaded=True,
                                                is_decrypted=None,
                                                content=None)
    session.add(message_is_decrypted_false)
    session.add(message_is_decrypted_none)
    session.commit()
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    job_1 = MessageDownloadJob(message_is_decrypted_false.uuid, homedir, gpg)
    job_2 = MessageDownloadJob(message_is_decrypted_none.uuid, homedir, gpg)
    mocker.patch.object(job_1.gpg, 'decrypt_submission_or_reply')
    mocker.patch.object(job_2.gpg, 'decrypt_submission_or_reply')
    api_client = mocker.MagicMock()
    path = os.path.join(homedir, 'data')
    api_client.download_submission = mocker.MagicMock(return_value=('', path))

    job_1.call_api(api_client, session)
    job_2.call_api(api_client, session)

    assert message_is_decrypted_false.content is not None
    assert message_is_decrypted_false.is_downloaded is True
    assert message_is_decrypted_false.is_decrypted is True
    assert message_is_decrypted_none.content is not None
    assert message_is_decrypted_none.is_downloaded is True
    assert message_is_decrypted_none.is_decrypted is True
示例#3
0
def main_window_no_key(mocker, homedir):
    # Setup
    app = QApplication([])
    gui = Window()
    app.setActiveWindow(gui)
    gui.show()
    controller = Controller("http://localhost", gui, mocker.MagicMock(), homedir, proxy=False)
    controller.qubes = False
    gui.setup(controller)

    # Create a source widget
    source_list = gui.main_view.source_list
    source = factory.Source(public_key=None)
    source_list.update([source])

    # Create a file widget, message widget, and reply widget
    mocker.patch("securedrop_client.gui.widgets.humanize_filesize", return_value="100")
    mocker.patch(
        "securedrop_client.gui.SecureQLabel.get_elided_text", return_value="1-yellow-doc.gz.gpg"
    )
    source.collection.append(
        [
            factory.File(source=source, filename="1-yellow-doc.gz.gpg"),
            factory.Message(source=source, filename="2-yellow-msg.gpg"),
            factory.Reply(source=source, filename="3-yellow-reply.gpg"),
        ]
    )
    source_list.setCurrentItem(source_list.item(0))
    gui.main_view.on_source_changed()

    yield gui

    # Teardown
    gui.login_dialog.close()
    app.exit()
示例#4
0
def test_MessageSync_exception(homedir, config, mocker, source):
    """
    Mostly here for code coverage- makes sure that if an exception is
    raised in the download thread, the code which catches it is actually
    run.
    Using the `config` fixture to ensure the config is written to disk.
    """
    message = factory.Message(source=source['source'])
    api = mocker.MagicMock()
    is_qubes = False

    # mock to return the submission we want
    mocker.patch('securedrop_client.storage.find_new_messages',
                 return_value=[message])
    mocker.patch('securedrop_client.storage.find_new_files', return_value=[])
    # mock to prevent GpgHelper from raising errors on init
    mocker.patch('securedrop_client.crypto.safe_mkdir')

    ms = MessageSync(api, str(homedir), is_qubes)
    mocker.patch.object(ms.gpg,
                        'decrypt_submission_or_reply',
                        side_effect=CryptoError)

    # check that it runs without raising exceptions
    ms.run(False)
示例#5
0
def test_MessageDownloadJob_with_crypto_error(mocker, homedir, session,
                                              session_maker):
    """
    Test when a message successfully downloads, but does not successfully decrypt. Use the `homedir`
    fixture to get a GPG keyring.
    """
    message = factory.Message(source=factory.Source(),
                              is_downloaded=False,
                              is_decrypted=None,
                              content=None)
    session.add(message)
    session.commit()
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    job = MessageDownloadJob(message.uuid, homedir, gpg)
    mocker.patch.object(job.gpg,
                        'decrypt_submission_or_reply',
                        side_effect=CryptoError)
    api_client = mocker.MagicMock()
    api_client.default_request_timeout = mocker.MagicMock()
    path = os.path.join(homedir, 'data')
    api_client.download_submission = mocker.MagicMock(return_value=('', path))

    with pytest.raises(DownloadDecryptionException):
        job.call_api(api_client, session)

    assert message.content is None
    assert message.is_downloaded is True
    assert message.is_decrypted is False
示例#6
0
def test_MessageDownloadJob_happiest_path(mocker, homedir, session,
                                          session_maker):
    """
    Test when a message successfully downloads and decrypts. Use the `homedir` fixture to get a GPG
    keyring.
    """
    message = factory.Message(source=factory.Source(),
                              is_downloaded=False,
                              is_decrypted=None,
                              content=None)
    session.add(message)
    session.commit()
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    job = MessageDownloadJob(message.uuid, homedir, gpg)
    mocker.patch.object(job.gpg, 'decrypt_submission_or_reply')
    api_client = mocker.MagicMock()
    api_client.default_request_timeout = mocker.MagicMock()
    data_dir = os.path.join(homedir, 'data')
    api_client.download_submission = mocker.MagicMock(return_value=('',
                                                                    data_dir))

    job.call_api(api_client, session)

    assert message.content is not None
    assert message.is_downloaded is True
    assert message.is_decrypted is True
示例#7
0
def test_MessageDownloadJob_with_base_error(mocker, homedir, session,
                                            session_maker):
    """
    Test when a message does not successfully download.
    """
    message = factory.Message(source=factory.Source(),
                              is_downloaded=False,
                              is_decrypted=None,
                              content=None)
    session.add(message)
    session.commit()
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    job = MessageDownloadJob(message.uuid, homedir, gpg)
    api_client = mocker.MagicMock()
    api_client.default_request_timeout = mocker.MagicMock()
    mocker.patch.object(api_client,
                        'download_submission',
                        side_effect=BaseError)
    decrypt_fn = mocker.patch.object(job.gpg, 'decrypt_submission_or_reply')

    with pytest.raises(BaseError):
        job.call_api(api_client, session)

    assert message.content is None
    assert message.is_downloaded is False
    assert message.is_decrypted is None
    decrypt_fn.assert_not_called()
示例#8
0
def test_MessageSync_run_failure(mocker, source):
    message = factory.Message(source=source['source'])

    # mock the fetching of submissions
    mocker.patch('securedrop_client.storage.find_new_messages',
                 return_value=[message])
    mocker.patch('securedrop_client.storage.find_new_files', return_value=[])
    # mock the handling of the downloaded files
    mocker.patch(
        'securedrop_client.message_sync.storage.mark_file_as_downloaded')
    mocker.patch(
        'securedrop_client.message_sync.storage.mark_message_as_downloaded')
    # mock the GpgHelper creation since we don't have directories/keys setup
    mocker.patch('securedrop_client.message_sync.GpgHelper')

    api = mocker.MagicMock()
    home = "/home/user/.sd"
    is_qubes = False

    ms = MessageSync(api, home, is_qubes)
    ms.api.download_submission = mocker.MagicMock(
        return_value=(1234, "/home/user/downloads/foo"))

    # check that it runs without raising exceptions
    ms.run(False)
示例#9
0
def test_mark_message_as_downloaded(mocker):
    session = mocker.MagicMock()
    message = factory.Message(source=factory.Source(), is_downloaded=False)
    session.query().filter_by().one.return_value = message
    mark_as_downloaded(type(message), "mock_uuid", session)
    assert message.is_downloaded is True
    session.add.assert_called_once_with(message)
    session.commit.assert_called_once_with()
示例#10
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
示例#11
0
def test_message_with_download_error(session, download_error_codes):
    m = factory.Message(is_decrypted=False, content=None)
    download_error = (session.query(DownloadError).filter_by(
        name=DownloadErrorCodes.DECRYPTION_ERROR.name).one())
    m.download_error = download_error
    session.commit()

    classname = m.__class__.__name__.lower()
    assert str(m) == f"cannot decrypt {classname}"
示例#12
0
def test_MessageSync_run_success(mocker, session, source):
    """Test when a message successfully downloads and decrypts."""
    message = factory.Message(source=source['source'],
                              is_downloaded=False,
                              is_decrypted=None,
                              content=None)
    session.add(message)
    session.commit()

    expected_content = 'foo'

    def set_object_decryption_status_with_content_side_effect(
            *nargs, **kwargs):
        message.content = expected_content

    # mock the fetching of submissions
    mocker.patch('securedrop_client.storage.find_new_messages',
                 return_value=[message])
    mock_download_status = mocker.patch(
        'securedrop_client.message_sync.storage.mark_message_as_downloaded')
    mock_decryption_status = mocker.patch(
        'securedrop_client.message_sync.storage.set_object_decryption_status_with_content',
        side_effect=set_object_decryption_status_with_content_side_effect)

    # don't create the signal
    mocker.patch('securedrop_client.message_sync.pyqtSignal')

    def mock_decrypt_submission_or_reply(filepath, plaintext_filename, is_doc):
        with open(plaintext_filename, 'w') as f:
            f.write(expected_content)

    # mock the GpgHelper creation since we don't have directories/keys setup
    mock_gpg_helper = mocker.MagicMock(
        decrypt_submission_or_reply=mock_decrypt_submission_or_reply)
    mocker.patch('securedrop_client.message_sync.GpgHelper',
                 return_value=mock_gpg_helper)

    api = mocker.MagicMock(session=session)
    ms = MessageSync(api, 'mock', True)
    ms.session = session  # "patch" it with a real session
    ms.api.download_submission = mocker.MagicMock(
        return_value=(1234, "/home/user/downloads/foo"))

    mock_message_ready = mocker.patch.object(ms, 'message_ready')

    # check that it runs without raising exceptions
    ms.run(False)

    mock_decryption_status.assert_called_once_with(message, api.session, True,
                                                   expected_content)
    mock_download_status.called_once_with(message, api.mock_session)
    mock_message_ready.emit.assert_called_once_with(message.uuid,
                                                    expected_content)
示例#13
0
def test_MessageSync_run_decryption_error(mocker, session, source):
    """Test when a message successfully downloads, but does not successfully decrypt."""
    message = factory.Message(source=source['source'],
                              is_downloaded=False,
                              is_decrypted=None,
                              content=None)
    session.add(message)
    session.commit()

    # mock the fetching of submissions
    mocker.patch('securedrop_client.storage.find_new_messages',
                 return_value=[message])
    mock_download_status = mocker.patch(
        'securedrop_client.message_sync.storage.mark_message_as_downloaded')
    mock_decryption_status = mocker.patch(
        'securedrop_client.message_sync.storage.set_object_decryption_status_with_content'
    )

    # don't create the signal
    mocker.patch('securedrop_client.message_sync.pyqtSignal')
    # mock the GpgHelper creation since we don't have directories/keys setup
    mocker.patch('securedrop_client.message_sync.GpgHelper')

    api = mocker.MagicMock(session=session)

    ms = MessageSync(api, 'mock', True)
    ms.session = session  # "patch" it with a real session
    mocker.patch.object(ms.gpg,
                        'decrypt_submission_or_reply',
                        side_effect=CryptoError)

    ms.api.download_submission = mocker.MagicMock(
        return_value=(1234, "/home/user/downloads/foo"))

    mock_message_ready = mocker.patch.object(ms, 'message_ready')

    # check that it runs without raising exceptions
    ms.run(False)

    mock_download_status.assert_called_once_with(message.uuid, session)
    mock_decryption_status.assert_called_once_with(message, api.session, False)
    mock_message_ready.emit.assert_called_once_with(
        message.uuid, '<Message not yet available>')
示例#14
0
def test_MessageDownloadJob_message_already_downloaded(mocker, homedir,
                                                       session, session_maker):
    """
    Test that call_api just decrypts and returns uuid if already downloaded.
    """
    message = factory.Message(source=factory.Source(),
                              is_downloaded=True,
                              is_decrypted=None)
    session.add(message)
    session.commit()
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    job = MessageDownloadJob(message.uuid, homedir, gpg)
    mocker.patch.object(job.gpg, 'decrypt_submission_or_reply')
    api_client = mocker.MagicMock()
    download_fn = mocker.patch.object(api_client, 'download_submission')

    return_uuid = job.call_api(api_client, session)

    assert message.uuid == return_uuid
    assert message.is_decrypted is True
    download_fn.assert_not_called()
示例#15
0
def test_set_object_decryption_status_with_content_with_content(
        session, source):
    '''
    It should be possible to set the decryption status of an object in the database to `True`.
    Additionally, if `content` is passed in, the `content` column of the DB should take that
    value. This is to ensure that we have a way to decrypt something without violating the
    condition: if is_decrypted then content is not none.
    '''
    message = factory.Message(source=source['source'],
                              is_downloaded=True,
                              is_decrypted=None,
                              content=None)
    session.add(message)
    session.commit()

    content = 'test'
    set_object_decryption_status_with_content(message, session, True, content)

    # requery to ensure new object
    message = session.query(db.Message).get(message.id)
    assert message.is_decrypted is True
    assert message.content == content
示例#16
0
def test_MessageDownloadJob_message_already_decrypted(mocker, homedir, session,
                                                      session_maker,
                                                      download_error_codes):
    """
    Test that call_api just returns uuid if already decrypted.
    """
    message = factory.Message(source=factory.Source(),
                              is_downloaded=True,
                              is_decrypted=True)
    session.add(message)
    session.commit()
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    job = MessageDownloadJob(message.uuid, homedir, gpg)
    decrypt_fn = mocker.patch.object(job.gpg, "decrypt_submission_or_reply")
    api_client = mocker.MagicMock()
    api_client.default_request_timeout = mocker.MagicMock()
    download_fn = mocker.patch.object(api_client, "download_submission")

    return_uuid = job.call_api(api_client, session)

    assert message.uuid == return_uuid
    decrypt_fn.assert_not_called()
    download_fn.assert_not_called()
示例#17
0
def test_set_message_decryption_status_with_content_with_content(
        session, source):
    """
    It should be possible to set the decryption status of an object in the database to `True`.
    Additionally, if `content` is passed in, the `content` column of the DB should take that
    value. This is to ensure that we have a way to decrypt something without violating the
    condition: if is_decrypted then content is not none.
    """
    message = factory.Message(source=source["source"],
                              is_downloaded=True,
                              is_decrypted=None,
                              content=None)
    session.add(message)
    session.commit()

    set_message_or_reply_content(type(message), message.uuid, "mock_content",
                                 session)
    mark_as_decrypted(type(message), message.uuid, session)

    # requery to ensure new object
    message = session.query(db.Message).get(message.id)
    assert message.is_decrypted is True
    assert message.content == "mock_content"