Ejemplo n.º 1
0
def test_password_save_and_load(path_exists, config_dir, alice):
    config_dir = config_dir if path_exists else config_dir / "dummy"
    save_device_with_password_in_config(config_dir, alice, "S3Cr37")

    key_file = get_key_file(config_dir, alice)
    alice_reloaded = load_device_with_password(key_file, "S3Cr37")
    assert alice == alice_reloaded
Ejemplo n.º 2
0
 async def _on_validate_clicked(self):
     self.button_validate.setEnabled(False)
     auth_method = self.widget_auth.get_auth_method()
     try:
         if auth_method == DeviceFileType.PASSWORD:
             save_device_with_password_in_config(
                 self.core.config.config_dir, self.loaded_device,
                 self.widget_auth.get_auth())
         elif auth_method == DeviceFileType.SMARTCARD:
             await save_device_with_smartcard_in_config(
                 self.core.config.config_dir, self.loaded_device)
         show_info(self, _("TEXT_AUTH_CHANGE_SUCCESS"))
         if self.dialog:
             self.dialog.accept()
         elif QApplication.activeModalWidget():
             QApplication.activeModalWidget().accept()
         else:
             logger.warning(
                 "Cannot close dialog when changing password info")
     except LocalDeviceCryptoError as exc:
         self.button_validate.setEnabled(True)
         if auth_method == DeviceFileType.SMARTCARD:
             show_error(self, _("TEXT_INVALID_SMARTCARD"), exception=exc)
     except LocalDeviceNotFoundError as exc:
         self.button_validate.setEnabled(True)
         if auth_method == DeviceFileType.PASSWORD:
             show_error(self, _("TEXT_CANNOT_SAVE_DEVICE"), exception=exc)
     except LocalDeviceError as exc:
         self.button_validate.setEnabled(True)
         show_error(self, _("TEXT_CANNOT_SAVE_DEVICE"), exception=exc)
Ejemplo n.º 3
0
async def test_tab_login_logout(gui_factory, core_config, alice, monkeypatch):
    password = "******"
    save_device_with_password_in_config(core_config.config_dir, alice, password)
    gui = await gui_factory()

    # Fix the return value of ensure_string_size, because it can depend of the size of the window
    monkeypatch.setattr(
        "parsec.core.gui.main_window.ensure_string_size", lambda s, size, font: (s[:16] + "...")
    )

    assert gui.tab_center.count() == 1
    assert gui.tab_center.tabText(0) == translate("TEXT_TAB_TITLE_LOG_IN_SCREEN")
    assert not gui.add_tab_button.isEnabled()
    first_created_tab = gui.test_get_tab()

    await gui.test_switch_to_logged_in(alice)
    assert gui.tab_center.count() == 1
    assert gui.tab_center.tabText(0) == "CoolOrg - Alicey..."
    assert gui.add_tab_button.isEnabled()
    assert gui.test_get_tab() == first_created_tab

    await gui.test_logout()
    assert gui.tab_center.count() == 1
    assert gui.tab_center.tabText(0) == translate("TEXT_TAB_TITLE_LOG_IN_SCREEN")
    assert not gui.add_tab_button.isEnabled()
    assert gui.test_get_tab() != first_created_tab
Ejemplo n.º 4
0
async def test_tab_login_logout_two_tabs(aqtbot, gui_factory, core_config, alice, monkeypatch):
    password = "******"
    save_device_with_password_in_config(core_config.config_dir, alice, password)
    gui = await gui_factory()

    # Fix the return value of ensure_string_size, because it can depend of the size of the window
    monkeypatch.setattr(
        "parsec.core.gui.main_window.ensure_string_size", lambda s, size, font: (s[:16] + "...")
    )

    assert gui.tab_center.count() == 1
    assert gui.tab_center.tabText(0) == translate("TEXT_TAB_TITLE_LOG_IN_SCREEN")
    first_created_tab = gui.test_get_tab()

    await gui.test_switch_to_logged_in(alice)
    assert gui.tab_center.count() == 1
    assert gui.tab_center.tabText(0) == "CoolOrg - Alicey..."
    logged_tab = gui.test_get_tab()

    aqtbot.mouse_click(gui.add_tab_button, QtCore.Qt.LeftButton)
    assert gui.tab_center.count() == 2
    assert gui.tab_center.tabText(0) == "CoolOrg - Alicey..."
    assert gui.tab_center.tabText(1) == translate("TEXT_TAB_TITLE_LOG_IN_SCREEN")

    gui.switch_to_tab(0)

    def _logged_tab_displayed():
        assert logged_tab == gui.test_get_tab()

    await aqtbot.wait_until(_logged_tab_displayed)
    await gui.test_logout()
    assert gui.tab_center.count() == 1
    assert gui.tab_center.tabText(0) == translate("TEXT_TAB_TITLE_LOG_IN_SCREEN")
    assert gui.test_get_tab() != first_created_tab
Ejemplo n.º 5
0
async def test_login_no_available_devices(aqtbot, gui_factory,
                                          autoclose_dialog, core_config,
                                          alice):
    password = "******"
    save_device_with_password_in_config(core_config.config_dir, alice,
                                        password)

    device = list_available_devices(core_config.config_dir)[0]

    gui = await gui_factory()

    ParsecApp.add_connected_device(device.organization_id, device.device_id)

    lw = gui.test_get_login_widget()

    lw.reload_devices()

    def _devices_listed():
        assert lw.widget.layout().count() > 0

    await aqtbot.wait_until(_devices_listed)

    no_device_w = lw.widget.layout().itemAt(0).widget()
    assert isinstance(no_device_w, LoginNoDevicesWidget)
    # 0 is spacer, 1 is label
    assert no_device_w.layout().itemAt(
        2).widget().text() == "Create an organization"
    assert no_device_w.layout().itemAt(
        3).widget().text() == "Join an organization"
    assert no_device_w.layout().itemAt(4).widget().text() == "Recover a device"
Ejemplo n.º 6
0
async def test_link_file_unknown_org(
    aqtbot, core_config, gui_factory, autoclose_dialog, running_backend, alice
):
    password = "******"
    save_device_with_password_in_config(core_config.config_dir, alice, password)

    # Cheating a bit but it does not matter, we just want a link that appears valid with
    # an unknown organization
    org_addr = BackendOrganizationAddr.build(
        running_backend.addr, OrganizationID("UnknownOrg"), alice.organization_addr.root_verify_key
    )

    file_link = BackendOrganizationFileLinkAddr.build(
        organization_addr=org_addr, workspace_id=EntryID.new(), encrypted_path=b"<whatever>"
    )

    gui = await gui_factory(core_config=core_config, start_arg=file_link.to_url())
    lw = gui.test_get_login_widget()

    assert len(autoclose_dialog.dialogs) == 1
    assert autoclose_dialog.dialogs[0][0] == "Error"
    assert autoclose_dialog.dialogs[0][1] == translate(
        "TEXT_FILE_LINK_NOT_IN_ORG_organization"
    ).format(organization="UnknownOrg")

    def _devices_listed():
        assert lw.widget.layout().count() > 0

    await aqtbot.wait_until(_devices_listed)

    accounts_w = lw.widget.layout().itemAt(0).widget()
    assert accounts_w

    assert isinstance(accounts_w, LoginPasswordInputWidget)
