Beispiel #1
0
def test_merge_local_user_manifest_changes_placeholder(gen_date, alice, speculative_placeholder):
    d1, d2, d3, d4 = [gen_date() for _ in range(4)]

    w1 = WorkspaceEntry.new(name=EntryName("w1"), timestamp=d2)
    w2 = WorkspaceEntry.new(name=EntryName("w2"), timestamp=d2)
    w3 = WorkspaceEntry.new(name=EntryName("w3"), timestamp=d2)

    diverged = LocalUserManifest.new_placeholder(
        alice.device_id,
        id=alice.user_manifest_id,
        timestamp=d4,
        speculative=speculative_placeholder,
    ).evolve(last_processed_message=30, workspaces=(w1, w3))
    target = UserManifest(
        author=alice.device_id,
        timestamp=d2,
        id=alice.user_manifest_id,
        version=3,
        created=d1,
        updated=d3,
        last_processed_message=20,
        workspaces=(w1, w2),
    )
    expected_merged = LocalUserManifest(
        base=target,
        updated=d4,
        last_processed_message=30,
        workspaces=(w1, w2, w3),
        need_sync=True,
        speculative=False,
    )

    merged = merge_local_user_manifests(diverged, target)
    assert merged == expected_merged
Beispiel #2
0
async def test_sharing_event_on_sync_if_same_role(
    running_backend, alice_user_fs, alice2_user_fs, bob_user_fs, alice, bob
):
    # Share a workspace, alice2 knows about it
    with freeze_time("2000-01-02"):
        wid = await create_shared_workspace("w", bob_user_fs, alice_user_fs, alice2_user_fs)
    expected_entry_v1 = WorkspaceEntry(
        name="w (shared by bob)",
        id=wid,
        key=ANY,
        encryption_revision=1,
        encrypted_on=Pendulum(2000, 1, 2),
        role_cached_on=Pendulum(2000, 1, 2),
        role=WorkspaceRole.MANAGER,
    )

    # Then change alice's role...
    await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.OWNER)
    with freeze_time("2000-01-03"):
        await alice_user_fs.process_last_messages()
    await alice_user_fs.sync()

    # ...and give back alice the same role
    await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.MANAGER)
    with freeze_time("2000-01-04"):
        await alice_user_fs.process_last_messages()
    expected_entry_v3 = expected_entry_v1.evolve(role_cached_on=Pendulum(2000, 1, 4))
    await alice_user_fs.sync()

    # A single sharing event should be triggered
    with alice2_user_fs.event_bus.listen() as spy:
        await alice2_user_fs.sync()
    spy.assert_event_occured(
        "sharing.updated", {"new_entry": expected_entry_v3, "previous_entry": expected_entry_v1}
    )
Beispiel #3
0
def test_created_field_modified_by_remote(gen_date, alice, with_local_changes):
    d1, d2, d3, d4 = [gen_date() for _ in range(4)]

    w1 = WorkspaceEntry.new(name=EntryName("w1"), timestamp=d2)
    base = UserManifest(
        author=alice.device_id,
        timestamp=d2,
        id=alice.user_manifest_id,
        version=1,
        created=d1,
        updated=d2,
        last_processed_message=0,
        workspaces=(w1,),
    )

    local = LocalUserManifest.from_remote(base)
    if with_local_changes:
        w2 = WorkspaceEntry.new(name=EntryName("w1"), timestamp=d3)
        local = local.evolve(
            need_sync=True, updated=d3, last_processed_message=1, workspaces=(w1, w2)
        )

    target = base.evolve(created=d4, version=2)

    expected_merged = local.evolve(base=target)
    merged = merge_local_user_manifests(local, target)
    # Remote always control the value of the create field
    assert merged == expected_merged
Beispiel #4
0
async def test_reshare_workspace(running_backend, alice_user_fs, bob_user_fs,
                                 alice, bob):
    # Share a workspace...
    with freeze_time("2000-01-02"):
        wid = await alice_user_fs.workspace_create("w1")
    await alice_user_fs.workspace_share(wid, bob.user_id,
                                        WorkspaceRole.MANAGER)
    with freeze_time("2000-01-03"):
        await bob_user_fs.process_last_messages()

    # ...and unshare it...
    await alice_user_fs.workspace_share(wid, bob.user_id, None)
    with freeze_time("2000-01-04"):
        await bob_user_fs.process_last_messages()

    # ...and re-share it !
    await alice_user_fs.workspace_share(wid, bob.user_id,
                                        WorkspaceRole.MANAGER)
    with bob_user_fs.event_bus.listen() as spy:
        with freeze_time("2000-01-05"):
            await bob_user_fs.process_last_messages()
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {
            "new_entry":
            WorkspaceEntry(
                name="w1",
                id=wid,
                key=ANY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 2),
                role_cached_on=datetime(2000, 1, 5),
                role=WorkspaceRole.MANAGER,
            ),
            "previous_entry":
            WorkspaceEntry(
                name="w1",
                id=wid,
                key=ANY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 2),
                role_cached_on=datetime(2000, 1, 4),
                role=None,
            ),
        },
    )

    # Check access
    aum = alice_user_fs.get_user_manifest()
    bum = bob_user_fs.get_user_manifest()
    assert len(aum.workspaces) == 1
    assert len(bum.workspaces) == 1
    aw = aum.workspaces[0]
    bw = bum.workspaces[0]

    assert bw.name == "w1"
    assert bw.id == aw.id
    assert bw.role == WorkspaceRole.MANAGER
