Exemplo n.º 1
0
def test_backend_organization_bootstrap_addr_bad_value(url, exc_msg):
    with pytest.raises(ValueError) as exc:
        BackendOrganizationBootstrapAddr.from_url(url)
    assert str(exc.value) == exc_msg
async def organization_bootstrap_addr(running_backend):
    org_id = OrganizationID("AnomalousMaterials")
    org_token = "123456"
    await running_backend.backend.organization.create(org_id, org_token)
    return BackendOrganizationBootstrapAddr.build(running_backend.addr, org_id,
                                                  org_token)
Exemplo n.º 3
0
async def initialize_test_organization(
    config_dir: Path,
    backend_address: BackendAddr,
    password: str,
    administration_token: str,
    force: bool,
) -> Tuple[LocalDevice, LocalDevice, LocalDevice]:

    configure_logging("WARNING")

    organization_id = OrganizationID("Org")

    # Create organization

    async with apiv1_backend_administration_cmds_factory(
        backend_address, administration_token
    ) as administration_cmds:

        rep = await administration_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 apiv1_backend_anonymous_cmds_factory(organization_bootstrap_addr) as anonymous_cmds:
        alice_device = await bootstrap_organization(
            cmds=anonymous_cmds,
            human_handle=HumanHandle(label="Alice", email="*****@*****.**"),
            device_label="laptop",
        )
        save_device_with_password(config_dir, alice_device, password, force=force)

    # 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("alice_workspace")
        await core.user_fs.sync()

    # Register a new device for Alice

    other_alice_device = None
    async with backend_authenticated_cmds_factory(
        addr=alice_device.organization_addr,
        device_id=alice_device.device_id,
        signing_key=alice_device.signing_key,
    ) as alice_cmds:
        rep = await alice_cmds.invite_new(type=InvitationType.DEVICE)
        assert rep["status"] == "ok"
        invitation_addr = BackendInvitationAddr.build(
            backend_addr=alice_device.organization_addr,
            organization_id=alice_device.organization_id,
            invitation_type=InvitationType.DEVICE,
            token=rep["token"],
        )

        async with backend_invited_cmds_factory(addr=invitation_addr) as invited_cmds:

            async def invite_task():
                initial_ctx = DeviceGreetInitialCtx(cmds=alice_cmds, token=invitation_addr.token)
                in_progress_ctx = await initial_ctx.do_wait_peer()
                in_progress_ctx = await in_progress_ctx.do_wait_peer_trust()
                in_progress_ctx = await in_progress_ctx.do_signify_trust()
                in_progress_ctx = await in_progress_ctx.do_get_claim_requests()
                await in_progress_ctx.do_create_new_device(
                    author=alice_device, device_label=in_progress_ctx.requested_device_label
                )

            async def claim_task():
                nonlocal other_alice_device
                initial_ctx = await claimer_retrieve_info(cmds=invited_cmds)
                in_progress_ctx = await initial_ctx.do_wait_peer()
                in_progress_ctx = await in_progress_ctx.do_signify_trust()
                in_progress_ctx = await in_progress_ctx.do_wait_peer_trust()
                other_alice_device = await in_progress_ctx.do_claim_device(
                    requested_device_label="pc"
                )

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

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

        # Invite Bob in

        bob_device = None
        rep = await alice_cmds.invite_new(type=InvitationType.USER, claimer_email="*****@*****.**")
        assert rep["status"] == "ok"
        invitation_addr = BackendInvitationAddr.build(
            backend_addr=alice_device.organization_addr,
            organization_id=alice_device.organization_id,
            invitation_type=InvitationType.USER,
            token=rep["token"],
        )

        async with backend_invited_cmds_factory(addr=invitation_addr) as invited_cmds:

            async def invite_task():
                initial_ctx = UserGreetInitialCtx(cmds=alice_cmds, token=invitation_addr.token)
                in_progress_ctx = await initial_ctx.do_wait_peer()
                in_progress_ctx = await in_progress_ctx.do_wait_peer_trust()
                in_progress_ctx = await in_progress_ctx.do_signify_trust()
                in_progress_ctx = await in_progress_ctx.do_get_claim_requests()
                await in_progress_ctx.do_create_new_user(
                    author=alice_device,
                    human_handle=in_progress_ctx.requested_human_handle,
                    device_label=in_progress_ctx.requested_device_label,
                    profile=UserProfile.STANDARD,
                )

            async def claim_task():
                nonlocal bob_device
                initial_ctx = await claimer_retrieve_info(cmds=invited_cmds)
                in_progress_ctx = await initial_ctx.do_wait_peer()
                in_progress_ctx = await in_progress_ctx.do_signify_trust()
                in_progress_ctx = await in_progress_ctx.do_wait_peer_trust()
                bob_device = await in_progress_ctx.do_claim_user(
                    requested_human_handle=HumanHandle(label="Bob", email="*****@*****.**"),
                    requested_device_label="laptop",
                )

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

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

    # 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("bob_workspace")
        await core.user_fs.workspace_share(bob_ws_id, alice_device.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.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_device, other_alice_device, bob_device)
