Exemple #1
0
def test_SourceList_maintains_selection(mocker):
    """
    Maintains the selected item if present in new list
    """
    sl = SourceList(None)
    sources = [factory.Source(), factory.Source()]
    sl.setup(mocker.MagicMock())
    sl.update(sources)

    sl.setCurrentItem(sl.itemAt(0, 0))
    sl.update(sources)

    assert sl.currentItem()
    assert sl.itemWidget(sl.currentItem()).source.id == sources[0].id
Exemple #2
0
def test_star_if_unstar(homedir, mocker, session, session_maker):
    '''
    Check if we call add_star method if a source is not stared.
    '''
    source = factory.Source()
    session.add(source)
    session.commit()

    api_client = mocker.MagicMock()

    api_client.add_star = mocker.MagicMock()

    mock_sdk_source = mocker.Mock()
    mock_source_init = mocker.patch('securedrop_client.logic.sdclientapi.Source',
                                    return_value=mock_sdk_source)

    job = UpdateStarJob(
        source.uuid,
        source.is_starred
    )

    job.call_api(api_client, session)

    # ensure we call add_star with right uuid for source
    mock_source_init.assert_called_once_with(uuid=source.uuid)
    api_client.add_star.assert_called_once_with(mock_sdk_source)
Exemple #3
0
def test_send_reply_failure_when_repr_is_none(homedir, mocker, session,
                                              session_maker):
    '''
    Check that the SendReplyJob api call results in a SendReplyJobError and nothing else, e.g.
    no TypeError, when an api call results in an exception that returns None for __repr__
    (regression test).
    '''
    class MockException(Exception):
        def __repr__(self):
            return None

    source = factory.Source(uuid='mock_reply_uuid')
    session.add(source)
    session.commit()
    api_client = mocker.MagicMock()
    mocker.patch.object(api_client,
                        'reply_source',
                        side_effect=MockException('mock'))
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    encrypt_fn = mocker.patch.object(gpg, 'encrypt_to_source')
    job = SendReplyJob(source.uuid, 'mock_reply_uuid', 'mock_message', gpg)

    with pytest.raises(
            SendReplyJobError,
            match=
            r'Failed to send reply for source mock_reply_uuid due to Exception: mock'
    ):
        job.call_api(api_client, session)

    encrypt_fn.assert_called_once_with(source.uuid, 'mock_message')
    replies = session.query(db.Reply).filter_by(uuid='mock_reply_uuid').all()
    assert len(replies) == 0
def test_send_reply_failure_timeout_error(
    homedir, mocker, session, session_maker, reply_status_codes, exception
):
    """
    Check that if the SendReplyJob api call fails because of a RequestTimeoutError or
    ServerConnectionError that a SendReplyJobTimeoutError is raised.
    """
    source = factory.Source()
    session.add(source)
    draft_reply = factory.DraftReply(uuid="mock_reply_uuid")
    session.add(draft_reply)
    session.commit()
    api_client = mocker.MagicMock()
    mocker.patch.object(api_client, "reply_source", side_effect=exception)
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    encrypt_fn = mocker.patch.object(gpg, "encrypt_to_source")
    job = SendReplyJob(source.uuid, "mock_reply_uuid", "mock_message", gpg)

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

    encrypt_fn.assert_called_once_with(source.uuid, "mock_message")
    replies = session.query(db.Reply).filter_by(uuid="mock_reply_uuid").all()
    assert len(replies) == 0

    # Ensure that the draft reply is still in the db
    drafts = session.query(db.DraftReply).filter_by(uuid="mock_reply_uuid").all()
    assert len(drafts) == 1
Exemple #5
0
def test_string_representation_of_message():
    source = factory.Source()
    msg = Message(source=source, uuid="test", size=123, filename="1-test.docx",
                  download_url='http://test/test')
    msg.__str__()
    msg.content = "hello"
    msg.__str__()
Exemple #6
0
def test_string_representation_of_file():
    source = factory.Source()
    file_ = File(source=source, uuid="test", size=123, filename="1-test.docx",
                 download_url='http://test/test')
    file_.__str__()
    file_.is_downloaded = True
    file_.__str__()
Exemple #7
0
def test_source_server_collection():
    # Create some test submissions and replies
    source = factory.Source()
    file_ = File(source=source, uuid="test", size=123, filename="2-test.doc.gpg",
                 download_url='http://test/test')
    message = Message(source=source, uuid="test", size=123, filename="3-test.doc.gpg",
                      download_url='http://test/test')
    user = User(username='******')
    reply = Reply(source=source, journalist=user, filename="1-reply.gpg",
                  size=1234, uuid='test')
    draft_reply = DraftReply(source=source, journalist=user,
                             uuid='test',
                             timestamp=datetime.datetime(2002, 6, 6, 6, 0))
    source.files = [file_]
    source.messages = [message]
    source.replies = [reply]
    source.draftreplies = [draft_reply]

    # Now these items should be in the source collection in the proper order
    assert source.server_collection[0] == reply
    assert source.server_collection[1] == file_
    assert source.server_collection[2] == message

    # Drafts do not appear in the server_collection, they are local only.
    assert draft_reply not in source.server_collection