Beispiel #5
0
async def test_unshare_ok(running_backend, alice_user_fs, bob_user_fs, alice,
                          bob):
    # Share a workspace...
    with freeze_time("2000-01-02"):
        wid = await alice_user_fs.workspace_create(EntryName("w1"))
    await alice_user_fs.workspace_share(wid, bob.user_id, WorkspaceRole.OWNER)
    await bob_user_fs.process_last_messages()

    # ...and unshare it
    await bob_user_fs.workspace_share(wid, alice.user_id, None)
    with alice_user_fs.event_bus.listen() as spy:
        with freeze_time("2000-01-03"):
            await alice_user_fs.process_last_messages()

    new_events = []
    for event in spy.events:
        if event.event == CoreEvent.SHARING_UPDATED:
            event.kwargs["new_entry"] = event.kwargs["new_entry"].evolve(
                key=KEY)
            event.kwargs["previous_entry"] = event.kwargs[
                "previous_entry"].evolve(key=KEY)
        new_events.append(event)
    spy.events = new_events

    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {
            "new_entry":
            WorkspaceEntry(
                name=EntryName("w1"),
                id=wid,
                key=KEY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 2),
                role_cached_on=datetime(2000, 1, 3),
                role=None,
            ),
            "previous_entry":
            WorkspaceEntry(
                name=EntryName("w1"),
                id=wid,
                key=KEY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 2),
                role_cached_on=datetime(2000, 1, 2),
                role=WorkspaceRole.OWNER,
            ),
        },
    )

    aum = alice_user_fs.get_user_manifest()
    aw = aum.workspaces[0]
    assert not aw.role
Beispiel #6
0
async def test_sharing_event_on_sync_if_same_role(running_backend,
                                                  alice_user_fs,
                                                  alice2_user_fs, bob_user_fs,
                                                  alice, bob):
    # Share a workspace, alice2 knows about it
    with freeze_time("2000-01-02"):
        wid = await create_shared_workspace(EntryName("w"), bob_user_fs,
                                            alice_user_fs, alice2_user_fs)
    expected_entry_v1 = WorkspaceEntry(
        name=EntryName("w"),
        id=wid,
        key=KEY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 2),
        role_cached_on=datetime(2000, 1, 2),
        role=WorkspaceRole.MANAGER,
    )

    # Then change alice's role...
    await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.OWNER)
    with freeze_time("2000-01-03"):
        await alice_user_fs.process_last_messages()
    await alice_user_fs.sync()

    # ...and give back alice the same role
    await bob_user_fs.workspace_share(wid, alice.user_id,
                                      WorkspaceRole.MANAGER)
    with freeze_time("2000-01-04"):
        await alice_user_fs.process_last_messages()
    expected_entry_v3 = expected_entry_v1.evolve(
        role_cached_on=datetime(2000, 1, 4))
    await alice_user_fs.sync()

    # A single sharing event should be triggered
    with alice2_user_fs.event_bus.listen() as spy:
        await alice2_user_fs.sync()

    new_events = []
    for event in spy.events:
        if event.event == CoreEvent.SHARING_UPDATED:
            event.kwargs["new_entry"] = event.kwargs["new_entry"].evolve(
                key=KEY)
            event.kwargs["previous_entry"] = event.kwargs[
                "previous_entry"].evolve(key=KEY)
        new_events.append(event)
    spy.events = new_events
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {
            "new_entry": expected_entry_v3,
            "previous_entry": expected_entry_v1
        },
    )
Beispiel #7
0
def test_merge_local_user_manifest_no_changes_in_diverged_placeholder(
    gen_date, alice, alice2, with_ignored_changes
):
    w1 = WorkspaceEntry.new(name="w1")
    d1, d2, d3, d4 = [gen_date() for _ in range(4)]

    base = UserManifest(
        author=alice.device_id,
        timestamp=d2,
        id=alice.user_manifest_id,
        version=1,
        created=d1,
        updated=d2,
        last_processed_message=0,
        workspaces=(w1,),
    )

    diverged = LocalUserManifest.from_remote(base)
    if with_ignored_changes:
        diverged = diverged.evolve(updated=d4, need_sync=True)

    target = UserManifest(
        author=alice2.device_id,
        timestamp=d2,
        id=alice2.user_manifest_id,
        version=2,
        created=d1,
        updated=d3,
        last_processed_message=0,
        workspaces=(w1,),
    )

    expected_merged = LocalUserManifest.from_remote(target)
    merged = merge_local_user_manifests(diverged, target)
    assert merged == expected_merged
