Exemplo n.º 1
0
async def test_block_create_and_read(alice_backend_sock, realm):
    await block_create(alice_backend_sock, BLOCK_ID, realm, BLOCK_DATA)

    rep = await block_read(alice_backend_sock, BLOCK_ID)
    assert rep == {"status": "ok", "block": BLOCK_DATA}

    # Test not found as well

    dummy_id = BlockID.from_hex("00000000000000000000000000000002")
    rep = await block_read(alice_backend_sock, dummy_id)
    assert rep == {"status": "not_found"}
Exemplo n.º 2
0
async def test_s3_read(caplog):
    org_id = OrganizationID("org42")
    block_id = BlockID.from_hex("0694a21176354e8295e28a543e5887f9")

    def _assert_log():
        log = caplog.assert_occured_once("[warning  ] Block read error")
        assert f"organization_id={org_id}" in log
        assert f"block_id={block_id}" in log
        assert len(caplog.messages) == 1
        caplog.clear()

    with mock.patch("boto3.client") as client_mock:
        client_mock.return_value = Mock()
        client_mock().head_bucket.return_value = True
        blockstore = S3BlockStoreComponent("europe", "parsec", "john",
                                           "secret")

        # Ok
        response_mock = Mock()
        response_mock.read.return_value = "content"
        client_mock().get_object.return_value = {"Body": response_mock}
        assert await blockstore.read(org_id, block_id) == "content"
        client_mock().get_object.assert_called_once_with(
            Bucket="parsec", Key="org42/0694a211-7635-4e82-95e2-8a543e5887f9")
        client_mock().get_object.reset_mock()
        assert not caplog.messages

        # Not found
        client_mock().get_object.side_effect = S3ClientError(
            error_response={"Error": {
                "Code": "404"
            }}, operation_name="GET")
        with pytest.raises(BlockStoreError):
            assert await blockstore.read(org_id, block_id)
        _assert_log()

        # Connection error
        client_mock().get_object.side_effect = S3EndpointConnectionError(
            endpoint_url="url")
        with pytest.raises(BlockStoreError):
            assert await blockstore.read(org_id, block_id)
        _assert_log()

        # Unknown exception
        client_mock().get_object.side_effect = S3ClientError(
            error_response={"Error": {
                "Code": "401"
            }}, operation_name="GET")
        with pytest.raises(BlockStoreError):
            assert await blockstore.read(org_id, block_id)
        _assert_log()
Exemplo n.º 3
0
async def test_realm_stats_ok(alice_backend_sock, realm):

    # Create new data
    await block_create(alice_backend_sock,
                       realm_id=realm,
                       block_id=BlockID.new(),
                       block=b"1234")
    rep = await realm_stats(alice_backend_sock, realm_id=realm)
    assert rep == {"status": "ok", "blocks_size": 4, "vlobs_size": 0}

    # Create new metadata
    await vlob_create(alice_backend_sock,
                      realm_id=realm,
                      vlob_id=VlobID.new(),
                      blob=b"1234")
    rep = await realm_stats(alice_backend_sock, realm_id=realm)
    assert rep == {"status": "ok", "blocks_size": 4, "vlobs_size": 4}