Ejemplo n.º 7
0
def test_share_workspace(tmp_path, monkeypatch, alice, bob,
                         cli_workspace_role):
    config_dir = tmp_path / "config"
    # Mocking
    factory_mock = AsyncMock()
    workspace_role, expected_workspace_role = cli_workspace_role

    @asynccontextmanager
    async def logged_core_factory(*args, **kwargs):
        yield factory_mock(*args, **kwargs)

    password = "******"
    save_device_with_password_in_config(config_dir, bob, password)
    alice_info = [
        UserInfo(
            user_id=alice.user_id,
            human_handle=alice.human_handle,
            profile=alice.profile,
            created_on=alice.timestamp(),
            revoked_on=None,
        )
    ]

    def _run_cli_share_workspace_test(args, expected_error_code,
                                      use_recipiant):
        factory_mock.reset_mock()

        factory_mock.return_value.user_fs.workspace_share.is_async = True
        factory_mock.return_value.find_humans.is_async = True
        factory_mock.return_value.find_humans.return_value = (alice_info, 1)
        factory_mock.return_value.find_workspace_from_name.return_value.id = "ws1_id"

        monkeypatch.setattr(
            "parsec.core.cli.share_workspace.logged_core_factory",
            logged_core_factory)
        runner = CliRunner()
        result = runner.invoke(cli, args)

        assert result.exit_code == expected_error_code
        factory_mock.assert_called_once_with(ANY, bob)
        factory_mock.return_value.user_fs.workspace_share.assert_called_once_with(
            "ws1_id", alice.user_id, expected_workspace_role)
        if use_recipiant:
            factory_mock.return_value.find_humans.assert_called_once()
        else:
            factory_mock.return_value.find_humans.assert_not_called()

    default_args = (
        f"core share_workspace --password {password} "
        f"--device={bob.slughash} --config-dir={config_dir.as_posix()} "
        f"--role={workspace_role} "
        f"--workspace-name=ws1 ")

    # Test with user-id
    args = default_args + f"--user-id={alice.user_id}"
    _run_cli_share_workspace_test(args, 0, False)
    # Test with recipiant
    args = default_args + f"[email protected]"
    _run_cli_share_workspace_test(args, 0, True)
Ejemplo n.º 8
0
def test_available_devices_slughash_uniqueness(organization_factory,
                                               local_device_factory,
                                               config_dir):
    def _to_available(device):
        return AvailableDevice(
            key_file_path=get_default_key_file(config_dir, device),
            organization_id=device.organization_id,
            device_id=device.device_id,
            human_handle=device.human_handle,
            device_label=device.device_label,
            slug=device.slug,
            type=DeviceFileType.PASSWORD,
        )

    def _assert_different_as_available(d1, d2):
        available_device_d1 = _to_available(d1)
        available_device_d2 = _to_available(d2)
        assert available_device_d1.slughash != available_device_d2.slughash
        # Make sure slughash is consistent between LocalDevice and AvailableDevice
        assert available_device_d1.slughash == d1.slughash
        assert available_device_d2.slughash == d2.slughash

    o1 = organization_factory("org1")
    o2 = organization_factory("org2")

    # Different user id
    o1u1d1 = local_device_factory("u1@d1", o1)
    o1u2d1 = local_device_factory("u2@d1", o1)
    _assert_different_as_available(o1u1d1, o1u2d1)

    # Different device name
    o1u1d2 = local_device_factory("u1@d2", o1)
    _assert_different_as_available(o1u1d1, o1u1d2)

    # Different organization id
    o2u1d1 = local_device_factory("u1@d1", o2)
    _assert_different_as_available(o1u1d1, o2u1d1)

    # Same organization_id, but different root verify key !
    dummy_key = SigningKey.generate().verify_key
    o1u1d1_bad_rvk = o1u1d1.evolve(
        organization_addr=o1u1d1.organization_addr.build(
            backend_addr=o1u1d1.organization_addr.get_backend_addr(),
            organization_id=o1u1d1.organization_addr.organization_id,
            root_verify_key=dummy_key,
        ))
    _assert_different_as_available(o1u1d1, o1u1d1_bad_rvk)

    # Finally make sure slughash is stable through save/load
    save_device_with_password_in_config(config_dir, o1u1d1, "S3Cr37")
    key_file = get_key_file(config_dir, o1u1d1)
    o1u1d1_reloaded = load_device_with_password(key_file, "S3Cr37")
    available_device = _to_available(o1u1d1)
    available_device_reloaded = _to_available(o1u1d1_reloaded)
    assert available_device.slughash == available_device_reloaded.slughash
Ejemplo n.º 9
0
def test_same_device_id_different_orginazations(config_dir, alice, otheralice):
    devices = (alice, otheralice)

    for device in devices:
        save_device_with_password_in_config(
            config_dir, device, f"S3Cr37-{device.organization_id}")

    for device in devices:
        key_file = get_key_file(config_dir, device)
        device_reloaded = load_device_with_password(
            key_file, f"S3Cr37-{device.organization_id}")
        assert device == device_reloaded
Ejemplo n.º 10
0
def test_change_password(config_dir, alice):
    old_password = "******"
    new_password = "******"

    save_device_with_password_in_config(config_dir, alice, old_password)
    key_file = get_key_file(config_dir, alice)

    change_device_password(key_file, old_password, new_password)

    alice_reloaded = load_device_with_password(key_file, new_password)
    assert alice == alice_reloaded

    with pytest.raises(LocalDeviceCryptoError):
        load_device_with_password(key_file, old_password)
Ejemplo n.º 11
0
async def logged_gui(aqtbot, gui_factory, core_config, alice, bob,
                     fixtures_customization):
    # Logged as bob (i.e. standard profile) by default
    if fixtures_customization.get("logged_gui_as_admin", False):
        device = alice
    else:
        device = bob

    save_device_with_password_in_config(core_config.config_dir, device,
                                        DEFAULT_PASSWORD)

    gui = await gui_factory()
    await gui.test_switch_to_logged_in(device)
    return gui