Beispiel #8
0
def test_merge_local_user_manifest_no_changes_in_diverged_placeholder(
    gen_date, alice, alice2, with_ignored_changes
):
    d1, d2, d3, d4, d5, d6, d7 = [gen_date() for _ in range(7)]

    w1 = WorkspaceEntry.new(name=EntryName("w1"), timestamp=d2)
    base = UserManifest(
        author=alice.device_id,
        timestamp=d4,
        id=alice.user_manifest_id,
        version=1,
        created=d1,
        updated=d3,
        last_processed_message=0,
        workspaces=(w1,),
    )

    diverged = LocalUserManifest.from_remote(base)
    if with_ignored_changes:
        w1_bis = WorkspaceEntry(
            name=w1.name,
            id=w1.id,
            key=w1.key,
            # Same encryption revision than remote (so encryption date should be ignored)
            encryption_revision=1,
            encrypted_on=d5,
            # Cache older than remote
            role_cached_on=d1,
            role=RealmRole.MANAGER,
        )
        diverged = diverged.evolve(updated=d4, need_sync=True, workspaces=(w1_bis,))

    target = UserManifest(
        author=alice2.device_id,
        timestamp=d7,
        id=alice2.user_manifest_id,
        version=2,
        created=d1,
        updated=d6,
        last_processed_message=0,
        workspaces=(w1,),
    )

    expected_merged = LocalUserManifest.from_remote(target)
    merged = merge_local_user_manifests(diverged, target)
    assert merged == expected_merged
async def test_event_bus_internal_connection(aqtbot, running_backend,
                                             logged_gui, autoclose_dialog):
    w_w = await logged_gui.test_switch_to_workspaces_widget()
    uuid = UUID("1bc1e17b-157a-462f-86f2-7f64657ba16a")
    w_entry = WorkspaceEntry(
        name="w",
        id=ANY,
        key=ANY,
        encryption_revision=1,
        encrypted_on=ANY,
        role_cached_on=ANY,
        role=None,
    )

    async with aqtbot.wait_signal(w_w.fs_synced_qt):
        w_w.event_bus.send(CoreEvent.FS_ENTRY_SYNCED,
                           workspace_id=None,
                           id=uuid)

    async with aqtbot.wait_signal(w_w.fs_updated_qt):
        w_w.event_bus.send(CoreEvent.FS_ENTRY_UPDATED,
                           workspace_id=uuid,
                           id=None)

    async with aqtbot.wait_signal(w_w._workspace_created_qt):
        w_w.event_bus.send(CoreEvent.FS_WORKSPACE_CREATED, new_entry=w_entry)

    async with aqtbot.wait_signal(w_w.sharing_updated_qt):
        w_w.event_bus.send(CoreEvent.SHARING_UPDATED,
                           new_entry=w_entry,
                           previous_entry=None)

    async with aqtbot.wait_signal(w_w.entry_downsynced_qt):
        w_w.event_bus.send(CoreEvent.FS_ENTRY_DOWNSYNCED,
                           workspace_id=uuid,
                           id=uuid)

    async with aqtbot.wait_signal(w_w.mountpoint_started):
        w_w.event_bus.send(
            CoreEvent.MOUNTPOINT_STARTED,
            mountpoint=None,
            workspace_id=uuid,
            timestamp=pendulum.now(),
        )

    assert not autoclose_dialog.dialogs
    async with aqtbot.wait_signal(w_w.mountpoint_stopped):
        w_w.event_bus.send(
            CoreEvent.MOUNTPOINT_STOPPED,
            mountpoint=None,
            workspace_id=uuid,
            timestamp=pendulum.now(),
        )
    assert autoclose_dialog.dialogs == [(
        "Error",
        "Your permissions on this workspace have been revoked. You no longer have access to theses files.",
    )]
Beispiel #10
0
def test_merge_speculative_with_it_unsuspected_former_self(alice, local_changes):
    d1 = datetime(2000, 1, 1)
    d2 = datetime(2000, 1, 2)
    d3 = datetime(2000, 1, 3)

    # 1) User manifest is originally created by our device
    local = LocalUserManifest.new_placeholder(
        author=alice.device_id, id=alice.user_manifest_id, timestamp=d1, speculative=False
    )
    w1 = WorkspaceEntry.new(EntryName("foo"), timestamp=d1)
    local = local.evolve(workspaces=(w1,), last_processed_message=1)

    # 2) We sync the user manifest
    v1 = local.to_remote(author=alice.device_id, timestamp=d2)

    # 3) Now let's pretend we lost local storage, hence creating a new speculative manifest
    new_local = LocalUserManifest.new_placeholder(
        author=alice.device_id, id=alice.user_manifest_id, timestamp=d3, speculative=True
    )
    if local_changes:
        w2 = WorkspaceEntry.new(EntryName("bar"), timestamp=d3)
        new_local = new_local.evolve(workspaces=(w2,), last_processed_message=2)

    # 4) When syncing the manifest, we shouldn't remove any data from the remote
    merged = merge_local_user_manifests(new_local, v1)

    if local_changes:
        assert merged == LocalUserManifest(
            base=v1,
            updated=d3,
            last_processed_message=2,
            workspaces=(w2, w1),
            need_sync=True,
            speculative=False,
        )
    else:
        assert merged == LocalUserManifest(
            base=v1,
            updated=v1.updated,
            last_processed_message=1,
            workspaces=(w1,),
            need_sync=False,
            speculative=False,
        )
