Пример #1
0
async def test_user_invite_claim_invalid_token(running_backend, backend, alice):
    new_device_id = DeviceID("zack@pc1")
    token = generate_invitation_token()
    bad_token = generate_invitation_token()
    invite_exception_occured = False
    claim_exception_occured = False

    async def _from_alice():
        nonlocal invite_exception_occured
        with pytest.raises(InviteClaimInvalidTokenError) as exc:
            await invite_and_create_user(alice, new_device_id.user_id, is_admin=False, token=token)
        assert (
            str(exc.value)
            == f"Invalid claim token provided by peer: `{bad_token}` (was expecting `{token}`)"
        )
        invite_exception_occured = True

    async def _from_new_device():
        nonlocal claim_exception_occured
        with pytest.raises(InviteClaimError) as exc:
            await claim_user(alice.organization_addr, new_device_id, token=bad_token)
        assert (
            str(exc.value)
            == "Cannot claim user: {'reason': 'Invitation creator rejected us.', 'status': 'denied'}"
        )
        claim_exception_occured = True

    await _invite_and_claim(running_backend, _from_alice, _from_new_device)
    assert invite_exception_occured
    assert claim_exception_occured
Пример #2
0
async def test_invite_claim_3_chained_users(running_backend, backend, alice):
    # Zeta will be invited by Zoe, Zoe will be invited by Zack
    new_device_id_1 = DeviceID("zack@pc1")
    new_device_1 = None
    token_1 = generate_invitation_token()
    new_device_id_2 = DeviceID("zoe@pc2")
    new_device_2 = None
    token_2 = generate_invitation_token()
    new_device_id_3 = DeviceID("zeta@pc3")
    new_device_3 = None
    token_3 = generate_invitation_token()

    async def _invite_from_alice():
        await invite_and_create_user(alice, new_device_id_1.user_id, token=token_1, is_admin=True)

    async def _claim_from_1():
        nonlocal new_device_1
        new_device_1 = await claim_user(alice.organization_addr, new_device_id_1, token=token_1)

    async def _invite_from_1():
        await invite_and_create_user(
            new_device_1, new_device_id_2.user_id, token=token_2, is_admin=True
        )

    async def _claim_from_2():
        nonlocal new_device_2
        new_device_2 = await claim_user(alice.organization_addr, new_device_id_2, token=token_2)

    async def _invite_from_2():
        await invite_and_create_user(
            new_device_2, new_device_id_3.user_id, token=token_3, is_admin=False
        )

    async def _claim_from_3():
        nonlocal new_device_3
        new_device_3 = await claim_user(alice.organization_addr, new_device_id_3, token=token_3)

    await _invite_and_claim(running_backend, _invite_from_alice, _claim_from_1)
    await _invite_and_claim(running_backend, _invite_from_1, _claim_from_2)
    await _invite_and_claim(running_backend, _invite_from_2, _claim_from_3)

    assert new_device_1.is_admin
    assert new_device_2.is_admin
    assert not new_device_3.is_admin

    # Now connect as the last user
    async with backend_authenticated_cmds_factory(
        new_device_2.organization_addr, new_device_2.device_id, new_device_2.signing_key
    ) as cmds:
        await cmds.ping("foo")
Пример #3
0
async def test_invite_claim_multiple_devices_from_chained_user(running_backend, backend, alice):
    # The devices are invited from one another
    new_device_id_1 = DeviceID("zack@pc1")
    new_device_1 = None
    token_1 = generate_invitation_token()

    new_device_id_2 = DeviceID("zack@pc2")
    new_device_2 = None
    token_2 = generate_invitation_token()

    new_device_id_3 = DeviceID("zack@pc3")
    new_device_3 = None
    token_3 = generate_invitation_token()

    async def _invite_from_alice():
        await invite_and_create_user(alice, new_device_id_1.user_id, token=token_1, is_admin=True)

    async def _claim_from_1():
        nonlocal new_device_1
        new_device_1 = await claim_user(alice.organization_addr, new_device_id_1, token=token_1)

    async def _invite_from_1():
        await invite_and_create_device(new_device_1, new_device_id_2.device_name, token=token_2)

    async def _claim_from_2():
        nonlocal new_device_2
        new_device_2 = await claim_device(alice.organization_addr, new_device_id_2, token=token_2)

    async def _invite_from_2():
        await invite_and_create_device(new_device_2, new_device_id_3.device_name, token=token_3)

    async def _claim_from_3():
        nonlocal new_device_3
        new_device_3 = await claim_device(alice.organization_addr, new_device_id_3, token=token_3)

    await _invite_and_claim(running_backend, _invite_from_alice, _claim_from_1)
    await _invite_and_claim(
        running_backend, _invite_from_1, _claim_from_2, event_name="device.claimed"
    )
    await _invite_and_claim(
        running_backend, _invite_from_2, _claim_from_3, event_name="device.claimed"
    )

    # Now connect as the last device
    async with backend_authenticated_cmds_factory(
        new_device_3.organization_addr, new_device_3.device_id, new_device_3.signing_key
    ) as cmds:
        await cmds.ping("foo")