Ejemplo n.º 12
0
def test_pki_enrollment_not_available(tmp_path, alice, no_parsec_extension):
    # First need to have alice device on the disk
    config_dir = tmp_path / "config"
    alice_password = "******"
    save_device_with_password_in_config(config_dir, alice, alice_password)

    # Now Run the cli
    runner = CliRunner()
    for cmd in [
            f"core pki_enrollment_submit --config-dir={config_dir.as_posix()} parsec://parsec.example.com/my_org?action=pki_enrollment",
            f"core pki_enrollment_poll --config-dir={config_dir.as_posix()}",
            f"core pki_enrollment_review_pendings --config-dir={config_dir.as_posix()} --device {alice.slughash} --password {alice_password}",
    ]:
        result = runner.invoke(cli, cmd)
        assert result.exit_code == 1
        assert "Error: Parsec smartcard extension not available" in result.output
 async def _on_validate_clicked(self):
     if isinstance(self.current_page, DeviceRecoveryImportPage1Widget):
         # No try/except given `self.line_edit_device` has already been validated against `DeviceLabel`
         device_label = DeviceLabel(
             validators.trim_user_name(
                 self.current_page.line_edit_device.text()))
         self.jobs_ctx.submit_job(
             self.create_new_device_success,
             self.create_new_device_failure,
             self._create_new_device,
             device_label=device_label,
             file_path=PurePath(self.current_page.get_recovery_key_file()),
             passphrase=self.current_page.get_passphrase(),
         )
     else:
         try:
             self.button_validate.setEnabled(False)
             auth_method = self.current_page.get_auth_method()
             if auth_method == DeviceFileType.PASSWORD:
                 save_device_with_password_in_config(
                     config_dir=self.config.config_dir,
                     device=self.new_device,
                     password=self.current_page.get_auth(),
                 )
             else:
                 await save_device_with_smartcard_in_config(
                     config_dir=self.config.config_dir,
                     device=self.new_device)
             show_info(self, translate("TEXT_RECOVERY_IMPORT_SUCCESS"))
             self.dialog.accept()
         except LocalDeviceCryptoError as exc:
             self.button_validate.setEnabled(True)
             if auth_method == DeviceFileType.SMARTCARD:
                 show_error(self,
                            translate("TEXT_INVALID_SMARTCARD"),
                            exception=exc)
         except LocalDeviceNotFoundError as exc:
             if auth_method == DeviceFileType.PASSWORD:
                 show_error(self,
                            translate("TEXT_CANNOT_SAVE_DEVICE"),
                            exception=exc)
             self.button_validate.setEnabled(True)
         except LocalDeviceError as exc:
             show_error(self,
                        translate("TEXT_CANNOT_SAVE_DEVICE"),
                        exception=exc)
             self.button_validate.setEnabled(True)
Ejemplo n.º 14
0
        async def test_switch_to_logged_in(self,
                                           device,
                                           password=DEFAULT_PASSWORD):
            try:
                save_device_with_password_in_config(self.config.config_dir,
                                                    device, password)
            except LocalDeviceAlreadyExistsError:
                pass

            # Reload to take into account the new saved device
            self.test_get_login_widget().reload_devices()

            await self.test_proceed_to_login(device, password)

            central_widget = self.test_get_central_widget()
            assert central_widget is not None

            return central_widget
Ejemplo n.º 15
0
def test_list_devices(organization_factory, local_device_factory, config_dir):
    org1 = organization_factory("org1")
    org2 = organization_factory("org2")

    o1d11 = local_device_factory("d1@1", org1)
    o1d12 = local_device_factory("d1@2", org1)
    o1d21 = local_device_factory("d2@1", org1)

    o2d11 = local_device_factory("d1@1", org2, has_human_handle=False)
    o2d12 = local_device_factory("d1@2", org2, has_device_label=False)
    o2d21 = local_device_factory("d2@1",
                                 org2,
                                 has_human_handle=False,
                                 has_device_label=False)

    for device in [o1d11, o1d12, o1d21]:
        save_device_with_password_in_config(config_dir, device, "S3Cr37")

    for device in [o2d11, o2d12, o2d21]:
        save_device_with_password_in_config(config_dir, device, "secret")

    # Also add dummy stuff that should be ignored
    device_dir = config_dir / "devices"
    (device_dir / "bad1").touch()
    (device_dir / "373955f566#corp#bob@laptop").mkdir()
    dummy_slug = "a54ed6df3a#corp#alice@laptop"
    (device_dir / dummy_slug).mkdir()
    (device_dir / dummy_slug / f"{dummy_slug}.keys").write_bytes(b"dummy")

    devices = list_available_devices(config_dir)

    expected_devices = {
        AvailableDevice(
            key_file_path=get_key_file(config_dir, d),
            organization_id=d.organization_id,
            device_id=d.device_id,
            human_handle=d.human_handle,
            device_label=d.device_label,
            slug=d.slug,
            type=DeviceFileType.PASSWORD,
        )
        for d in [o1d11, o1d12, o1d21, o2d11, o2d12, o2d21]
    }
    assert set(devices) == expected_devices
Ejemplo n.º 16
0
async def test_login(aqtbot, gui_factory, autoclose_dialog, core_config, alice,
                     monkeypatch):
    # Create an existing device before starting the gui
    password = "******"
    save_device_with_password_in_config(core_config.config_dir, alice,
                                        password)

    gui = await gui_factory()
    lw = gui.test_get_login_widget()
    tabw = gui.test_get_tab()

    def _devices_listed():
        assert lw.widget.layout().count() > 0

    await aqtbot.wait_until(_devices_listed)

    accounts_w = lw.widget.layout().itemAt(0).widget()
    assert accounts_w

    # Fix the return value of ensure_string_size, because it can depend of the size of the window
    monkeypatch.setattr("parsec.core.gui.main_window.ensure_string_size",
                        lambda s, size, font: (s[:16] + "..."))

    # Only one device, we skip the device selection

    def _password_widget_shown():
        assert isinstance(lw.widget.layout().itemAt(0).widget(),
                          LoginPasswordInputWidget)

    await aqtbot.wait_until(_password_widget_shown)

    password_w = lw.widget.layout().itemAt(0).widget()

    await aqtbot.key_clicks(password_w.line_edit_password, "P@ssw0rd")

    async with aqtbot.wait_signals(
        [lw.login_with_password_clicked, tabw.logged_in]):
        aqtbot.mouse_click(password_w.button_login, QtCore.Qt.LeftButton)

    central_widget = gui.test_get_central_widget()
    assert central_widget is not None
    assert (central_widget.button_user.text() ==
            f"{alice.organization_id}\n{alice.short_user_display}")
    assert gui.tab_center.tabText(0) == "CoolOrg - Alicey..."
Ejemplo n.º 17
0
def test_password_save_already_existing(config_dir, alice, alice2, otheralice):
    save_device_with_password_in_config(config_dir, alice, "S3Cr37")

    # Different devices should not overwrite each other
    save_device_with_password_in_config(config_dir, otheralice, "S3Cr37")
    save_device_with_password_in_config(config_dir, alice2, "S3Cr37")

    # Overwritting self is allowed
    save_device_with_password_in_config(config_dir, alice, "S3Cr37")

    devices = list_available_devices(config_dir)
    assert len(devices) == 3