Exemple #8
0
def test_source_collection_ordering_with_multiple_draft_replies():
    # Create some test submissions, replies, and draft replies.
    source = factory.Source()
    file_1 = File(source=source, uuid="test", size=123, filename="1-test.doc.gpg",
                  download_url='http://test/test')
    message_2 = Message(source=source, uuid="test", size=123, filename="2-test.doc.gpg",
                        download_url='http://test/test')
    user = User(username='******')
    reply_3 = Reply(source=source, journalist=user, filename="3-reply.gpg",
                    size=1234, uuid='test')
    draft_reply_4 = DraftReply(uuid='4', source=source, journalist=user, file_counter=3,
                               timestamp=datetime.datetime(2000, 6, 6, 6, 0))
    draft_reply_5 = DraftReply(uuid='5', source=source, journalist=user, file_counter=3,
                               timestamp=datetime.datetime(2001, 6, 6, 6, 0))
    reply_6 = Reply(source=source, journalist=user, filename="4-reply.gpg",
                    size=1234, uuid='test2')
    draft_reply_7 = DraftReply(uuid='6', source=source, journalist=user, file_counter=4,
                               timestamp=datetime.datetime(2002, 6, 6, 6, 0))
    source.files = [file_1]
    source.messages = [message_2]
    source.replies = [reply_3, reply_6]
    source.draftreplies = [draft_reply_4, draft_reply_5, draft_reply_7]

    # Now these items should be in the source collection in the proper order
    assert source.collection[0] == file_1
    assert source.collection[1] == message_2
    assert source.collection[2] == reply_3
    assert source.collection[3] == draft_reply_4
    assert source.collection[4] == draft_reply_5
    assert source.collection[5] == reply_6
    assert source.collection[6] == draft_reply_7
Exemple #9
0
def test_string_representation_of_draft_reply():
    user = User(username='******')
    source = factory.Source()
    draft_reply = DraftReply(source=source, journalist=user, uuid='test')
    draft_reply.__str__()
    draft_reply.content = "hello"
    draft_reply.__str__()
def test_unstar_if_star(homedir, mocker, session, session_maker):
    """
    Check if we call remove_star method if a source is stared.
    """
    source = factory.Source()
    source.is_starred = True
    session.add(source)
    session.commit()

    api_client = mocker.MagicMock()

    api_client.remove_star = mocker.MagicMock()

    mock_sdk_source = mocker.Mock()
    mock_source_init = mocker.patch(
        "securedrop_client.logic.sdclientapi.Source", return_value=mock_sdk_source
    )

    job = UpdateStarJob(source.uuid, source.is_starred)

    job.call_api(api_client, session)

    # ensure we call remove start wtih right source uuid
    mock_source_init.assert_called_once_with(uuid=source.uuid)
    api_client.remove_star.assert_called_once_with(mock_sdk_source)
Exemple #11
0
def test_source_collection():
    # Create some test submissions and replies
    source = factory.Source()
    file_ = File(
        source=source,
        uuid="test",
        size=123,
        filename="2-test.doc.gpg",
        download_url="http://test/test",
    )
    message = Message(
        source=source,
        uuid="test",
        size=123,
        filename="3-test.doc.gpg",
        download_url="http://test/test",
    )
    user = User(username="******")
    reply = Reply(source=source,
                  journalist=user,
                  filename="1-reply.gpg",
                  size=1234,
                  uuid="test")
    source.files = [file_]
    source.messages = [message]
    source.replies = [reply]

    # Now these items should be in the source collection in the proper order
    assert source.collection[0] == reply
    assert source.collection[1] == file_
    assert source.collection[2] == message