Пример #4
0
async def test_invite_claim_device(running_backend, backend, alice):
    new_device_id = DeviceID(f"{alice.user_id}@NewDevice")
    new_device = None
    token = generate_invitation_token()

    async def _from_alice():
        await invite_and_create_device(alice,
                                       new_device_id.device_name,
                                       token=token)

    async def _from_new_device():
        nonlocal new_device
        new_device = await claim_device(alice.organization_addr,
                                        new_device_id,
                                        token=token)

    await _invite_and_claim(running_backend,
                            _from_alice,
                            _from_new_device,
                            event_name="device.claimed")

    # Now connect as the new device
    async with backend_authenticated_cmds_factory(
        new_device.organization_addr, new_device.device_id,
        new_device.signing_key) as cmds:
        await cmds.ping("foo")
Пример #5
0
async def test_user_invite_claim_cancel_invitation(monitor, running_backend,
                                                   backend, alice):
    new_device_id = DeviceID("zack@pc1")
    token = generate_invitation_token()

    invite_and_claim_cancel_scope = None

    async def _from_alice():
        nonlocal invite_and_claim_cancel_scope
        with trio.CancelScope() as invite_and_claim_cancel_scope:
            await invite_and_create_user(alice,
                                         new_device_id.user_id,
                                         is_admin=False,
                                         token=token)

    async def _cancel_invite_and_claim():
        invite_and_claim_cancel_scope.cancel()

    await _invite_and_claim(running_backend, _from_alice,
                            _cancel_invite_and_claim)

    # Now make sure the invitation cannot be used
    with trio.fail_after(1):
        with pytest.raises(InviteClaimError) as exc:
            await claim_user(alice.organization_addr,
                             new_device_id,
                             token=token)
        assert (
            str(exc.value) ==
            "Cannot retrieve invitation creator: User `zack` doesn't exist in backend"
        )
Пример #6
0
async def test_device_invite_claim_cancel_invitation(running_backend, backend,
                                                     alice):
    new_device_id = DeviceID(f"{alice.user_id}@NewDevice")
    token = generate_invitation_token()

    invite_and_claim_cancel_scope = None

    async def _from_alice():
        nonlocal invite_and_claim_cancel_scope
        with trio.CancelScope() as invite_and_claim_cancel_scope:
            await invite_and_create_device(alice,
                                           new_device_id.device_name,
                                           token=token)

    async def _cancel_invite_and_claim():
        invite_and_claim_cancel_scope.cancel()

    await _invite_and_claim(running_backend,
                            _from_alice,
                            _cancel_invite_and_claim,
                            event_name="device.claimed")

    # Now make sure the invitation cannot be used
    with trio.fail_after(1):
        with pytest.raises(InviteClaimError) as exc:
            await claim_device(alice.organization_addr,
                               new_device_id,
                               token=token)
    assert (
        str(exc.value) ==
        "Cannot retrieve invitation creator: User `alice@NewDevice` doesn't exist in backend"
    )
Пример #7
0
async def test_invite_claim_admin_user(running_backend, backend, alice):
    new_device_id = DeviceID("zack@pc1")
    new_device = None
    token = generate_invitation_token()

    async def _from_alice():
        await invite_and_create_user(alice,
                                     new_device_id.user_id,
                                     token=token,
                                     is_admin=True)

    async def _from_new_device():
        nonlocal new_device
        new_device = await claim_user(alice.organization_addr,
                                      new_device_id,
                                      token=token)

    await _invite_and_claim(running_backend, _from_alice, _from_new_device)

    assert new_device.is_admin

    # Now connect as the new user
    async with backend_authenticated_cmds_factory(
        new_device.organization_addr, new_device.device_id,
        new_device.signing_key) as cmds:
        await cmds.ping("foo")