async def _do_bootstrap_organization(
    config_dir,
    password: str,
    password_check: str,
    user_id: str,
    device_name: str,
    bootstrap_addr: BackendOrganizationBootstrapAddr,
):
    if password != password_check:
        raise JobResultError("password-mismatch")
    if len(password) < 8:
        raise JobResultError("password-size")

    try:
        device_id = DeviceID(f"{user_id}@{device_name}")
    except ValueError as exc:
        raise JobResultError("bad-device_name") from exc

    root_signing_key = SigningKey.generate()
    root_verify_key = root_signing_key.verify_key
    organization_addr = bootstrap_addr.generate_organization_addr(
        root_verify_key)

    try:
        device = generate_new_device(device_id,
                                     organization_addr,
                                     is_admin=True)
        save_device_with_password(config_dir, device, password)

    except LocalDeviceAlreadyExistsError as exc:
        raise JobResultError("user-exists") from exc

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

    try:
        async with backend_anonymous_cmds_factory(bootstrap_addr) as cmds:
            rep = await cmds.organization_bootstrap(
                bootstrap_addr.organization_id,
                bootstrap_addr.token,
                root_verify_key,
                user_certificate,
                device_certificate,
            )

            if rep["status"] == "already_bootstrapped":
                raise JobResultError("already-bootstrapped", info=str(rep))
            elif rep["status"] == "not_found":
                raise JobResultError("invalid-url", info=str(rep))
            elif rep["status"] != "ok":
                raise JobResultError("refused-by-backend", info=str(rep))
        return device, password
    except BackendConnectionRefused as exc:
        raise JobResultError("invalid-url", info=str(exc)) from exc

    except BackendNotAvailable as exc:
        raise JobResultError("backend-offline", info=str(exc)) from exc

    except BackendConnectionError as exc:
        raise JobResultError("refused-by-backend", info=str(exc)) from exc