Beispiel #11
0
async def test_sharing_notifs(aqtbot, logged_gui, snackbar_catcher,
                              monkeypatch):
    c_w = logged_gui.test_get_central_widget()

    def _snackbar_shown(sb):
        assert snackbar_catcher.snackbars == sb

    ne = WorkspaceEntry.new(EntryName("Workspace"), datetime(2000, 1, 2))
    ne = ne.evolve(role=RealmRole.CONTRIBUTOR)
    pe = WorkspaceEntry.new(EntryName("Workspace"), datetime(2000, 1, 1))
    pe = pe.evolve(role=RealmRole.READER)

    c_w.handle_event(CoreEvent.SHARING_UPDATED,
                     new_entry=ne,
                     previous_entry=pe)
    await aqtbot.wait_until(lambda: _snackbar_shown([(
        "INFO",
        translate("TEXT_NOTIF_INFO_WORKSPACE_ROLE_UPDATED_workspace").format(
            workspace="Workspace"),
    )]))

    snackbar_catcher.reset()
    c_w.handle_event(CoreEvent.SHARING_UPDATED,
                     new_entry=ne,
                     previous_entry=None)
    await aqtbot.wait_until(lambda: _snackbar_shown([(
        "INFO",
        translate("TEXT_NOTIF_INFO_WORKSPACE_SHARED_workspace").format(
            workspace="Workspace"),
    )]))

    ne = ne.evolve(role=None)

    snackbar_catcher.reset()
    c_w.handle_event(CoreEvent.SHARING_UPDATED,
                     new_entry=ne,
                     previous_entry=pe)
    await aqtbot.wait_until(lambda: _snackbar_shown([(
        "INFO",
        translate("TEXT_NOTIF_INFO_WORKSPACE_UNSHARED_workspace").format(
            workspace="Workspace"),
    )]))
async def test_unshare_ok(running_backend, alice_user_fs, bob_user_fs, alice,
                          bob):
    # Share a workspace...
    with freeze_time("2000-01-02"):
        wid = await alice_user_fs.workspace_create("w1")
    await alice_user_fs.workspace_share(wid, bob.user_id, WorkspaceRole.OWNER)
    await bob_user_fs.process_last_messages()

    # ...and unshare it
    await bob_user_fs.workspace_share(wid, alice.user_id, None)
    with alice_user_fs.event_bus.listen() as spy:
        with freeze_time("2000-01-03"):
            await alice_user_fs.process_last_messages()
    spy.assert_event_occured(
        "sharing.updated",
        {
            "new_entry":
            WorkspaceEntry(
                name="w1",
                id=wid,
                key=ANY,
                encryption_revision=1,
                encrypted_on=Pendulum(2000, 1, 2),
                role_cached_on=Pendulum(2000, 1, 3),
                role=None,
            ),
            "previous_entry":
            WorkspaceEntry(
                name="w1",
                id=wid,
                key=ANY,
                encryption_revision=1,
                encrypted_on=Pendulum(2000, 1, 2),
                role_cached_on=Pendulum(2000, 1, 2),
                role=WorkspaceRole.OWNER,
            ),
        },
    )

    aum = alice_user_fs.get_user_manifest()
    aw = aum.workspaces[0]
    assert not aw.role
Beispiel #13
0
def merge_workspace_entry(base: Optional[WorkspaceEntry],
                          diverged: WorkspaceEntry,
                          target: WorkspaceEntry) -> WorkspaceEntry:
    assert diverged.id == target.id
    assert not base or base.id == target.id

    # If the name has been modified on both sides, target always wins
    if base and base.name != target.name:
        name = target.name
    elif base and base.name != diverged.name:
        name = diverged.name
    else:
        name = target.name

    # Keep last encryption
    if diverged.encryption_revision <= target.encryption_revision:
        # Note `diverged.encryption_revision == target.encryption_revision`
        # should imply `diverged.encrypted_on == target.encrypted_on`, but
        # there is no way to enforce this (e.g. a buggy client may have change
        # this value...). However we'd better keep the remote value to avoid
        # constant sync fight between two client if they endup with a different
        # value they both consider the "right" one.
        encryption_revision = target.encryption_revision
        encrypted_on = target.encrypted_on
        key = target.key
    else:
        encryption_revision = diverged.encryption_revision
        encrypted_on = diverged.encrypted_on
        key = diverged.key

    # Keep most recent cache info on role
    if target.role == diverged.role:
        role = target.role
        role_cached_on = max(target.role_cached_on, diverged.role_cached_on)

    elif target.role_cached_on > diverged.role_cached_on:
        role = target.role
        role_cached_on = target.role_cached_on

    else:
        role = diverged.role
        role_cached_on = diverged.role_cached_on

    return WorkspaceEntry(
        name=name,
        id=target.id,
        key=key,
        encryption_revision=encryption_revision,
        encrypted_on=encrypted_on,
        role_cached_on=role_cached_on,
        role=role,
    )