Пример #8
0
    async def _do_test():
        nonlocal device_count
        device_count += 1
        new_device_id = DeviceID(f"{alice.user_id}@newdev{device_count}")
        token = generate_invitation_token()
        exception_occured = False

        async def _from_alice():
            await invite_and_create_device(alice,
                                           new_device_id.device_name,
                                           token=token)

        async def _from_new_device():
            nonlocal exception_occured
            with pytest.raises(InviteClaimCryptoError):
                await claim_device(alice.organization_addr,
                                   new_device_id,
                                   token=token)
            exception_occured = True

        await _invite_and_claim(running_backend,
                                _from_alice,
                                _from_new_device,
                                event_name="device.claimed")
        assert exception_occured
Пример #9
0
async def test_invite_claim_non_admin_user(running_backend, backend, alice):
    new_device_id = DeviceID("zack@pc1")
    new_device = None
    token = generate_invitation_token()

    async def _from_alice():
        await invite_and_create_user(alice, new_device_id.user_id, token=token, is_admin=False)

    async def _from_new_device():
        nonlocal new_device
        new_device = await claim_user(alice.organization_addr, new_device_id, token=token)

    await _invite_and_claim(running_backend, _from_alice, _from_new_device)
Пример #10
0
    def register_device(self):
        def _run_registration(device_name, token):
            self.portal.run(
                _handle_invite_and_create_device,
                self.register_queue,
                self.on_registered,
                self.on_register_error,
                self.core,
                device_name,
                token,
            )

        if not self.line_edit_device_name.text():
            show_warning(
                self,
                QCoreApplication.translate("RegisterDeviceDialog",
                                           "Please enter a device name."),
            )
            return

        try:
            token = generate_invitation_token()
            self.line_edit_device.setText(self.line_edit_device_name.text())
            self.line_edit_device.setCursorPosition(0)
            self.line_edit_token.setText(token)
            self.line_edit_token.setCursorPosition(0)
            self.line_edit_url.setText(self.core.device.organization_addr)
            self.line_edit_url.setCursorPosition(0)
            self.button_cancel.setFocus()
            self.widget_registration.show()
            self.register_thread = threading.Thread(
                target=_run_registration,
                args=(self.line_edit_device_name.text(), token))
            self.register_thread.start()
            self.cancel_scope = self.register_queue.get()
            self.button_cancel.show()
            self.line_edit_device_name.hide()
            self.button_register.hide()
            self.closing_allowed = False
        except:
            show_warning(
                self,
                QCoreApplication.translate("RegisterDeviceDialog",
                                           "Could not register the device."),
            )
Пример #11
0
async def _invite_user(config, device, invited_user_id, admin):
    async with backend_cmds_factory(device.organization_addr, device.device_id,
                                    device.signing_key) as cmds:

        token = generate_invitation_token()

        organization_addr_display = click.style(device.organization_addr,
                                                fg="yellow")
        token_display = click.style(token, fg="yellow")
        click.echo(f"Backend url: {organization_addr_display}")
        click.echo(f"Invitation token: {token_display}")

        async with spinner("Waiting for invitation reply"):
            invite_device_id = await invite_and_create_user(
                device, cmds, invited_user_id, token, admin)

        display_device = click.style(invite_device_id, fg="yellow")
        click.echo(f"Device {display_device} has been created")
Пример #12
0
    async def _do_test():
        nonlocal device_count
        device_count += 1
        new_device_id = DeviceID(f"user{device_count}@dev")
        token = generate_invitation_token()
        exception_occured = False

        async def _from_alice():
            await invite_and_create_user(alice, new_device_id.user_id, token=token, is_admin=False)

        async def _from_new_device():
            nonlocal exception_occured
            with pytest.raises(InviteClaimCryptoError):
                await claim_user(alice.organization_addr, new_device_id, token=token)
            exception_occured = True

        await _invite_and_claim(running_backend, _from_alice, _from_new_device)
        assert exception_occured