def test_find_new_replies(mocker, session):
    source = factory.Source()
    reply_not_downloaded = factory.Reply(source=source,
                                         is_downloaded=False,
                                         is_decrypted=None,
                                         content=None)
    reply_decrypt_not_attempted = factory.Reply(source=source,
                                                is_downloaded=True,
                                                is_decrypted=None,
                                                content=None)
    reply_decrypt_failed = factory.Reply(source=source,
                                         is_downloaded=True,
                                         is_decrypted=False,
                                         content=None)
    reply_decrypt_success = factory.Reply(source=source,
                                          is_downloaded=True,
                                          is_decrypted=True,
                                          content="teehee")
    session.add(source)
    session.add(reply_decrypt_not_attempted)
    session.add(reply_not_downloaded)
    session.add(reply_decrypt_failed)
    session.add(reply_decrypt_success)
    session.commit()

    replies = find_new_replies(session)
    assert len(replies) == 3

    for reply in replies:
        assert reply.is_downloaded is False or reply.is_decrypted is not True
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
def test_send_reply_failure_unknown_error(
    homedir, mocker, session, session_maker, reply_status_codes
):
    """
    Check that if the SendReplyJob api call fails when sending a message that SendReplyJobError
    is raised and the reply is not added to the local database.
    """
    source = factory.Source()
    session.add(source)
    draft_reply = factory.DraftReply(uuid="mock_reply_uuid")
    session.add(draft_reply)
    session.commit()
    api_client = mocker.MagicMock()
    mocker.patch.object(api_client, "reply_source", side_effect=Exception)
    gpg = GpgHelper(homedir, session_maker, is_qubes=False)
    encrypt_fn = mocker.patch.object(gpg, "encrypt_to_source")
    job = SendReplyJob(source.uuid, "mock_reply_uuid", "mock_message", gpg)

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

    encrypt_fn.assert_called_once_with(source.uuid, "mock_message")
    replies = session.query(db.Reply).filter_by(uuid="mock_reply_uuid").all()
    assert len(replies) == 0

    # Ensure that the draft reply is still in the db
    drafts = session.query(db.DraftReply).filter_by(uuid="mock_reply_uuid").all()
    assert len(drafts) == 1
def test_update_replies_missing_source(homedir, mocker, session):
    """
    Verify that a reply to an invalid source is handled.
    """
    data_dir = os.path.join(homedir, "data")

    journalist = factory.User(id=1)
    session.add(journalist)

    source = factory.Source()
    session.add(source)

    # Some remote reply objects from the API, one of which will exist in the
    # local database, the other will NOT exist in the local database
    # (this will be added to the database)
    remote_reply = make_remote_reply("nonexistent-source", journalist.uuid)
    remote_replies = [remote_reply]
    local_replies = []

    error_logger = mocker.patch("securedrop_client.storage.logger.error")

    update_replies(remote_replies, local_replies, session, data_dir)

    error_logger.assert_called_once_with(
        f"No source found for reply {remote_reply.uuid}")
def test_delete_single_submission_or_reply_single_file_no_folder(
        homedir, mocker):
    """
    This test checks that calling the delete_single_submission_or_reply
    method deletes the file even if its not in a per-document folder.
    """

    source = factory.Source(journalist_designation="dissolved-steak")
    file_server_filename = "1-dissolved-steak-msg.gpg"
    test_obj = db.File(
        source=source,
        uuid="test",
        size=123,
        filename=file_server_filename,
        download_url="http://test/test",
    )
    original_location = test_obj.location
    test_obj.location = mocker.MagicMock(side_effect=[
        os.path.join(homedir, file_server_filename),
        original_location(homedir)
    ])
    add_test_file_to_temp_dir(homedir, file_server_filename)

    delete_single_submission_or_reply_on_disk(test_obj, homedir)

    with pytest.raises(FileNotFoundError):
        open(os.path.join(homedir, file_server_filename), "r")
def test_delete_single_submission_or_reply_single_file(homedir, mocker):
    """
    This test checks that calling the delete_single_submission_or_reply
    method deletes the file as well as the folder it's inside.
    """

    source = factory.Source(journalist_designation="dissolved-steak")
    file_server_filename = "1-dissolved-steak-msg.gpg"
    test_obj = db.File(
        source=source,
        uuid="test",
        size=123,
        filename=file_server_filename,
        download_url="http://test/test",
    )
    source_directory = os.path.dirname(test_obj.location(homedir))
    add_test_file_to_temp_dir(source_directory, file_server_filename)

    delete_single_submission_or_reply_on_disk(test_obj, homedir)

    # Ensure both file and its containing folder are gone.
    with pytest.raises(FileNotFoundError):
        open(os.path.join(source_directory, file_server_filename), "r")

    with pytest.raises(FileNotFoundError):
        open(source_directory, "r")
def test_string_representation_of_submission():
    source = factory.Source()
    submission = Submission(source=source,
                            uuid="test",
                            size=123,
                            filename="test.docx",
                            download_url='http://test/test')
    submission.__repr__()
def test_mark_reply_as_downloaded(mocker):
    session = mocker.MagicMock()
    reply = factory.Reply(source=factory.Source(), is_downloaded=False)
    session.query().filter_by().one.return_value = reply
    mark_as_downloaded(type(reply), "mock_uuid", session)
    assert reply.is_downloaded is True
    session.add.assert_called_once_with(reply)
    session.commit.assert_called_once_with()
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()