Exemplo n.º 4
0
async def test_swift_create(caplog):
    org_id = OrganizationID("org42")
    block_id = BlockID.from_hex("0694a21176354e8295e28a543e5887f9")

    def _assert_log():
        log = caplog.assert_occured_once("[warning  ] Block create error")
        assert f"organization_id={org_id}" in log
        assert f"block_id={block_id}" in log
        assert len(caplog.messages) == 1
        caplog.clear()

    with mock.patch("swiftclient.Connection") as connection_mock:
        connection_mock.return_value = Mock()
        connection_mock().head_container.return_value = True
        blockstore = SwiftBlockStoreComponent("http://url", "scille", "parsec",
                                              "john", "secret")

        # Ok
        connection_mock().get_object.side_effect = ClientException(
            http_status=404, msg="")
        await blockstore.create(org_id, block_id, "content")
        connection_mock().put_object.assert_called_with(
            "parsec", "org42/0694a211-7635-4e82-95e2-8a543e5887f9", "content")
        connection_mock().put_object.reset_mock()
        assert not caplog.messages

        # Connection error at PUT
        connection_mock().get_object.side_effect = ClientException(
            msg="Connection error")
        connection_mock().put_object.side_effect = ClientException(
            msg="Connection error")
        with pytest.raises(BlockStoreError):
            await blockstore.create(org_id, block_id, "content")
        _assert_log()

        # Unknown exception at PUT
        connection_mock().put_object.side_effect = ClientException(
            http_status=500, msg="")
        with pytest.raises(BlockStoreError):
            await blockstore.create(org_id, block_id, "content")
        _assert_log()
Exemplo n.º 5
0
async def test_organization_stats_data(alice_backend_sock, realm,
                                       realm_factory, alice, backend):
    stats = await organization_stats(alice_backend_sock)
    assert stats == {
        "status":
        "ok",
        "data_size":
        0,
        "metadata_size":
        ANY,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        4,
    }
    initial_metadata_size = stats["metadata_size"]

    # Create new metadata
    await backend.vlob.create(
        organization_id=alice.organization_id,
        author=alice.device_id,
        realm_id=realm,
        encryption_revision=1,
        vlob_id=VlobID.new(),
        timestamp=pendulum.now(),
        blob=b"1234",
    )
    stats = await organization_stats(alice_backend_sock)
    assert stats == {
        "status":
        "ok",
        "data_size":
        0,
        "metadata_size":
        initial_metadata_size + 4,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        4,
    }

    # Create new data
    await backend.block.create(
        organization_id=alice.organization_id,
        author=alice.device_id,
        block_id=BlockID.new(),
        realm_id=realm,
        block=b"1234",
    )
    stats = await organization_stats(alice_backend_sock)
    assert stats == {
        "status":
        "ok",
        "data_size":
        4,
        "metadata_size":
        initial_metadata_size + 4,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        4,
    }

    # create new workspace
    await realm_factory(backend, alice)
    stats = await organization_stats(alice_backend_sock)
    assert stats == {
        "status":
        "ok",
        "data_size":
        4,
        "metadata_size":
        initial_metadata_size + 4,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        5,
    }
Exemplo n.º 6
0
async def test_block_create_check_access_rights(backend, alice, bob,
                                                bob_backend_sock, realm,
                                                next_timestamp):
    block_id = BlockID.new()

    # User not part of the realm
    rep = await block_create(bob_backend_sock,
                             block_id,
                             realm,
                             BLOCK_DATA,
                             check_rep=False)
    assert rep == {"status": "not_allowed"}

    # User part of the realm with various role
    for role, access_granted in [
        (RealmRole.READER, False),
        (RealmRole.CONTRIBUTOR, True),
        (RealmRole.MANAGER, True),
        (RealmRole.OWNER, True),
    ]:
        await backend.realm.update_roles(
            alice.organization_id,
            RealmGrantedRole(
                certificate=b"<dummy>",
                realm_id=realm,
                user_id=bob.user_id,
                role=role,
                granted_by=alice.device_id,
                granted_on=next_timestamp(),
            ),
        )
        block_id = BlockID.new()
        rep = await block_create(bob_backend_sock,
                                 block_id,
                                 realm,
                                 BLOCK_DATA,
                                 check_rep=False)
        if access_granted:
            assert rep == {"status": "ok"}

        else:
            assert rep == {"status": "not_allowed"}

    # Ensure user that used to be part of the realm have no longer access
    await backend.realm.update_roles(
        alice.organization_id,
        RealmGrantedRole(
            certificate=b"<dummy>",
            realm_id=realm,
            user_id=bob.user_id,
            role=None,
            granted_by=alice.device_id,
            granted_on=next_timestamp(),
        ),
    )
    rep = await block_create(bob_backend_sock,
                             block_id,
                             realm,
                             BLOCK_DATA,
                             check_rep=False)
    assert rep == {"status": "not_allowed"}