Пример #13
0
async def _invite_device(config, device, new_device_name):
    token = generate_invitation_token()

    organization_addr_display = click.style(device.organization_addr,
                                            fg="yellow")
    token_display = click.style(token, fg="yellow")
    click.echo(f"Backend url: {organization_addr_display}")
    click.echo(f"Invitation token: {token_display}")

    async with backend_cmds_factory(device.organization_addr, device.device_id,
                                    device.signing_key) as cmds:

        async with spinner("Waiting for invitation reply"):
            await invite_and_create_device(device, cmds, new_device_name,
                                           token)

        display_device = click.style(f"{device.device_name}@{new_device_name}",
                                     fg="yellow")
        click.echo(f"Device {display_device} is ready !")
Пример #14
0
async def _invite_user(config, device, invited_user_id, admin):
    action_addr = BackendOrganizationClaimUserAddr.build(
        organization_addr=device.organization_addr, user_id=invited_user_id)
    token = generate_invitation_token()

    action_addr_display = click.style(action_addr.to_url(), fg="yellow")
    token_display = click.style(token, fg="yellow")
    click.echo(f"url: {action_addr_display}")
    click.echo(f"token: {token_display}")

    async with spinner("Waiting for invitation reply"):
        invite_device_id = await invite_and_create_user(
            device=device,
            user_id=invited_user_id,
            token=token,
            is_admin=admin,
            keepalive=config.backend_connection_keepalive,
        )

    display_device = click.style(invite_device_id, fg="yellow")
    click.echo(f"Device {display_device} has been created")
Пример #15
0
async def _invite_device(config, device, new_device_name):
    action_addr = BackendOrganizationClaimDeviceAddr.build(
        organization_addr=device.organization_addr,
        device_id=DeviceID(f"{device.user_id}@{new_device_name}"),
    )
    token = generate_invitation_token()

    action_addr_display = click.style(action_addr.to_url(), fg="yellow")
    token_display = click.style(token, fg="yellow")
    click.echo(f"url: {action_addr_display}")
    click.echo(f"token: {token_display}")

    async with spinner("Waiting for invitation reply"):
        await invite_and_create_device(
            device=device,
            new_device_name=new_device_name,
            token=token,
            keepalive=config.backend_connection_keepalive,
        )

    display_device = click.style(f"{device.device_name}@{new_device_name}",
                                 fg="yellow")
    click.echo(f"Device {display_device} is ready !")
    def register_user(self):
        def _run_registration(username, token, is_admin):
            self.portal.run(
                _handle_invite_and_create_user,
                self.register_queue,
                self.on_registered,
                self.on_register_error,
                self.core,
                username,
                token,
                is_admin,
            )

        if not self.line_edit_username.text():
            show_warning(
                self,
                QCoreApplication.translate("RegisterUserDialog",
                                           "Please enter a username."))
            return

        users = self.portal.run(self.core.fs.backend_cmds.user_find,
                                self.line_edit_username.text())
        if len(users):
            show_warning(
                self,
                QCoreApplication.translate(
                    "RegisterUserDialog",
                    "A user with the same name already exists."),
            )
            return
        try:
            token = generate_invitation_token()
            self.line_edit_user.setText(self.line_edit_username.text())
            self.line_edit_user.setCursorPosition(0)
            self.line_edit_token.setText(token)
            self.line_edit_token.setCursorPosition(0)
            self.line_edit_url.setText(self.core.device.organization_addr)
            self.line_edit_url.setCursorPosition(0)
            self.button_cancel.setFocus()
            self.widget_registration.show()
            self.register_thread = threading.Thread(
                target=_run_registration,
                args=(self.line_edit_username.text(), token,
                      self.checkbox_is_admin.isChecked()),
            )
            self.register_thread.start()
            self.cancel_scope = self.register_queue.get()
            self.button_cancel.show()
            self.line_edit_username.hide()
            self.checkbox_is_admin.hide()
            self.button_register.hide()
            self.closing_allowed = False
        except:
            import traceback

            traceback.print_exc()
            show_warning(
                self,
                QCoreApplication.translate("RegisterUserDialog",
                                           "Could not register the user."),
            )