Ejemplo n.º 18
0
async def test_expired_notification_logging(
    aqtbot,
    running_backend,
    autoclose_dialog,
    expiredorgalice,
    gui_factory,
    core_config,
    snackbar_catcher,
):

    # Log has alice on an expired organization
    save_device_with_password_in_config(core_config.config_dir,
                                        expiredorgalice, "P@ssw0rd")

    gui = await gui_factory()
    lw = gui.test_get_login_widget()
    tabw = gui.test_get_tab()

    def _devices_listed():
        assert lw.widget.layout().count() > 0

    await aqtbot.wait_until(_devices_listed)

    def _password_widget_shown():
        assert isinstance(lw.widget.layout().itemAt(0).widget(),
                          LoginPasswordInputWidget)

    await aqtbot.wait_until(_password_widget_shown)

    password_w = lw.widget.layout().itemAt(0).widget()

    await aqtbot.key_clicks(password_w.line_edit_password, "P@ssw0rd")

    async with aqtbot.wait_signals(
        [lw.login_with_password_clicked, tabw.logged_in]):
        aqtbot.mouse_click(password_w.button_login, QtCore.Qt.LeftButton)

    # Assert dialog
    def _expired_notified():
        assert snackbar_catcher.snackbars == [("WARN",
                                               "The organization has expired")]

    await aqtbot.wait_until(_expired_notified)
Ejemplo n.º 19
0
 async def _on_finished(self, new_device, auth_method, password):
     try:
         if auth_method == DeviceFileType.PASSWORD:
             save_device_with_password_in_config(
                 config_dir=self.config.config_dir,
                 device=new_device,
                 password=password)
         elif auth_method == DeviceFileType.SMARTCARD:
             await save_device_with_smartcard_in_config(
                 config_dir=self.config.config_dir, device=new_device)
         show_info(self, _("TEXT_CLAIM_DEVICE_SUCCESSFUL"))
         self.status = (new_device, auth_method, password)
         self.dialog.accept()
     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)
Ejemplo n.º 20
0
async def test_login_back_to_account_list(aqtbot, gui_factory,
                                          autoclose_dialog, core_config, alice,
                                          bob):
    # Create an existing device before starting the gui
    password = "******"
    save_device_with_password_in_config(core_config.config_dir, alice,
                                        password)
    save_device_with_password_in_config(core_config.config_dir, bob, password)

    gui = await gui_factory()
    lw = gui.test_get_login_widget()

    def _devices_listed():
        assert lw.widget.layout().count() > 0

    await aqtbot.wait_until(_devices_listed)

    accounts_w = lw.widget.layout().itemAt(0).widget()
    assert accounts_w

    async with aqtbot.wait_signal(accounts_w.account_clicked):
        aqtbot.mouse_click(
            accounts_w.accounts_widget.layout().itemAt(0).widget(),
            QtCore.Qt.LeftButton)

    def _password_widget_shown():
        assert isinstance(lw.widget.layout().itemAt(0).widget(),
                          LoginPasswordInputWidget)

    await aqtbot.wait_until(_password_widget_shown)

    password_w = lw.widget.layout().itemAt(0).widget()

    async with aqtbot.wait_signal(password_w.back_clicked):
        aqtbot.mouse_click(password_w.button_back, QtCore.Qt.LeftButton)

    def _account_widget_shown():
        assert isinstance(lw.widget.layout().itemAt(0).widget(),
                          LoginAccountsWidget)
Ejemplo n.º 21
0
async def test_link_file_unknown_workspace(
    aqtbot, core_config, gui_factory, autoclose_dialog, running_backend, alice
):
    password = "******"
    save_device_with_password_in_config(core_config.config_dir, alice, password)

    file_link = BackendOrganizationFileLinkAddr.build(
        organization_addr=alice.organization_addr,
        workspace_id=EntryID.new(),
        encrypted_path=b"<whatever>",
    )

    gui = await gui_factory(core_config=core_config, start_arg=file_link.to_url())
    lw = gui.test_get_login_widget()

    def _password_prompt():
        assert len(autoclose_dialog.dialogs) == 0
        lpi_w = lw.widget.layout().itemAt(0).widget()
        assert isinstance(lpi_w, LoginPasswordInputWidget)

    await aqtbot.wait_until(_password_prompt)

    lpi_w = lw.widget.layout().itemAt(0).widget()
    await aqtbot.key_clicks(lpi_w.line_edit_password, password)
    await aqtbot.wait_until(lambda: lpi_w.line_edit_password.text() == password)

    tabw = gui.test_get_tab()
    async with aqtbot.wait_signals([lw.login_with_password_clicked, tabw.logged_in]):
        aqtbot.mouse_click(lpi_w.button_login, QtCore.Qt.LeftButton)

    def _error_shown():
        assert len(autoclose_dialog.dialogs) == 1
        print(autoclose_dialog.dialogs)
        assert autoclose_dialog.dialogs[0] == (
            "Error",
            "You do not have access to the workspace containing the file. It may not have been shared with you.",
        )

    await aqtbot.wait_until(_error_shown)
Ejemplo n.º 22
0
 async def _on_finalize_clicked(self):
     try:
         if self.widget_auth.get_auth_method() == DeviceFileType.PASSWORD:
             save_device_with_password_in_config(
                 config_dir=self.config.config_dir,
                 device=self.new_device,
                 password=self.widget_auth.get_auth(),
             )
         elif self.widget_auth.get_auth_method(
         ) == DeviceFileType.SMARTCARD:
             await save_device_with_smartcard_in_config(
                 config_dir=self.config.config_dir, device=self.new_device)
         self.succeeded.emit(self.new_device,
                             self.widget_auth.get_auth_method(),
                             self.widget_auth.get_auth())
     except LocalDeviceCryptoError as exc:
         if self.widget_auth.get_auth_method() == DeviceFileType.SMARTCARD:
             show_error(self, _("TEXT_INVALID_SMARTCARD"), exception=exc)
     except LocalDeviceNotFoundError as exc:
         if self.widget_auth.get_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)
Ejemplo n.º 23
0
async def test_login_device_list(aqtbot, gui_factory, autoclose_dialog,
                                 core_config, alice, bob, alice2, adam,
                                 otheralice):
    password = "******"

    local_devices = [alice, bob, alice2, otheralice, adam]
    devices = []

    for d in local_devices:
        path = save_device_with_password_in_config(core_config.config_dir, d,
                                                   password)
        devices.append(load_device_file(path))

    # Settings the last device used
    core_config = core_config.evolve(gui_last_device=bob.device_id.str)

    gui = await gui_factory(core_config=core_config)
    lw = gui.test_get_login_widget()

    def _accounts_widget_listed():
        assert lw.widget.layout().count() == 1

    await aqtbot.wait_until(_accounts_widget_listed)

    accounts_w = lw.widget.layout().itemAt(0).widget()
    assert accounts_w

    def _devices_listed():
        # 5 devices, 1 spacer
        assert accounts_w.accounts_widget.layout().count() == len(devices) + 1

    await aqtbot.wait_until(_devices_listed)

    for idx in range(len(devices)):
        acc_w = accounts_w.accounts_widget.layout().itemAt(idx).widget()
        assert acc_w.device in devices
        assert acc_w.device.device_display == acc_w.label_device.text()
        assert acc_w.device.short_user_display == acc_w.label_name.text()
        assert acc_w.device.organization_id.str == acc_w.label_organization.text(
        )
        # We set the last_device in the config, the first one in the list should be bob
        if idx == 0:
            assert acc_w.device.device_id == bob.device_id
        devices.remove(acc_w.device)

    assert len(devices) == 0