Beispiel #14
0
async def test_share_with_sharing_name_already_taken(running_backend,
                                                     alice_user_fs,
                                                     bob_user_fs, alice, bob):
    # Bob and Alice both has a workspace with similar name
    with freeze_time("2000-01-01"):
        awid = await alice_user_fs.workspace_create("w")
        bwid = await bob_user_fs.workspace_create("w")
        bw2id = await bob_user_fs.workspace_create("w")

    # Sharing them shouldn't be a trouble
    await bob_user_fs.sync()
    await alice_user_fs.workspace_share(awid, bob.user_id,
                                        WorkspaceRole.MANAGER)

    # Bob should get a notification
    with bob_user_fs.event_bus.listen() as spy:
        with freeze_time("2000-01-02"):
            await bob_user_fs.process_last_messages()
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {
            "new_entry":
            WorkspaceEntry(
                name="w",
                id=awid,
                key=ANY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 1),
                role_cached_on=datetime(2000, 1, 2),
                role=WorkspaceRole.MANAGER,
            ),
            "previous_entry":
            None,
        },
    )

    assert len(bob_user_fs.get_user_manifest().workspaces) == 3

    b_aw_stat = await bob_user_fs.get_workspace(awid).path_info("/")
    a_aw_stat = await alice_user_fs.get_workspace(awid).path_info("/")
    b_aw_stat.pop("need_sync")
    a_aw_stat.pop("need_sync")
    assert b_aw_stat == a_aw_stat

    b_bw_stat = await bob_user_fs.get_workspace(bwid).path_info("/")
    assert b_bw_stat["id"] == bwid
    b_bw2_stat = await bob_user_fs.get_workspace(bw2id).path_info("/")
    assert b_bw2_stat["id"] == bw2id
Beispiel #15
0
async def test_share_ok(running_backend, alice_user_fs, bob_user_fs, alice,
                        bob, presynced):
    with freeze_time("2000-01-02"):
        wid = await alice_user_fs.workspace_create("w1")

    if presynced:
        await alice_user_fs.sync()

    await alice_user_fs.workspace_share(wid, bob.user_id,
                                        WorkspaceRole.MANAGER)

    with bob_user_fs.event_bus.listen() as spy:
        with freeze_time("2000-01-03"):
            await bob_user_fs.process_last_messages()
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {
            "new_entry":
            WorkspaceEntry(
                name="w1",
                id=wid,
                key=ANY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 2),
                role_cached_on=datetime(2000, 1, 3),
                role=WorkspaceRole.MANAGER,
            ),
            "previous_entry":
            None,
        },
    )

    aum = alice_user_fs.get_user_manifest()
    bum = bob_user_fs.get_user_manifest()
    assert len(aum.workspaces) == 1
    assert len(bum.workspaces) == 1
    awe = aum.get_workspace_entry(wid)
    bwe = bum.get_workspace_entry(wid)

    assert bwe.name == "w1"
    assert bwe.id == awe.id
    assert bwe.role == WorkspaceRole.MANAGER

    aw = alice_user_fs.get_workspace(wid)
    bw = bob_user_fs.get_workspace(wid)
    aw_stat = await aw.path_info("/")
    bw_stat = await bw.path_info("/")
    assert aw_stat == bw_stat
Beispiel #16
0
def merge_workspace_entry(base: Optional[WorkspaceEntry],
                          diverged: WorkspaceEntry,
                          target: WorkspaceEntry) -> WorkspaceEntry:
    assert diverged.id == target.id
    assert not base or base.id == target.id

    # If the name has been modified on both sides, target always wins
    if base and base.name != target.name:
        name = target.name
    elif base and base.name != diverged.name:
        name = diverged.name
    else:
        name = target.name

    # Keep last encryption
    if diverged.encryption_revision < target.encryption_revision:
        encryption_revision = target.encryption_revision
        encrypted_on = target.encrypted_on
        key = target.key
    else:
        encryption_revision = diverged.encryption_revision
        encrypted_on = diverged.encrypted_on
        key = diverged.key

    # Keep most recent cache info on role
    if target.role == diverged.role:
        role = target.role
        role_cached_on = max(target.role_cached_on, diverged.role_cached_on)

    elif target.role_cached_on > diverged.role_cached_on:
        role = target.role
        role_cached_on = target.role_cached_on

    else:
        role = diverged.role
        role_cached_on = diverged.role_cached_on

    return WorkspaceEntry(
        name=name,
        id=target.id,
        key=key,
        encryption_revision=encryption_revision,
        encrypted_on=encrypted_on,
        role_cached_on=role_cached_on,
        role=role,
    )