async def amain(
    backend_address="ws://*****:*****@laptop",
    bob_device_id="bob@laptop",
    other_device_name="pc",
    alice_workspace="alicews",
    bob_workspace="bobws",
    password="******",
    administrator_token=DEFAULT_ADMINISTRATOR_TOKEN,
    force=False,
):

    configure_logging("WARNING")

    config_dir = get_default_config_dir(os.environ)
    organization_id = OrganizationID(organization_id)
    backend_address = BackendAddr(backend_address)
    alice_device_id = DeviceID(alice_device_id)
    bob_device_id = DeviceID(bob_device_id)
    alice_slugid = f"{organization_id}:{alice_device_id}"
    bob_slugid = f"{organization_id}:{bob_device_id}"

    # Create organization

    async with backend_administrator_cmds_factory(backend_address, administrator_token) as cmds:

        bootstrap_token = await cmds.organization_create(organization_id)

        organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build(
            backend_address, organization_id, bootstrap_token
        )

    # Bootstrap organization and Alice user

    async with backend_anonymous_cmds_factory(organization_bootstrap_addr) as cmds:
        root_signing_key = SigningKey.generate()
        root_verify_key = root_signing_key.verify_key
        organization_addr = organization_bootstrap_addr.generate_organization_addr(root_verify_key)

        alice_device = generate_new_device(alice_device_id, organization_addr)

        save_device_with_password(config_dir, alice_device, password, force=force)

        now = pendulum.now()
        certified_user = certify_user(
            None, root_signing_key, alice_device.user_id, alice_device.public_key, now
        )
        certified_device = certify_device(
            None, root_signing_key, alice_device_id, alice_device.verify_key, now
        )

        await cmds.organization_bootstrap(
            organization_bootstrap_addr.organization_id,
            organization_bootstrap_addr.bootstrap_token,
            root_verify_key,
            certified_user,
            certified_device,
        )

    # Create a workspace for Alice

    config = load_config(config_dir, debug="DEBUG" in os.environ)
    async with logged_core_factory(config, alice_device) as core:
        await core.fs.workspace_create(f"/{alice_workspace}")

    # Register a new device for Alice

    token = generate_invitation_token()
    other_alice_device_id = DeviceID("@".join((alice_device.user_id, other_device_name)))
    other_alice_slugid = f"{organization_id}:{other_alice_device_id}"

    async def invite_task():
        async with backend_cmds_factory(
            alice_device.organization_addr, alice_device.device_id, alice_device.signing_key
        ) as cmds:
            await invite_and_create_device(alice_device, cmds, other_device_name, token)

    async def claim_task():
        async with backend_anonymous_cmds_factory(alice_device.organization_addr) as cmds:
            other_alice_device = await retry(claim_device, cmds, other_alice_device_id, token)
            save_device_with_password(config_dir, other_alice_device, password, force=force)

    async with trio.open_nursery() as nursery:
        nursery.start_soon(invite_task)
        nursery.start_soon(claim_task)

    # Invite Bob in

    token = generate_invitation_token()

    async def invite_task():
        async with backend_cmds_factory(
            alice_device.organization_addr, alice_device.device_id, alice_device.signing_key
        ) as cmds:
            await invite_and_create_user(
                alice_device, cmds, bob_device_id.user_id, token, is_admin=True
            )

    async def claim_task():
        async with backend_anonymous_cmds_factory(alice_device.organization_addr) as cmds:
            bob_device = await retry(claim_user, cmds, bob_device_id, token)
            save_device_with_password(config_dir, bob_device, password, force=force)

    async with trio.open_nursery() as nursery:
        nursery.start_soon(invite_task)
        nursery.start_soon(claim_task)

    # Create bob workspace and share with Alice

    bob_device = load_device_with_password(
        config.config_dir, organization_id, bob_device_id, password
    )

    async with logged_core_factory(config, bob_device) as core:
        await core.fs.workspace_create(f"/{bob_workspace}")
        await core.fs.share(f"/{bob_workspace}", alice_device_id.user_id)

    # Share Alice workspace with bob

    async with logged_core_factory(config, alice_device) as core:
        await core.fs.share(f"/{alice_workspace}", bob_device_id.user_id)

    # Print out

    click.echo(
        f"""
Mount alice and bob drives using:

    $ parsec core run -P {password} -D {alice_slugid}
    $ parsec core run -P {password} -D {other_alice_slugid}
    $ parsec core run -P {password} -D {bob_slugid}
"""
    )