Ejemplo n.º 24
0
def test_multiple_files_same_device(config_dir, alice):
    path = save_device_with_password_in_config(config_dir, alice, "test")

    # File names contain the slughash
    assert path.stem == alice.slughash

    # .. but are no longer meaningful
    (path.parent / "testing.keys").write_bytes(path.read_bytes())

    # Make sure we don't list duplicates
    devices = list_available_devices(config_dir)
    assert len(devices) == 1
    assert devices[0].device_id == alice.device_id

    # Remove orignal file
    path.unlink()
    devices = list_available_devices(config_dir)
    assert len(devices) == 1
    assert devices[0].device_id == alice.device_id
Ejemplo n.º 25
0
async def test_import_recovery_device(
    gui,
    aqtbot,
    monkeypatch,
    core_config,
    running_backend,
    catch_import_recovery_widget,
    autoclose_dialog,
    tmp_path,
    alice,
    kind,
):
    PASSWORD = "******"
    NEW_DEVICE_LABEL = DeviceLabel("Alice_New_Device")
    save_device_with_password_in_config(core_config.config_dir, alice,
                                        PASSWORD)

    recovery_device = await generate_recovery_device(alice)
    file_name = get_recovery_device_file_name(recovery_device)
    file_path = tmp_path / file_name
    passphrase = await save_recovery_device(file_path, recovery_device)

    with monkeypatch.context() as m:
        m.setattr(
            "parsec.core.gui.main_window.ask_question",
            lambda *args, **kwargs: translate("ACTION_RECOVER_DEVICE"),
        )
        aqtbot.key_click(gui, "i", QtCore.Qt.ControlModifier, 200)

    imp_w = await catch_import_recovery_widget()

    assert imp_w
    assert isinstance(imp_w.current_page, DeviceRecoveryImportPage1Widget)
    assert not imp_w.button_validate.isEnabled()

    imp_w.current_page.line_edit_device.setText("")

    imp_w.current_page.label_key_file.setText(str(file_path))
    assert not imp_w.button_validate.isEnabled()

    await aqtbot.key_clicks(imp_w.current_page.edit_passphrase, "abcdef")
    assert not imp_w.button_validate.isEnabled()
    await aqtbot.wait_until(
        lambda: imp_w.current_page.label_passphrase_error.text() == translate(
            "TEXT_RECOVERY_INVALID_PASSPHRASE"))
    assert imp_w.current_page.label_passphrase_error.isVisible()

    imp_w.current_page.edit_passphrase.setText("")
    await aqtbot.key_clicks(imp_w.current_page.edit_passphrase, passphrase)
    await aqtbot.wait_until(
        lambda: not imp_w.current_page.label_passphrase_error.isVisible())
    assert not imp_w.button_validate.isEnabled()

    await aqtbot.key_clicks(imp_w.current_page.line_edit_device,
                            str(NEW_DEVICE_LABEL))
    await aqtbot.wait_until(imp_w.button_validate.isEnabled)

    if kind == "ok":

        async with aqtbot.wait_signal(imp_w.create_new_device_success):
            aqtbot.mouse_click(imp_w.button_validate, QtCore.Qt.LeftButton)

        def _page2_shown():
            assert isinstance(imp_w.current_page, AuthenticationChoiceWidget)

        await aqtbot.wait_until(_page2_shown)

        assert not imp_w.button_validate.isEnabled()
        await aqtbot.key_clicks(
            imp_w.current_page.main_layout.itemAt(
                0).widget().line_edit_password, PASSWORD)
        assert not imp_w.button_validate.isEnabled()
        await aqtbot.key_clicks(
            imp_w.current_page.main_layout.itemAt(
                0).widget().line_edit_password_check, PASSWORD)
        await aqtbot.wait_until(imp_w.button_validate.isEnabled)

        aqtbot.mouse_click(imp_w.button_validate, QtCore.Qt.LeftButton)
        await aqtbot.wait_until(lambda: autoclose_dialog.dialogs == [(
            "", translate("TEXT_RECOVERY_IMPORT_SUCCESS"))])

        lw = gui.test_get_login_widget()

        def _devices_listed():
            assert lw.widget.layout().count() > 0

        await aqtbot.wait_until(_devices_listed)

        accounts_w = lw.widget.layout().itemAt(0).widget()
        assert accounts_w.accounts_widget.layout().count() == 3

        assert any(
            accounts_w.accounts_widget.layout().itemAt(i).widget() is not None
            and accounts_w.accounts_widget.layout().itemAt(
                i).widget().label_device.text() == str(NEW_DEVICE_LABEL)
            for i in range(accounts_w.accounts_widget.layout().count()))

    elif kind == "offline":

        with running_backend.offline():
            aqtbot.mouse_click(imp_w.button_validate, QtCore.Qt.LeftButton)

            def _error_shown():
                assert autoclose_dialog.dialogs == [
                    ("Error", translate("IMPORT_KEY_BACKEND_OFFLINE"))
                ]

            await aqtbot.wait_until(_error_shown)