Exemplo n.º 5
0
async def test_create_organization_already_bootstrapped(
    aqtbot,
    running_backend,
    catch_create_org_widget,
    autoclose_dialog,
    gui,
    monkeypatch,
    organization_factory,
    local_device_factory,
    alice,
):
    org = organization_factory()
    backend_user, backend_first_device = local_device_to_backend_user(alice, org)
    bootstrap_token = "123456"
    await running_backend.backend.organization.create(org.organization_id, bootstrap_token, None)
    await running_backend.backend.organization.bootstrap(
        org.organization_id,
        backend_user,
        backend_first_device,
        bootstrap_token,
        org.root_verify_key,
    )

    org_bs_addr = BackendOrganizationBootstrapAddr.build(
        running_backend.addr, org.organization_id, bootstrap_token
    )

    monkeypatch.setattr(
        "parsec.core.gui.main_window.get_text_input", lambda *args, **kwargs: (str(org_bs_addr))
    )

    # The org bootstrap window is usually opened using a sub-menu.
    # Sub-menus can be a bit challenging to open in tests so we cheat
    # using the keyboard shortcut Ctrl+O that has the same effect.
    await aqtbot.key_click(gui, "o", QtCore.Qt.ControlModifier, 200)

    co_w = await catch_create_org_widget()
    await aqtbot.wait_until(co_w.user_widget.isVisible)

    await aqtbot.key_clicks(co_w.user_widget.line_edit_user_full_name, "Gordon Freeman")
    await aqtbot.key_clicks(co_w.user_widget.line_edit_user_email, "*****@*****.**")

    def _user_widget_ready():
        assert co_w.user_widget.line_edit_org_name.text() == org.organization_id
        assert co_w.user_widget.line_edit_org_name.isReadOnly() is True

    await aqtbot.wait_until(_user_widget_ready)

    await aqtbot.mouse_click(co_w.user_widget.check_accept_contract, QtCore.Qt.LeftButton)
    await aqtbot.mouse_click(co_w.button_validate, QtCore.Qt.LeftButton)

    await aqtbot.wait_until(co_w.device_widget.isVisible)

    await aqtbot.key_clicks(co_w.device_widget.line_edit_device, "HEV")
    await aqtbot.key_clicks(co_w.device_widget.widget_password.line_edit_password, "nihilanth")
    await aqtbot.key_clicks(
        co_w.device_widget.widget_password.line_edit_password_check, "nihilanth"
    )
    await aqtbot.mouse_click(co_w.button_validate, QtCore.Qt.LeftButton)

    def _modal_shown():
        assert autoclose_dialog.dialogs == [("Error", "This bootstrap link was already used.")]

    await aqtbot.wait_until(_modal_shown)
Exemplo n.º 6
0
    async def _on_next_clicked(self):
        if isinstance(self.current_widget, CreateOrgUserInfoWidget):
            backend_addr = None
            org_id = None
            device_label = None
            human_handle = None

            if self.start_addr:
                backend_addr = self.start_addr
            else:
                try:
                    org_id = OrganizationID(
                        self.current_widget.line_edit_org_name.text())
                except ValueError as exc:
                    show_error(self,
                               _("TEXT_ORG_WIZARD_INVALID_ORGANIZATION_ID"),
                               exception=exc)
                    return
                try:
                    backend_addr = BackendOrganizationBootstrapAddr.build(
                        backend_addr=self.current_widget.backend_addr
                        if self.current_widget.radio_use_custom.isChecked()
                        else self.config.preferred_org_creation_backend_addr,
                        organization_id=org_id,
                    )
                except ValueError as exc:
                    show_error(self,
                               _("TEXT_ORG_WIZARD_INVALID_BACKEND_ADDR"),
                               exception=exc)
                    return
            try:
                user_name = validators.trim_user_name(
                    self.current_widget.line_edit_user_full_name.text())

                human_handle = HumanHandle(
                    self.current_widget.line_edit_user_email.text(), user_name)
            except ValueError as exc:
                show_error(self,
                           _("TEXT_ORG_WIZARD_INVALID_HUMAN_HANDLE"),
                           exception=exc)
                return
            # No try/except given `self.current_widget.line_edit_device` has already been validated against `DeviceLabel`
            try:
                device_label = DeviceLabel(
                    self.current_widget.line_edit_device.text())
            except ValueError as exc:
                show_error(self,
                           _("TEXT_ORG_WIZARD_INVALID_DEVICE_LABEL"),
                           exception=exc)
                return

            # TODO: call `await _do_create_org` directly since the context is now async
            self.create_job = self.jobs_ctx.submit_job(
                self.req_success,
                self.req_error,
                _do_create_org,
                config=self.config,
                human_handle=human_handle,
                device_label=device_label,
                backend_addr=backend_addr,
            )
            self.dialog.button_close.setVisible(False)
            self.button_validate.setEnabled(False)
        else:
            auth_method = self.current_widget.get_auth_method()
            try:
                if auth_method == DeviceFileType.PASSWORD:
                    save_device_with_password_in_config(
                        self.config.config_dir, self.new_device,
                        self.current_widget.get_auth())
                elif auth_method == DeviceFileType.SMARTCARD:
                    await save_device_with_smartcard_in_config(
                        self.config.config_dir, self.new_device)
                self.status = (self.new_device, auth_method,
                               self.current_widget.get_auth())

                if self.dialog:
                    self.dialog.accept()
                elif QApplication.activeModalWidget():
                    QApplication.activeModalWidget().accept()
                else:
                    logger.warning("Cannot close dialog when org wizard")
            except LocalDeviceCryptoError as exc:
                if auth_method == DeviceFileType.SMARTCARD:
                    show_error(self,
                               _("TEXT_INVALID_SMARTCARD"),
                               exception=exc)
            except LocalDeviceNotFoundError as exc:
                if auth_method == DeviceFileType.PASSWORD:
                    show_error(self,
                               _("TEXT_CANNOT_SAVE_DEVICE"),
                               exception=exc)
            except LocalDeviceError as exc:
                show_error(self, _("TEXT_CANNOT_SAVE_DEVICE"), exception=exc)
