async def test_link_file_unknown_org(core_config, gui_factory, autoclose_dialog, running_backend, alice): password = "******" save_device_with_password(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, "UnknownOrg", alice.organization_addr.root_verify_key) file_link = BackendOrganizationFileLinkAddr.build( org_addr, EntryID(), FsPath("/doesntmattereither")) 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") accounts_w = lw.widget.layout().itemAt(0).widget() assert accounts_w assert isinstance(accounts_w, LoginPasswordInputWidget)
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)
def goto_file_clicked(self): file_link = get_text_input( self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_TITLE"), _("TEXT_WORKSPACE_GOTO_FILE_LINK_INSTRUCTIONS"), placeholder=_("TEXT_WORKSPACE_GOTO_FILE_LINK_PLACEHOLDER"), default_text="", button_text=_("ACTION_GOTO_FILE_LINK"), ) if not file_link: return url = None try: url = BackendOrganizationFileLinkAddr.from_url(file_link) except ValueError as exc: show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_INVALID_LINK"), exception=exc) return for widget in self._iter_workspace_buttons(): if widget.workspace_fs.workspace_id == url.workspace_id: self.load_workspace(widget.workspace_fs, path=url.path, selected=True) return show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_WORKSPACE_NOT_FOUND"))
async def test_link_file_invalid_workspace( aqtbot, running_backend, backend, autoclose_dialog, logged_gui_with_files, bob, monkeypatch, bob_available_device, ): logged_gui, w_w, f_w = logged_gui_with_files url = BackendOrganizationFileLinkAddr.build( f_w.core.device.organization_addr, "not_a_workspace", "/dir1") monkeypatch.setattr( "parsec.core.gui.main_window.list_available_devices", lambda *args, **kwargs: [bob_available_device], ) await aqtbot.run(logged_gui.add_instance, str(url)) def _assert_dialogs(): assert len(autoclose_dialog.dialogs) == 1 assert autoclose_dialog.dialogs == [("Error", translate("TEXT_INVALID_URL"))] await aqtbot.wait_until(_assert_dialogs)
async def test_link_file_disconnected_cancel_login( aqtbot, running_backend, backend, autoclose_dialog, logged_gui_with_files, bob, monkeypatch, bob_available_device, ): gui, w_w, f_w = logged_gui_with_files url = BackendOrganizationFileLinkAddr.build( f_w.core.device.organization_addr, f_w.workspace_fs.workspace_id, "/dir1") monkeypatch.setattr( "parsec.core.gui.main_window.list_available_devices", lambda *args, **kwargs: [bob_available_device], ) # Log out and send link await gui.test_logout_and_switch_to_login_widget() await aqtbot.run(gui.add_instance, str(url)) def _assert_dialogs(): assert len(autoclose_dialog.dialogs) == 1 assert autoclose_dialog.dialogs == [( "", translate("TEXT_FILE_LINK_PLEASE_LOG_IN_organization").format( organization=bob.organization_id), )] await aqtbot.wait_until(_assert_dialogs) # Assert login widget is displayed lw = gui.test_get_login_widget() def _password_widget_shown(): assert isinstance(lw.widget.layout().itemAt(0).widget(), LoginPasswordInputWidget) password_w = lw.widget.layout().itemAt(0).widget() assert password_w.button_login.isEnabled() await aqtbot.wait_until(_password_widget_shown) password_w = lw.widget.layout().itemAt(0).widget() # Cancel login async with aqtbot.wait_signal(lw.login_canceled): await aqtbot.mouse_click(password_w.button_back, QtCore.Qt.LeftButton) await gui.test_switch_to_logged_in(bob) # Assert previous file link is not followed f_w = gui.test_get_files_widget() def _folder_ready(): assert not f_w.isVisible() await aqtbot.wait_until(_folder_ready)
async def test_link_file_invalid_path( aqtbot, running_backend, backend, autoclose_dialog, logged_gui_with_files, bob, monkeypatch, bob_available_device, ): logged_gui, w_w, f_w = logged_gui_with_files url = BackendOrganizationFileLinkAddr.build( f_w.core.device.organization_addr, f_w.workspace_fs.workspace_id, "/not_a_valid_path") monkeypatch.setattr( "parsec.core.gui.main_window.list_available_devices", lambda *args, **kwargs: [bob_available_device], ) await aqtbot.run(logged_gui.add_instance, str(url)) def _assert_dialogs(): assert len(autoclose_dialog.dialogs) == 1 assert autoclose_dialog.dialogs == [ ("Error", translate("TEXT_FILE_GOTO_LINK_NOT_FOUND")) ] await aqtbot.wait_until(_assert_dialogs) assert logged_gui.tab_center.count() == 1
async def test_link_file( aqtbot, running_backend, backend, autoclose_dialog, logged_gui_with_files, bob, monkeypatch, bob_available_device, ): logged_gui, w_w, f_w = logged_gui_with_files url = BackendOrganizationFileLinkAddr.build( f_w.core.device.organization_addr, f_w.workspace_fs.workspace_id, f_w.current_directory) monkeypatch.setattr( "parsec.core.gui.main_window.list_available_devices", lambda *args, **kwargs: [bob_available_device], ) await aqtbot.run(logged_gui.add_instance, str(url)) def _folder_ready(): assert f_w.isVisible() assert f_w.table_files.rowCount() == 2 folder = f_w.table_files.item(1, 1) assert folder assert folder.text() == "dir1" await aqtbot.wait_until(_folder_ready) assert logged_gui.tab_center.count() == 1
def goto_file_clicked(self): file_link = get_text_input( self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_TITLE"), _("TEXT_WORKSPACE_GOTO_FILE_LINK_INSTRUCTIONS"), placeholder=_("TEXT_WORKSPACE_GOTO_FILE_LINK_PLACEHOLDER"), default_text="", button_text=_("ACTION_GOTO_FILE_LINK"), ) if not file_link: return try: addr = BackendOrganizationFileLinkAddr.from_url(file_link, allow_http_redirection=True) except ValueError as exc: show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_INVALID_LINK"), exception=exc) return button = self.get_workspace_button(addr.workspace_id) if button is not None: try: path = button.workspace_fs.decrypt_file_link_path(addr) except ValueError as exc: show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_INVALID_LINK"), exception=exc) return self.load_workspace(button.workspace_fs, path=path, selected=True) return show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_WORKSPACE_NOT_FOUND"))
def on_get_file_path_clicked(self): files = self.table_files.selected_files() if len(files) != 1: return url = BackendOrganizationFileLinkAddr.build( self.core.device.organization_addr, self.workspace_fs.workspace_id, self.current_directory / files[0].name, ) desktop.copy_to_clipboard(str(url)) show_info(self, _("TEXT_FILE_LINK_COPIED_TO_CLIPBOARD"))
def generate_file_link(self, path: AnyPath) -> BackendOrganizationFileLinkAddr: """ Raises: Nothing """ workspace_entry = self.get_workspace_entry() encrypted_path = workspace_entry.key.encrypt( str(FsPath(path)).encode("utf-8")) return BackendOrganizationFileLinkAddr.build( organization_addr=self.device.organization_addr, workspace_id=workspace_entry.id, encrypted_path=encrypted_path, )
def test_build_addrs(): backend_addr = BackendAddr.from_url(BackendAddrTestbed.url) assert backend_addr.hostname == "parsec.cloud.com" assert backend_addr.port == 443 assert backend_addr.use_ssl is True organization_id = OrganizationID("MyOrg") root_verify_key = SigningKey.generate().verify_key organization_addr = BackendOrganizationAddr.build( backend_addr=backend_addr, organization_id=organization_id, root_verify_key=root_verify_key ) assert organization_addr.organization_id == organization_id assert organization_addr.root_verify_key == root_verify_key organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build( backend_addr=backend_addr, organization_id=organization_id, token="a0000000000000000000000000000001", ) assert organization_bootstrap_addr.token == "a0000000000000000000000000000001" assert organization_bootstrap_addr.organization_id == organization_id organization_bootstrap_addr2 = BackendOrganizationBootstrapAddr.build( backend_addr=backend_addr, organization_id=organization_id, token=None ) assert organization_bootstrap_addr2.organization_id == organization_id assert organization_bootstrap_addr2.token == "" organization_file_link_addr = BackendOrganizationFileLinkAddr.build( organization_addr=organization_addr, workspace_id=EntryID.from_hex("2d4ded12-7406-4608-833b-7f57f01156e2"), encrypted_path=b"<encrypted_payload>", ) assert organization_file_link_addr.workspace_id == EntryID.from_hex( "2d4ded12-7406-4608-833b-7f57f01156e2" ) assert organization_file_link_addr.encrypted_path == b"<encrypted_payload>" invitation_addr = BackendInvitationAddr.build( backend_addr=backend_addr, organization_id=organization_id, invitation_type=InvitationType.USER, token=InvitationToken.from_hex("a0000000000000000000000000000001"), ) assert invitation_addr.organization_id == organization_id assert invitation_addr.token == InvitationToken.from_hex("a0000000000000000000000000000001") assert invitation_addr.invitation_type == InvitationType.USER
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)