Ejemplo n.º 26
0
async def test_pki_enrollment(tmp_path, mocked_parsec_ext_smartcard, backend,
                              alice):
    async with cli_with_running_backend_testbed(backend,
                                                alice) as (backend_addr,
                                                           alice):
        # First, save the local device needed for pki_enrollment_review_pendings command
        config_dir = tmp_path / "config"
        alice_password = "******"
        save_device_with_password_in_config(config_dir, alice, alice_password)

        runner = CliRunner()

        async def _cli_invoke_in_thread(cmd: str):
            # We must run the command from another thread given it will create it own trio loop
            async with real_clock_timeout():
                # Pass DEBUG environment variable for better output on crash
                return await trio.to_thread.run_sync(
                    lambda: runner.invoke(cli, cmd, env={"DEBUG": "1"}))

        async def run_review_pendings(extra_args: str = "",
                                      check_result: bool = True):
            result = await _cli_invoke_in_thread(
                f"core pki_enrollment_review_pendings --config-dir={config_dir.as_posix()} --device {alice.slughash} --password {alice_password} {extra_args}"
            )
            if not check_result:
                return result
            if result.exception:
                raise AssertionError(
                    f"CliRunner raise an exception: {result.exception}"
                ) from result.exception
            assert (
                result.exit_code == 0
            ), f"Bad exit_code: {result.exit_code}\nOutput: {result.output}"

            first_line, *other_lines = result.output.splitlines()
            match = re.match(r"^Found ([0-9]+) pending enrollment\(s\):",
                             first_line)
            assert match
            enrollments_count = int(match.group(1))
            # Just retrieve the enrollment ID
            enrollments = []
            for line in other_lines:
                match = re.match(r"^Pending enrollment ([a-f0-9]+)", line)
                if match:
                    enrollments.append(match.group(1))
            assert len(enrollments) == enrollments_count
            return enrollments

        addr = BackendPkiEnrollmentAddr.build(
            backend_addr, organization_id=alice.organization_id)

        async def run_submit(extra_args: str = "", check_result: bool = True):
            result = await _cli_invoke_in_thread(
                f"core pki_enrollment_submit --config-dir={config_dir.as_posix()} {addr} --device-label PC1 {extra_args}"
            )
            if not check_result:
                return result
            if result.exception:
                raise result.exception
            assert (
                result.exit_code == 0
            ), f"Bad exit_code: {result.exit_code}\nOutput: {result.output}"
            match = re.match(r"PKI enrollment ([a-f0-9]+) submitted",
                             result.output.splitlines()[-1])
            assert match
            return UUID(match.group(1))

        async def run_poll(extra_args: str = "", check_result: bool = True):
            result = await _cli_invoke_in_thread(
                f"core pki_enrollment_poll --config-dir={config_dir.as_posix()} --password S3cr3t {extra_args}"
            )
            if not check_result:
                return result
            if result.exception:
                raise AssertionError(
                    f"CliRunner raise an exception: {result.exception}"
                ) from result.exception
            assert (
                result.exit_code == 0
            ), f"Bad exit_code: {result.exit_code}\nOutput: {result.output}"

            first_line, *other_lines = result.output.splitlines()
            match = re.match(r"^Found ([0-9]+) pending enrollment\(s\):",
                             first_line)
            assert match
            enrollments_count = int(match.group(1))
            # Just retrieve the enrollment ID
            enrollments = []
            for line in other_lines:
                match = re.match(r"^Pending enrollment ([a-f0-9]+)", line)
                if match:
                    enrollments.append(match.group(1))
            assert len(enrollments) == enrollments_count
            return enrollments

        # Time for testing !

        # List with no enrollments
        assert await run_review_pendings(extra_args="--list-only") == []

        # Poll with no local enrollments
        assert await run_poll() == []

        # New enrollment
        enrollment_id1 = await run_submit()
        assert await run_poll() == [enrollment_id1.hex[:3]]
        # Poll doesn't modify the pending enrollment
        assert await run_poll() == [enrollment_id1.hex[:3]]

        # List new enrollment
        assert await run_review_pendings(extra_args="--list-only"
                                         ) == [enrollment_id1.hex[:3]]

        # Try to reply enrollment without force
        result = await run_submit(check_result=False)
        assert result.exit_code == 1
        assert (
            f"The certificate `{mocked_parsec_ext_smartcard.default_x509_certificate.certificate_sha1.hex()}` has already been submitted"
            in result.output)
        assert await run_review_pendings(extra_args="--list-only"
                                         ) == [enrollment_id1.hex[:3]
                                               ]  # No change

        # Actually reply enrollment with force
        enrollment_id3 = await run_submit(extra_args="--force")
        assert await run_review_pendings(extra_args="--list-only"
                                         ) == [enrollment_id3.hex[:3]]

        # Reject enrollment
        await run_review_pendings(extra_args=f"--reject {enrollment_id3.hex}")
        assert await run_review_pendings(extra_args="--list-only") == []

        # Accept enrollment
        enrollment_id4 = await run_submit()
        await run_review_pendings(
            extra_args=
            f"--accept {enrollment_id4.hex} --pki-extra-trust-root {mocked_parsec_ext_smartcard.default_trust_root_path}"
        )
        assert await run_review_pendings(extra_args="--list-only") == []

        # It is no longer possible to do another enrollment with the same certificate (until the user is revoked)
        result = await run_submit(check_result=False)
        assert (
            f"The certificate `{mocked_parsec_ext_smartcard.default_x509_certificate.certificate_sha1.hex()}` has already been enrolled"
            in result.output)

        # Reject/Accept not possible against unknown/cancelled/accepted enrollments
        for extra_args in (
                # Unknown
                f"--reject e499f9aed05e4287875a177909d62d90",
                f"--accept e499f9aed05e4287875a177909d62d90",
                # Already Cancelled
                f"--reject {enrollment_id1.hex[:3]}",
                f"--accept {enrollment_id1.hex[:3]}",
                # Already Rejected
                f"--reject {enrollment_id3.hex[:3]}",
                f"--accept {enrollment_id3.hex[:3]}",
                # Already Accepted
                f"--reject {enrollment_id4.hex[:3]}",
                f"--accept {enrollment_id4.hex[:3]}",
        ):
            result = await run_review_pendings(extra_args=extra_args,
                                               check_result=False)
            assert result.exit_code == 1
            assert "Additional --accept/--reject elements not used" in result.output

        # Poll to handle the accepted enrollments, and discard the non-pendings ones
        ids = await run_poll(
            extra_args=
            f"--finalize {enrollment_id4.hex[:3]} --pki-extra-trust-root {mocked_parsec_ext_smartcard.default_trust_root_path}"
        )
        assert set(ids) == {
            enrollment_id1.hex[:3], enrollment_id3.hex[:3],
            enrollment_id4.hex[:3]
        }

        # Now we should have a new local device !
        result = await _cli_invoke_in_thread(
            f"core list_devices --config-dir={config_dir.as_posix()}")
        assert result.exit_code == 0
        assert result.output.startswith("Found 2 device(s)")
        assert "CoolOrg: John Doe <*****@*****.**> @ PC1" in result.output

        # And all the enrollments should have been taken care of
        assert await run_poll() == []
Ejemplo n.º 27
0
async def test_export_recovery_device(
    gui,
    aqtbot,
    monkeypatch,
    core_config,
    running_backend,
    autoclose_dialog,
    catch_export_recovery_widget,
    tmp_path,
    alice,
    kind,
):
    PASSWORD = "******"
    save_device_with_password_in_config(core_config.config_dir, alice,
                                        PASSWORD)

    with monkeypatch.context() as m:
        m.setattr(
            "parsec.core.gui.main_window.ask_question",
            lambda *args, **kwargs: translate("ACTION_CREATE_RECOVERY_DEVICE"),
        )
        aqtbot.key_click(gui, "i", QtCore.Qt.ControlModifier, 200)

    exp_w = await catch_export_recovery_widget()
    assert exp_w

    assert isinstance(exp_w.current_page, DeviceRecoveryExportPage1Widget)
    assert not exp_w.button_validate.isEnabled()

    assert exp_w.current_page.combo_devices.count() == 1
    assert exp_w.current_page.combo_devices.currentData() == alice.slug

    exp_w.current_page.label_file_path.setText(str(tmp_path))

    exp_w.current_page._check_infos()

    monkeypatch.setattr(
        "parsec.core.gui.device_recovery_export_widget.get_text_input",
        lambda *args, **kwargs: PASSWORD,
    )

    if kind == "ok":
        aqtbot.mouse_click(exp_w.button_validate, QtCore.Qt.LeftButton)

        def _page2_shown():
            assert isinstance(exp_w.current_page,
                              DeviceRecoveryExportPage2Widget)

        await aqtbot.wait_until(_page2_shown)

        recovery_file = Path(exp_w.current_page.label_file_path.text())

        assert recovery_file.is_file()
        assert recovery_file.stat().st_size > 0

        assert len(exp_w.current_page.edit_passphrase.toPlainText()) > 0

        assert exp_w.button_validate.isEnabled()
        aqtbot.mouse_click(exp_w.button_validate, QtCore.Qt.LeftButton)

    elif kind == "offline":
        with running_backend.offline():
            aqtbot.mouse_click(exp_w.button_validate, QtCore.Qt.LeftButton)

            def _error_shown():
                assert autoclose_dialog.dialogs == [
                    ("Error", translate("EXPORT_KEY_BACKEND_OFFLINE"))
                ]

            await aqtbot.wait_until(_error_shown)

    elif kind == "already_exists":
        file_name = get_recovery_device_file_name(alice)
        (tmp_path / file_name).touch()

        aqtbot.mouse_click(exp_w.button_validate, QtCore.Qt.LeftButton)

        def _error_shown():
            assert autoclose_dialog.dialogs == [
                ("Error",
                 translate("TEXT_RECOVERY_DEVICE_FILE_ALREADY_EXISTS"))
            ]

        await aqtbot.wait_until(_error_shown)