Exemplo n.º 7
0
async def initialize_test_organization(
    config_dir: Path,
    backend_address: BackendAddr,
    password: str,
    administration_token: str,
    additional_users_number: int,
    additional_devices_number: int,
) -> Tuple[LocalDevice, LocalDevice, LocalDevice]:
    organization_id = OrganizationID("Org")
    config = load_config(config_dir, debug="DEBUG" in os.environ)

    # Create organization

    bootstrap_token = await create_organization_req(
        organization_id, backend_address, administration_token
    )
    organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build(
        backend_address, organization_id, bootstrap_token
    )

    # Bootstrap organization and Alice user and create device "laptop" for Alice

    alice_device = await bootstrap_organization(
        organization_bootstrap_addr,
        human_handle=HumanHandle(label="Alice", email="*****@*****.**"),
        device_label=DeviceLabel("laptop"),
    )
    await user_storage_non_speculative_init(data_base_dir=config.data_base_dir, device=alice_device)
    save_device_with_password_in_config(
        config_dir=config_dir, device=alice_device, password=password
    )

    # Create context manager, alice_core will be needed for the rest of the script
    async with logged_core_factory(config, alice_device) as alice_core:
        async with backend_authenticated_cmds_factory(
            addr=alice_device.organization_addr,
            device_id=alice_device.device_id,
            signing_key=alice_device.signing_key,
        ) as alice_cmds:

            # Create new device "pc" for Alice
            other_alice_device = await _register_new_device(
                cmds=alice_cmds, author=alice_device, device_label=DeviceLabel("pc")
            )
            save_device_with_password_in_config(
                config_dir=config_dir, device=other_alice_device, password=password
            )
            # Invite Bob in organization
            bob_device = await _register_new_user(
                cmds=alice_cmds,
                author=alice_device,
                device_label=DeviceLabel("laptop"),
                human_handle=HumanHandle(email="*****@*****.**", label="Bob"),
                profile=UserProfile.STANDARD,
            )
            await user_storage_non_speculative_init(
                data_base_dir=config.data_base_dir, device=bob_device
            )
            save_device_with_password_in_config(
                config_dir=config_dir, device=bob_device, password=password
            )

            # Invite Toto in organization
            toto_device = await _register_new_user(
                cmds=alice_cmds,
                author=alice_device,
                device_label=DeviceLabel("laptop"),
                human_handle=HumanHandle(email="*****@*****.**", label="Toto"),
                profile=UserProfile.OUTSIDER,
            )
            await user_storage_non_speculative_init(
                data_base_dir=config.data_base_dir, device=toto_device
            )
            save_device_with_password_in_config(
                config_dir=config_dir, device=toto_device, password=password
            )
            # Create Alice workspace
            alice_ws_id = await alice_core.user_fs.workspace_create(EntryName("alice_workspace"))
            # Create context manager
            async with logged_core_factory(config, bob_device) as bob_core:
                # Create Bob workspace
                bob_ws_id = await bob_core.user_fs.workspace_create(EntryName("bob_workspace"))
                # Bob share workspace with Alice
                await bob_core.user_fs.workspace_share(
                    bob_ws_id, alice_device.user_id, WorkspaceRole.MANAGER
                )
                # Alice share workspace with Bob
                await alice_core.user_fs.workspace_share(
                    alice_ws_id, bob_device.user_id, WorkspaceRole.MANAGER
                )
                # Add additional random users
                await _add_random_users(
                    cmds=alice_cmds,
                    author=alice_device,
                    alice_core=alice_core,
                    bob_core=bob_core,
                    alice_ws_id=alice_ws_id,
                    bob_ws_id=bob_ws_id,
                    additional_users_number=additional_users_number,
                )
                # Add additional random device for alice
                await _add_random_device(
                    cmds=alice_cmds,
                    device=alice_device,
                    additional_devices_number=additional_devices_number,
                )

    # 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_device, other_alice_device, bob_device)