Exemplo n.º 7
0
async def block(backend, alice, realm):
    block_id = BlockID.from_hex("0000000000000000000000000000000C")

    await backend.block.create(alice.organization_id, alice.device_id,
                               block_id, realm, BLOCK_DATA)
    return block_id
Exemplo n.º 8
0
    generate_checksum_chunk,
    rebuild_block_from_chunks,
)
from parsec.api.protocol import (
    BlockID,
    VlobID,
    block_create_serializer,
    block_read_serializer,
    packb,
    RealmRole,
)

from tests.common import customize_fixtures
from tests.backend.common import block_create, block_read

BLOCK_ID = BlockID.from_hex("00000000000000000000000000000001")
VLOB_ID = VlobID.from_hex("00000000000000000000000000000002")
BLOCK_DATA = b"Hodi ho !"


@pytest.fixture
async def block(backend, alice, realm):
    block_id = BlockID.from_hex("0000000000000000000000000000000C")

    await backend.block.create(alice.organization_id, alice.device_id,
                               block_id, realm, BLOCK_DATA)
    return block_id


@pytest.mark.trio
async def test_block_read_check_access_rights(backend, alice, bob,
Exemplo n.º 9
0
async def test_organization_stats_data(backend_rest_send, realm, realm_factory,
                                       alice, backend):
    async def organization_stats():
        status, _, body = await backend_rest_send(
            f"/administration/organizations/{alice.organization_id}/stats")
        assert status == (200, "OK")
        return organization_stats_rep_serializer.load(body)

    rep = await organization_stats()
    assert rep == {
        "data_size":
        0,
        "metadata_size":
        ANY,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        4,
    }
    initial_metadata_size = rep["metadata_size"]

    # Create new metadata
    await backend.vlob.create(
        organization_id=alice.organization_id,
        author=alice.device_id,
        realm_id=realm,
        encryption_revision=1,
        vlob_id=VlobID.new(),
        timestamp=pendulum.now(),
        blob=b"1234",
    )
    rep = await organization_stats()
    assert rep == {
        "data_size":
        0,
        "metadata_size":
        initial_metadata_size + 4,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        4,
    }

    # Create new data
    await backend.block.create(
        organization_id=alice.organization_id,
        author=alice.device_id,
        block_id=BlockID.new(),
        realm_id=realm,
        block=b"1234",
    )
    rep = await organization_stats()
    assert rep == {
        "data_size":
        4,
        "metadata_size":
        initial_metadata_size + 4,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        4,
    }

    # create new workspace
    await realm_factory(backend, alice)
    rep = await organization_stats()
    assert rep == {
        "data_size":
        4,
        "metadata_size":
        initial_metadata_size + 4,
        "users":
        3,
        "active_users":
        3,
        "users_per_profile_detail": [
            {
                "profile": UserProfile.ADMIN,
                "active": 2,
                "revoked": 0
            },
            {
                "profile": UserProfile.STANDARD,
                "active": 1,
                "revoked": 0
            },
            {
                "profile": UserProfile.OUTSIDER,
                "active": 0,
                "revoked": 0
            },
        ],
        "realms":
        5,
    }
Exemplo n.º 10
0
async def test_access_during_reencryption(
    backend, alice_backend_sock, alice, realm_factory, next_timestamp
):
    # First initialize a nice realm with block and vlob
    realm_id = await realm_factory(backend, author=alice)
    vlob_id = VlobID.new()
    block_id = BlockID.new()
    await backend.vlob.create(
        organization_id=alice.organization_id,
        author=alice.device_id,
        realm_id=realm_id,
        encryption_revision=1,
        vlob_id=vlob_id,
        timestamp=next_timestamp(),
        blob=b"v1",
    )
    await backend.block.create(
        organization_id=alice.organization_id,
        author=alice.device_id,
        realm_id=realm_id,
        block_id=block_id,
        block=b"<block_data>",
    )

    async def _assert_write_access_disallowed(encryption_revision):
        rep = await vlob_create(
            alice_backend_sock,
            realm_id=realm_id,
            vlob_id=VlobID.new(),
            blob=b"data",
            encryption_revision=encryption_revision,
            check_rep=False,
        )
        assert rep == {"status": "in_maintenance"}
        rep = await vlob_update(
            alice_backend_sock,
            vlob_id,
            version=2,
            blob=b"data",
            encryption_revision=encryption_revision,
            check_rep=False,
        )
        assert rep == {"status": "in_maintenance"}
        rep = await block_create(
            alice_backend_sock, block_id=block_id, realm_id=realm_id, block=b"data", check_rep=False
        )
        assert rep == {"status": "in_maintenance"}

    async def _assert_read_access_allowed(encryption_revision, expected_blob=b"v1"):
        rep = await vlob_read(
            alice_backend_sock, vlob_id=vlob_id, version=1, encryption_revision=encryption_revision
        )
        assert rep["status"] == "ok"
        assert rep["blob"] == expected_blob

        rep = await block_read(alice_backend_sock, block_id=block_id)
        assert rep == {"status": "ok", "block": b"<block_data>"}

        # For good measure, also try those read-only commands even if they
        # are encryption-revision agnostic
        rep = await vlob_list_versions(alice_backend_sock, vlob_id=vlob_id)
        assert rep["status"] == "ok"
        rep = await vlob_poll_changes(alice_backend_sock, realm_id=realm_id, last_checkpoint=0)
        assert rep["status"] == "ok"

    async def _assert_read_access_bad_encryption_revision(encryption_revision, expected_status):
        rep = await vlob_read(
            alice_backend_sock, vlob_id=vlob_id, version=1, encryption_revision=encryption_revision
        )
        assert rep == {"status": expected_status}

    # Sanity check just to make we can access the data with initial encryption revision
    await _assert_read_access_allowed(1)

    # Now start reencryption
    await backend.realm.start_reencryption_maintenance(
        organization_id=alice.organization_id,
        author=alice.device_id,
        realm_id=realm_id,
        encryption_revision=2,
        per_participant_message={alice.user_id: b"<whatever>"},
        timestamp=pendulum_now(),
    )

    # Only read with old encryption revision is now allowed
    await _assert_read_access_allowed(1)
    await _assert_read_access_bad_encryption_revision(2, expected_status="in_maintenance")
    await _assert_write_access_disallowed(1)
    await _assert_write_access_disallowed(2)

    # Actually reencrypt the vlob data, this shouldn't affect us for the moment
    # given reencryption is not formally finished
    await backend.vlob.maintenance_save_reencryption_batch(
        organization_id=alice.organization_id,
        author=alice.device_id,
        realm_id=realm_id,
        encryption_revision=2,
        batch=[(vlob_id, 1, b"v2")],
    )

    await _assert_read_access_allowed(1)
    await _assert_read_access_bad_encryption_revision(2, expected_status="in_maintenance")
    await _assert_write_access_disallowed(1)
    await _assert_write_access_disallowed(2)

    # Finish the reencryption
    await backend.realm.finish_reencryption_maintenance(
        organization_id=alice.organization_id,
        author=alice.device_id,
        realm_id=realm_id,
        encryption_revision=2,
    )

    # Now only the new encryption revision is allowed
    await _assert_read_access_allowed(2, expected_blob=b"v2")
    await _assert_read_access_bad_encryption_revision(1, expected_status="bad_encryption_revision")