Ejemplo n.º 28
0
async def test_full_enrollment(
    aqtbot,
    mocked_parsec_ext_smartcard,
    core_config,
    gui,
    alice,
    running_backend,
    catch_enrollment_accept_check_infos_widget,
    snackbar_catcher,
    monkeypatch,
    autoclose_dialog,
    catch_enrollment_query_widget,
):
    # Add trust root to the configuration
    gui.config = gui.config.evolve(pki_extra_trust_roots={
        mocked_parsec_ext_smartcard.default_trust_root_path
    })
    config_dir = gui.config.config_dir
    alice_password = "******"
    save_device_with_password_in_config(config_dir, alice, alice_password)

    pki_org_addr = BackendPkiEnrollmentAddr.build(
        alice.organization_addr.get_backend_addr(),
        alice.organization_addr.organization_id)
    monkeypatch.setattr(
        "parsec.core.gui.main_window.get_text_input",
        lambda *args, **kwargs: (pki_org_addr.to_url()),
    )

    # Open the PKI enrollment dialog
    aqtbot.key_click(gui, "o", QtCore.Qt.ControlModifier, 200)

    eq_w = await catch_enrollment_query_widget()
    assert eq_w

    aqtbot.mouse_click(eq_w.button_select_cert, QtCore.Qt.LeftButton)

    def _cert_loaded():
        assert not eq_w.label_cert_error.isVisible()
        assert eq_w.widget_user_info.isVisible()
        assert eq_w.button_ask_to_join.isEnabled()
        assert eq_w.button_select_cert.isEnabled()
        assert eq_w.line_edit_user_name.text() == "John Doe"
        assert eq_w.line_edit_user_email.text() == "*****@*****.**"
        assert len(eq_w.line_edit_device.text())

    await aqtbot.wait_until(_cert_loaded)

    aqtbot.mouse_click(eq_w.button_ask_to_join, QtCore.Qt.LeftButton)

    def _request_made():
        assert autoclose_dialog.dialogs == [("", "Your request has been sent")]

    await aqtbot.wait_until(_request_made)

    # Check if the enrollment appears among the list of devices with a PENDING status

    lw = gui.test_get_login_widget()

    def _devices_listed():
        assert lw.widget.layout().count() == 1
        assert lw.widget.layout().itemAt(0) is not None
        assert lw.widget.layout().itemAt(
            0).widget().accounts_widget.layout().count() == 3
        pending = lw.widget.layout().itemAt(
            0).widget().accounts_widget.layout().itemAt(0).widget()
        assert isinstance(pending, EnrollmentPendingButton)
        assert pending.label_org.text(
        ) == alice.organization_addr.organization_id.str
        assert pending.label_name.text() == "John Doe"
        assert pending.label_status.text() == "Pending request"
        assert pending.button_action.isVisible() is False

    await aqtbot.wait_until(_devices_listed)

    # Log in with an admin device

    cw = await gui.test_switch_to_logged_in(alice, alice_password)
    cw.button_user.text() == "Alice"

    assert cw.menu.button_enrollment.isVisible() is True

    # Check if the enrollment request we made appears

    e_w = await gui.test_switch_to_enrollment_widget()

    def _enrollments_listed():
        assert e_w.main_layout.count() == 1
        assert e_w.main_layout.itemAt(0) is not None
        assert isinstance(e_w.main_layout.itemAt(0).widget(), EnrollmentButton)

    await aqtbot.wait_until(_enrollments_listed)

    button = e_w.main_layout.itemAt(0).widget()
    assert isinstance(button, EnrollmentButton)
    assert button.button_accept.isEnabled()
    assert button.button_reject.isEnabled()
    assert button.widget_cert_infos.isVisible()
    assert not button.widget_cert_error.isVisible()
    assert button.label_name.text() == "John Doe"
    assert button.label_email.text() == "*****@*****.**"
    assert button.label_issuer.text() == "My CA"
    assert button.label_cert_validity.text() == "✔ Valid certificate"

    # Accept the enrollment request

    aqtbot.mouse_click(button.button_accept, QtCore.Qt.LeftButton)

    acc_w = await catch_enrollment_accept_check_infos_widget()
    assert acc_w

    assert acc_w.line_edit_user_full_name.text() == "John Doe"
    assert acc_w.line_edit_user_email.text() == "*****@*****.**"
    assert len(acc_w.line_edit_device.text()) > 0

    assert acc_w.combo_profile.currentData() is None
    assert acc_w.button_create_user.isEnabled() is False

    acc_w.combo_profile.setCurrentIndex(2)

    def _button_create_user_enabled():
        acc_w.button_create_user.isEnabled() is True

    await aqtbot.wait_until(_button_create_user_enabled)

    aqtbot.mouse_click(acc_w.button_create_user, QtCore.Qt.LeftButton)

    await aqtbot.wait_until(lambda: snackbar_catcher.snackbars == [(
        "INFO", translate("TEXT_ENROLLMENT_ACCEPT_SUCCESS"))])
    snackbar_catcher.reset()

    monkeypatch.setattr("parsec.core.gui.users_widget.ensure_string_size",
                        lambda s, size, font: (s[:12] + "..."))

    # Check if the new user appears

    u_w = await gui.test_switch_to_users_widget()
    assert u_w.layout_users.count() == 4

    a_w = u_w.layout_users.itemAt(0).widget()
    assert a_w.label_username.text() == "Adamy McAdam..."
    assert a_w.label_email.text() == "adam@example..."

    j_w = u_w.layout_users.itemAt(3).widget()
    assert j_w.label_username.text() == "John Doe..."
    assert j_w.label_email.text() == "john@example..."

    # Logout and check the status of the enrollment request

    lw = await gui.test_logout_and_switch_to_login_widget()

    def _devices_listed():
        assert lw.widget.layout().count() == 1
        assert lw.widget.layout().itemAt(0) is not None
        assert lw.widget.layout().itemAt(
            0).widget().accounts_widget.layout().count() == 3
        pending = lw.widget.layout().itemAt(
            0).widget().accounts_widget.layout().itemAt(0).widget()
        assert isinstance(pending, EnrollmentPendingButton)
        assert pending.label_org.text(
        ) == alice.organization_addr.organization_id.str
        assert pending.label_name.text() == "John Doe"
        assert pending.label_status.text() == "Request approved"
        assert pending.button_action.isVisible() is True

    await aqtbot.wait_until(_devices_listed)

    # Finalize the enrollment

    pending = lw.widget.layout().itemAt(
        0).widget().accounts_widget.layout().itemAt(0).widget()
    aqtbot.mouse_click(pending.button_action, QtCore.Qt.LeftButton)

    # Check if the new device appears

    def _new_device_listed():
        org_name = alice.organization_addr.organization_id.str
        devices = ["John Doe", "Alicey McAliceFace"]

        assert lw.widget.layout().itemAt(0) is not None
        assert lw.widget.layout().itemAt(
            0).widget().accounts_widget.layout().count() == 3
        for i in [0, 1]:
            device = (lw.widget.layout().itemAt(
                0).widget().accounts_widget.layout().itemAt(i).widget())
            assert isinstance(device, AccountButton)
            assert device.label_name.text() in devices
            devices.remove(device.label_name.text())
            assert device.label_organization.text() == org_name

    await aqtbot.wait_until(_new_device_listed)

    # Log in with the new device

    new_device = None
    for i in [0, 1]:
        device_button = (lw.widget.layout().itemAt(
            0).widget().accounts_widget.layout().itemAt(i).widget())
        if device_button.label_name.text() == "John Doe":
            new_device = device_button.device
            break

    cw = await gui.test_proceed_to_login(new_device)

    assert isinstance(cw, CentralWidget)
    assert cw.button_user.text(
    ) == f"{alice.organization_addr.organization_id.str}\nJohn Doe"