Beispiel #17
0
async def test_share_ok(running_backend, alice_user_fs, bob_user_fs, alice,
                        bob, presynced):
    with freeze_time("2000-01-02"):
        wid = await alice_user_fs.workspace_create(EntryName("w1"))

    if presynced:
        await alice_user_fs.sync()

    await alice_user_fs.workspace_share(wid, bob.user_id,
                                        WorkspaceRole.MANAGER)

    with bob_user_fs.event_bus.listen() as spy:
        with freeze_time("2000-01-03"):
            await bob_user_fs.process_last_messages()
    expected_bob_w1_workspace_entry = WorkspaceEntry(
        name=EntryName("w1"),
        id=wid,
        key=KEY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 2),
        role_cached_on=datetime(2000, 1, 3),
        role=WorkspaceRole.MANAGER,
    )
    new_events = []
    for event in spy.events:
        if event.event == CoreEvent.SHARING_UPDATED:
            event.kwargs["new_entry"] = event.kwargs["new_entry"].evolve(
                key=KEY)
        new_events.append(event)
    spy.events = new_events
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {
            "new_entry": expected_bob_w1_workspace_entry,
            "previous_entry": None
        },
    )

    aum = alice_user_fs.get_user_manifest()
    bum = bob_user_fs.get_user_manifest()
    aum = _update_user_manifest_key(aum)
    bum = _update_user_manifest_key(bum)
    assert bum.workspaces == (expected_bob_w1_workspace_entry, )
    assert bum.get_workspace_entry(wid).key == aum.get_workspace_entry(wid).key
Beispiel #18
0
async def test_sharing_events_triggered_on_sync(
    running_backend, alice_user_fs, alice2_user_fs, bob_user_fs, alice, bob
):
    # Share a first workspace
    with freeze_time("2000-01-02"):
        wid = await create_shared_workspace("w", bob_user_fs, alice_user_fs)

    with alice2_user_fs.event_bus.listen() as spy:
        await alice2_user_fs.sync()
    expected_entry_v1 = WorkspaceEntry(
        name="w",
        id=wid,
        key=ANY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 2),
        role_cached_on=datetime(2000, 1, 2),
        role=WorkspaceRole.MANAGER,
    )
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED, {"new_entry": expected_entry_v1, "previous_entry": None}
    )

    # Change role
    await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.OWNER)
    with freeze_time("2000-01-03"):
        await alice_user_fs.process_last_messages()
    await alice_user_fs.sync()

    with alice2_user_fs.event_bus.listen() as spy:
        await alice2_user_fs.sync()
    expected_entry_v2 = WorkspaceEntry(
        name="w",
        id=wid,
        key=ANY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 2),
        role_cached_on=datetime(2000, 1, 3),
        role=WorkspaceRole.OWNER,
    )
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {"new_entry": expected_entry_v2, "previous_entry": expected_entry_v1},
    )

    # Revoke
    await bob_user_fs.workspace_share(wid, alice.user_id, None)
    with freeze_time("2000-01-04"):
        await alice_user_fs.process_last_messages()
    await alice_user_fs.sync()

    with alice2_user_fs.event_bus.listen() as spy:
        await alice2_user_fs.sync()
    expected_entry_v3 = WorkspaceEntry(
        name="w",
        id=wid,
        key=ANY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 2),
        role_cached_on=datetime(2000, 1, 4),
        role=None,
    )
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {"new_entry": expected_entry_v3, "previous_entry": expected_entry_v2},
    )