Пример #18
0
async def initialize_test_organization(
    backend_address,
    organization_id,
    alice_device_id,
    bob_device_id,
    other_device_name,
    alice_workspace,
    bob_workspace,
    password,
    administration_token,
    force,
):

    configure_logging("WARNING")

    config_dir = get_default_config_dir(os.environ)
    alice_slugid = f"{organization_id}:{alice_device_id}"
    bob_slugid = f"{organization_id}:{bob_device_id}"

    # Create organization
    async with backend_administration_cmds_factory(
        backend_address, administration_token) as cmds:

        rep = await cmds.organization_create(organization_id)
        assert rep["status"] == "ok"
        bootstrap_token = rep["bootstrap_token"]

        organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build(
            backend_address, organization_id, bootstrap_token)

    # Bootstrap organization and Alice user

    async with backend_anonymous_cmds_factory(
            organization_bootstrap_addr) as cmds:
        root_signing_key = SigningKey.generate()
        root_verify_key = root_signing_key.verify_key
        organization_addr = organization_bootstrap_addr.generate_organization_addr(
            root_verify_key)

        alice_device = generate_new_device(alice_device_id, organization_addr,
                                           True)

        save_device_with_password(config_dir,
                                  alice_device,
                                  password,
                                  force=force)

        now = pendulum.now()
        user_certificate = UserCertificateContent(
            author=None,
            timestamp=now,
            user_id=alice_device.user_id,
            public_key=alice_device.public_key,
            is_admin=True,
        ).dump_and_sign(author_signkey=root_signing_key)
        device_certificate = DeviceCertificateContent(
            author=None,
            timestamp=now,
            device_id=alice_device.device_id,
            verify_key=alice_device.verify_key,
        ).dump_and_sign(author_signkey=root_signing_key)

        rep = await cmds.organization_bootstrap(
            organization_bootstrap_addr.organization_id,
            organization_bootstrap_addr.token,
            root_verify_key,
            user_certificate,
            device_certificate,
        )
        assert rep["status"] == "ok"

    # Create a workspace for Alice

    config = load_config(config_dir, debug="DEBUG" in os.environ)
    async with logged_core_factory(config, alice_device) as core:
        alice_ws_id = await core.user_fs.workspace_create(f"{alice_workspace}")
        await core.user_fs.sync()

    # Register a new device for Alice

    token = generate_invitation_token()
    other_alice_device_id = DeviceID(
        f"{alice_device.user_id}@{other_device_name}")
    other_alice_slugid = f"{organization_id}:{other_alice_device_id}"

    async def invite_task():
        await invite_and_create_device(alice_device, other_device_name, token)

    other_alice_device = None

    async def claim_task():
        nonlocal other_alice_device
        other_alice_device = await retry_claim(claim_device,
                                               alice_device.organization_addr,
                                               other_alice_device_id, token)
        save_device_with_password(config_dir,
                                  other_alice_device,
                                  password,
                                  force=force)

    async with trio.open_service_nursery() as nursery:
        nursery.start_soon(invite_task)
        nursery.start_soon(claim_task)

    # Invite Bob in

    token = generate_invitation_token()
    bob_device = None

    async def invite_task():
        await invite_and_create_user(alice_device,
                                     bob_device_id.user_id,
                                     token,
                                     is_admin=False)

    async def claim_task():
        nonlocal bob_device
        bob_device = await retry_claim(claim_user,
                                       alice_device.organization_addr,
                                       bob_device_id, token)
        save_device_with_password(config_dir,
                                  bob_device,
                                  password,
                                  force=force)

    async with trio.open_service_nursery() as nursery:
        nursery.start_soon(invite_task)
        nursery.start_soon(claim_task)

    # Create bob workspace and share with Alice

    async with logged_core_factory(config, bob_device) as core:
        bob_ws_id = await core.user_fs.workspace_create(f"{bob_workspace}")
        await core.user_fs.workspace_share(bob_ws_id, alice_device_id.user_id,
                                           WorkspaceRole.MANAGER)

    # Share Alice workspace with bob

    async with logged_core_factory(config, alice_device) as core:
        await core.user_fs.workspace_share(alice_ws_id, bob_device_id.user_id,
                                           WorkspaceRole.MANAGER)

    # Synchronize every device
    for device in (alice_device, other_alice_device, bob_device):
        async with logged_core_factory(config, device) as core:
            await core.user_fs.process_last_messages()
            await core.user_fs.sync()

    return alice_slugid, other_alice_slugid, bob_slugid