Exemplo n.º 8
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
async def test_create_organization_with_boostrap_token(
    gui, aqtbot, running_backend, catch_create_org_widget, autoclose_dialog
):
    # Firt create the organization
    bootstrap_token = "T0k3n"
    organization_id = OrganizationID("AnomalousMaterials")
    await running_backend.backend.organization.create(
        id=organization_id, bootstrap_token=bootstrap_token
    )

    good_bootstrap_addr = BackendOrganizationBootstrapAddr.build(
        running_backend.addr, organization_id, bootstrap_token
    )
    bad_bootstrap_addr = BackendOrganizationBootstrapAddr.build(
        running_backend.addr, organization_id, "B@dT0k3n"
    )

    # Now try to use the wrong bootstrap link
    for bootstrap_addr in (bad_bootstrap_addr, good_bootstrap_addr):
        autoclose_dialog.reset()

        await aqtbot.run(gui.add_instance, bootstrap_addr.to_url())

        co_w = await catch_create_org_widget()

        assert co_w

        def _user_widget_ready():
            assert co_w.user_widget.isVisible()
            assert not co_w.device_widget.isVisible()
            assert not co_w.button_previous.isVisible()
            assert not co_w.button_validate.isEnabled()
            # Organization name and server address should be already provided
            assert co_w.user_widget.line_edit_org_name.isReadOnly()
            assert not co_w.user_widget.radio_use_custom.isEnabled()

        await aqtbot.wait_until(_user_widget_ready)

        await aqtbot.key_clicks(co_w.user_widget.line_edit_user_full_name, "Gordon Freeman")
        await aqtbot.key_clicks(
            co_w.user_widget.line_edit_user_email, "*****@*****.**"
        )
        await aqtbot.mouse_click(co_w.user_widget.check_accept_contract, QtCore.Qt.LeftButton)

        def _user_widget_button_validate_ready():
            assert co_w.button_validate.isEnabled()

        await aqtbot.wait_until(_user_widget_button_validate_ready)
        await aqtbot.mouse_click(co_w.button_validate, QtCore.Qt.LeftButton)

        def _device_widget_ready():
            assert not co_w.user_widget.isVisible()
            assert co_w.device_widget.isVisible()
            assert co_w.button_previous.isVisible()
            assert not co_w.button_validate.isEnabled()

        await aqtbot.wait_until(_device_widget_ready)

        await aqtbot.key_clicks(co_w.device_widget.line_edit_device, "HEV")
        await aqtbot.key_clicks(co_w.device_widget.widget_password.line_edit_password, "nihilanth")
        await aqtbot.key_clicks(
            co_w.device_widget.widget_password.line_edit_password_check, "nihilanth"
        )

        def _device_widget_button_validate_ready():
            assert co_w.button_validate.isEnabled()

        await aqtbot.wait_until(_device_widget_button_validate_ready)

        await aqtbot.mouse_click(co_w.button_validate, QtCore.Qt.LeftButton)

        if bootstrap_addr is bad_bootstrap_addr:

            def _error_modal_shown():
                assert autoclose_dialog.dialogs == [("Error", "This bootstrap link is invalid.")]

            await aqtbot.wait_until(_error_modal_shown)

        else:

            def _modal_shown():
                assert autoclose_dialog.dialogs == [
                    (
                        "",
                        "You organization <b>AnomalousMaterials</b> has been created!<br />\n<br />\n"
                        "You will now be automatically logged in.<br />\n<br />\n"
                        "To help you start with PARSEC, you can read the "
                        '<a href="https://docs.parsec.cloud/en/stable/" title="User guide">user guide</a>.',
                    )
                ]

            await aqtbot.wait_until(_modal_shown)