Beispiel #19
0
async def test_share_workspace_then_conflict_on_rights(
    running_backend, alice_user_fs, alice2_user_fs, bob_user_fs, alice, alice2, bob, first_to_sync
):
    # Bob shares a workspace with Alice...
    with freeze_time("2000-01-01"):
        wid = await bob_user_fs.workspace_create("w")
    with freeze_time("2000-01-02"):
        await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.MANAGER)

    # ...but only Alice's first device get the information
    with freeze_time("2000-01-03"):
        await alice_user_fs.process_last_messages()

    # Now Bob change the sharing rights...
    with freeze_time("2000-01-04"):
        await bob_user_fs.workspace_share(wid, alice.user_id, WorkspaceRole.CONTRIBUTOR)

    # ...this time it's Alice's second device which get the info
    with freeze_time("2000-01-05"):
        # Note we will process the 2 sharing messages bob sent us, this
        # will attribute role_cached_on to the first message timestamp even
        # if we cache the second message role...
        await alice2_user_fs.process_last_messages()

    if first_to_sync == "alice":
        first = alice_user_fs
        second = alice2_user_fs
        synced_timestamp = datetime(2000, 1, 7)
        synced_version = 3
    else:
        first = alice2_user_fs
        second = alice_user_fs
        synced_timestamp = datetime(2000, 1, 6)
        synced_version = 2

    # Finally Alice devices try to reconciliate
    with freeze_time("2000-01-06"):
        await first.sync()
    with freeze_time("2000-01-07"):
        await second.sync()
    # Resync first device to get changes from the 2nd
    with freeze_time("2000-01-08"):
        await first.sync()

    am = alice_user_fs.get_user_manifest()
    a2m = alice2_user_fs.get_user_manifest()
    expected_remote = UserManifest(
        author=alice2.device_id,
        timestamp=synced_timestamp,
        id=alice2.user_manifest_id,
        version=synced_version,
        created=datetime(2000, 1, 1),
        updated=datetime(2000, 1, 5),
        last_processed_message=2,
        workspaces=(
            WorkspaceEntry(
                name="w",
                id=wid,
                key=ANY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 1),
                role_cached_on=datetime(2000, 1, 5),
                role=WorkspaceRole.CONTRIBUTOR,
            ),
        ),
    )
    expected = LocalUserManifest(
        base=expected_remote,
        need_sync=False,
        updated=expected_remote.updated,
        last_processed_message=expected_remote.last_processed_message,
        workspaces=expected_remote.workspaces,
    )
    assert am == expected
    assert a2m == expected

    a_w = alice_user_fs.get_workspace(wid)
    a2_w = alice2_user_fs.get_workspace(wid)

    a_w_stat = await a_w.path_info("/")
    a2_w_stat = await a2_w.path_info("/")

    a_w_entry = a_w.get_workspace_entry()
    a2_w_entry = a2_w.get_workspace_entry()

    assert a_w_stat == {
        "type": "folder",
        "is_placeholder": False,
        "id": wid,
        "created": ANY,
        "updated": ANY,
        "base_version": 1,
        "need_sync": False,
        "children": [],
        "confinement_point": None,
    }
    assert a_w_stat == a2_w_stat

    assert a_w_entry == WorkspaceEntry(
        name=f"w",
        id=wid,
        key=ANY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 1),
        role_cached_on=datetime(2000, 1, 5),
        role=WorkspaceRole.CONTRIBUTOR,
    )
    assert a2_w_entry == a_w_entry
Beispiel #20
0
async def test_concurrent_devices_agree_on_user_manifest(
    running_backend,
    backend_data_binder,
    data_base_dir,
    user_fs_factory,
    coolorg,
    alice,
    alice2,
    with_speculative,
):
    KEY = SecretKey.generate()

    async def _switch_running_backend_offline(task_status):
        should_switch_online = trio.Event()
        backend_online = trio.Event()

        async def _switch_backend_online():
            should_switch_online.set()
            await backend_online.wait()

        with running_backend.offline():
            task_status.started(_switch_backend_online)
            await should_switch_online.wait()
        backend_online.set()

    # I call this "diagonal programming"...
    async with trio.open_nursery() as nursery:
        switch_back_online = await nursery.start(
            _switch_running_backend_offline)

        with freeze_time("2000-01-01"):
            if with_speculative != "both":
                await user_storage_non_speculative_init(
                    data_base_dir=data_base_dir, device=alice)
            async with user_fs_factory(
                    alice, data_base_dir=data_base_dir) as user_fs1:
                wksp1_id = await user_fs1.workspace_create(EntryName("wksp1"))

                with freeze_time("2000-01-02"):
                    if with_speculative not in ("both", "alice2"):
                        await user_storage_non_speculative_init(
                            data_base_dir=data_base_dir, device=alice2)
                    async with user_fs_factory(
                            alice2, data_base_dir=data_base_dir) as user_fs2:
                        wksp2_id = await user_fs2.workspace_create(
                            EntryName("wksp2"))

                        with freeze_time("2000-01-03"):
                            # Only now the backend appear offline, this is to ensure each
                            # userfs has created a user manifest in isolation
                            await backend_data_binder.bind_organization(
                                coolorg,
                                alice,
                                initial_user_manifest="not_synced")
                            await backend_data_binder.bind_device(
                                alice2, certifier=alice)

                            await switch_back_online()

                            # Sync user_fs2 first to ensure created_on field is
                            # kept even if further syncs have an earlier value
                            with freeze_time("2000-01-04"):
                                await user_fs2.sync()
                            with freeze_time("2000-01-05"):
                                await user_fs1.sync()
                            with freeze_time("2000-01-06"):
                                await user_fs2.sync()

                            # Now, both user fs should have the same view on data
                            expected_workspaces_entries = (
                                WorkspaceEntry(
                                    name=EntryName("wksp1"),
                                    id=wksp1_id,
                                    key=KEY,
                                    encryption_revision=1,
                                    encrypted_on=datetime(2000, 1, 1),
                                    role_cached_on=datetime(2000, 1, 1),
                                    role=WorkspaceRole.OWNER,
                                ),
                                WorkspaceEntry(
                                    name=EntryName("wksp2"),
                                    id=wksp2_id,
                                    key=KEY,
                                    encryption_revision=1,
                                    encrypted_on=datetime(2000, 1, 2),
                                    role_cached_on=datetime(2000, 1, 2),
                                    role=WorkspaceRole.OWNER,
                                ),
                            )
                            expected_user_manifest = LocalUserManifest(
                                base=UserManifest(
                                    id=alice.user_manifest_id,
                                    version=2,
                                    timestamp=datetime(2000, 1, 5),
                                    author=alice.device_id,
                                    created=datetime(2000, 1, 2),
                                    updated=datetime(2000, 1, 2),
                                    last_processed_message=0,
                                    workspaces=expected_workspaces_entries,
                                ),
                                need_sync=False,
                                updated=datetime(2000, 1, 2),
                                last_processed_message=0,
                                workspaces=expected_workspaces_entries,
                                speculative=False,
                            )

                            user_fs1_manifest = user_fs1.get_user_manifest()
                            user_fs2_manifest = user_fs2.get_user_manifest()

                            # We use to use ANY for the "key" argument in expected_user_manifest,
                            # so that we could compare the two instances safely. Sadly, ANY doesn't
                            # play nicely with the Rust bindings, so we instead update the instances
                            # to change the key.
                            user_fs1_manifest = user_fs1_manifest.evolve(
                                workspaces=tuple(
                                    w.evolve(key=KEY)
                                    for w in user_fs1_manifest.workspaces),
                                base=user_fs1_manifest.base.evolve(
                                    workspaces=tuple(
                                        w.evolve(key=KEY) for w in
                                        user_fs1_manifest.base.workspaces)),
                            )
                            user_fs2_manifest = user_fs2_manifest.evolve(
                                workspaces=tuple(
                                    w.evolve(key=KEY)
                                    for w in user_fs2_manifest.workspaces),
                                base=user_fs2_manifest.base.evolve(
                                    workspaces=tuple(
                                        w.evolve(key=KEY) for w in
                                        user_fs2_manifest.base.workspaces)),
                            )

                            assert user_fs1_manifest == expected_user_manifest
                            assert user_fs2_manifest == expected_user_manifest