Ejemplo n.º 29
0
async def test_share_workspace(
    aqtbot,
    running_backend,
    logged_gui,
    gui_workspace_sharing,
    autoclose_dialog,
    core_config,
    alice,
    bob,
    adam,
    catch_share_workspace_widget,
    monkeypatch,
    snackbar_catcher,
):

    _, w_w, share_w_w = gui_workspace_sharing

    # 1) Logged as Bob, we share our workspace with Adam

    # Fix the return value of ensure_string_size, because it can depend of the size of the window
    monkeypatch.setattr(
        "parsec.core.gui.workspace_button.ensure_string_size",
        lambda s, size, font: (s[:16] + "..."),
    )

    def _users_listed():
        assert share_w_w.scroll_content.layout().count() == 4

    await aqtbot.wait_until(_users_listed)

    user_w = share_w_w.scroll_content.layout().itemAt(1).widget()
    assert user_w.combo_role.currentIndex() == 0
    assert user_w.user_info.short_user_display == adam.human_handle.label
    user_w.status_timer.setInterval(200)

    snackbar_catcher.reset()

    def _sharing_updated():
        assert snackbar_catcher.snackbars == [(
            "INFO",
            "The workspace <b>Workspace</b> has been shared with <b>Adamy McAdamFace</b>."
        )]

    async with aqtbot.wait_signal(share_w_w.share_success):
        user_w.combo_role.setCurrentIndex(3)

    await aqtbot.wait_until(_sharing_updated)

    async with aqtbot.wait_signal(user_w.status_timer.timeout):

        def _timer_started():
            assert not user_w.label_status.pixmap().isNull()
            assert user_w.status_timer.isActive()

        await aqtbot.wait_until(_timer_started)

    def _timer_stopped():
        assert user_w.label_status.pixmap().isNull()
        assert not user_w.status_timer.isActive()

    await aqtbot.wait_until(_timer_stopped)

    # 2) Sharing info should now be displayed in the workspaces list view

    # We have to be careful about keeping a reference to the parent.
    # Otherwise, it's garbage collected later on and can trigger a
    # sporadic segfault, causing the test to become inconsistent
    parent = share_w_w.parent().parent()
    async with aqtbot.wait_signals([parent.closing, w_w.list_success]):
        parent.reject()

    def _workspace_listed():
        assert w_w.layout_workspaces.count() == 1
        wk_button = w_w.layout_workspaces.itemAt(0).widget()
        assert isinstance(wk_button, WorkspaceButton)
        assert wk_button.name == EntryName("Workspace")
        assert wk_button.label_title.toolTip(
        ) == "Workspace (shared with Adamy McAdamFace)"
        assert wk_button.label_title.text() == "Workspace (share..."
        assert not autoclose_dialog.dialogs

    await aqtbot.wait_until(_workspace_listed)

    # 3) Now loggin as Adam and check the workspaces view

    password = "******"
    save_device_with_password_in_config(core_config.config_dir, adam, password)
    await logged_gui.test_logout()
    await logged_gui.test_proceed_to_login(adam, password)

    w_w = await logged_gui.test_switch_to_workspaces_widget()

    def _workspace_listed():
        assert w_w.layout_workspaces.count() == 1
        wk_button = w_w.layout_workspaces.itemAt(0).widget()
        assert isinstance(wk_button, WorkspaceButton)
        assert wk_button.name == EntryName("Workspace")
        assert not autoclose_dialog.dialogs

    await aqtbot.wait_until(_workspace_listed)

    w_b = w_w.layout_workspaces.itemAt(0).widget()
    assert isinstance(w_b, WorkspaceButton)
    assert w_b.workspace_name == EntryName("Workspace")
    assert w_b.is_owner is False

    # Also check the workspace shared with view

    aqtbot.mouse_click(w_b.button_share, QtCore.Qt.LeftButton)
    share_w_w = await catch_share_workspace_widget()

    def _users_listed():
        assert share_w_w.scroll_content.layout().count() == 4

    await aqtbot.wait_until(_users_listed)

    user_w = share_w_w.scroll_content.layout().itemAt(0).widget()
    assert user_w.user_info.user_id == bob.user_id
    assert user_w.role == WorkspaceRole.OWNER
    assert not user_w.is_current_user
    assert user_w.combo_role.currentIndex() == 4
    assert user_w.isEnabled() is False

    user_w = share_w_w.scroll_content.layout().itemAt(1).widget()
    assert user_w.user_info.user_id == adam.user_id
    assert user_w.role == WorkspaceRole.MANAGER
    assert user_w.is_current_user
    assert user_w.combo_role.currentIndex() == 3
    assert user_w.isEnabled() is False

    user_w = share_w_w.scroll_content.layout().itemAt(2).widget()
    assert user_w.user_info.user_id == alice.user_id
    assert user_w.role is None
    assert not user_w.is_current_user
    assert user_w.combo_role.currentIndex() == 0
    assert user_w.isEnabled() is True
Ejemplo n.º 30
0
def test_load_bad_password(config_dir, alice):
    save_device_with_password_in_config(config_dir, alice, "S3Cr37")

    with pytest.raises(LocalDeviceCryptoError):
        key_file = get_key_file(config_dir, alice)
        load_device_with_password(key_file, "dummy")