Beispiel #21
0
async def test_share_with_sharing_name_already_taken(running_backend,
                                                     alice_user_fs,
                                                     bob_user_fs, alice, bob):
    # Bob and Alice both has a workspace with similar name
    with freeze_time("2000-01-01"):
        awid = await alice_user_fs.workspace_create(EntryName("w"))
        bwid = await bob_user_fs.workspace_create(EntryName("w"))
        bw2id = await bob_user_fs.workspace_create(EntryName("w"))

    # Sharing them shouldn't be a trouble
    await bob_user_fs.sync()
    await alice_user_fs.workspace_share(awid, bob.user_id,
                                        WorkspaceRole.MANAGER)

    # Bob should get a notification
    with bob_user_fs.event_bus.listen() as spy:
        with freeze_time("2000-01-02"):
            await bob_user_fs.process_last_messages()
    new_events = []
    for event in spy.events:
        if event.event == CoreEvent.SHARING_UPDATED:
            event.kwargs["new_entry"] = event.kwargs["new_entry"].evolve(
                key=KEY)
        new_events.append(event)
    spy.events = new_events
    spy.assert_event_occured(
        CoreEvent.SHARING_UPDATED,
        {
            "new_entry":
            WorkspaceEntry(
                name=EntryName("w"),
                id=awid,
                key=KEY,
                encryption_revision=1,
                encrypted_on=datetime(2000, 1, 1),
                role_cached_on=datetime(2000, 1, 2),
                role=WorkspaceRole.MANAGER,
            ),
            "previous_entry":
            None,
        },
    )

    bob_user_manifest = bob_user_fs.get_user_manifest()
    assert len(bob_user_manifest.workspaces) == 3
    bob_user_manifest = _update_user_manifest_key(bob_user_manifest)
    assert (WorkspaceEntry(
        name=EntryName("w"),
        id=bwid,
        key=KEY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 1),
        role_cached_on=datetime(2000, 1, 1),
        role=RealmRole.OWNER,
    ) in bob_user_manifest.workspaces)
    assert (WorkspaceEntry(
        name=EntryName("w"),
        id=bw2id,
        key=KEY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 1),
        role_cached_on=datetime(2000, 1, 1),
        role=RealmRole.OWNER,
    ) in bob_user_manifest.workspaces)
    assert (WorkspaceEntry(
        name=EntryName("w"),
        id=awid,
        key=KEY,
        encryption_revision=1,
        encrypted_on=datetime(2000, 1, 1),
        role_cached_on=datetime(2000, 1, 2),
        role=RealmRole.MANAGER,
    ) in bob_user_manifest.workspaces)