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)
async def finalize(self) -> "PkiEnrollmentFinalizedCtx": """ Raises: PkiEnrollmentCertificateNotFoundError PkiEnrollmentCertificateCryptoError PkiEnrollmentCertificateError PkiEnrollmentCertificatePinCodeUnavailableError PkiEnrollmentLocalPendingCryptoError """ signing_key, private_key = await pki_enrollment_load_local_pending_secret_part( config_dir=self.config_dir, enrollment_id=self.enrollment_id) # Create the local device organization_addr = BackendOrganizationAddr.build( backend_addr=BackendAddr(self.addr.hostname, self.addr.port, self.addr.use_ssl), organization_id=self.addr.organization_id, root_verify_key=self.accept_payload.root_verify_key, ) new_device = generate_new_device( organization_addr=organization_addr, device_id=self.accept_payload.device_id, profile=self.accept_payload.profile, human_handle=self.accept_payload.human_handle, device_label=self.accept_payload.device_label, signing_key=signing_key, private_key=private_key, ) return PkiEnrollmentFinalizedCtx( config_dir=self.config_dir, enrollment_id=self.enrollment_id, new_device=new_device, x509_certificate=self.x509_certificate, )
async def bootstrap_organization( cmds: APIV1_BackendAnonymousCmds, human_handle: Optional[HumanHandle], device_label: Optional[str], ) -> LocalDevice: root_signing_key = SigningKey.generate() root_verify_key = root_signing_key.verify_key organization_addr = BackendOrganizationAddr.build( backend_addr=cmds.addr, organization_id=cmds.addr.organization_id, root_verify_key=root_verify_key, ) device = generate_new_device( organization_addr=organization_addr, profile=UserProfile.ADMIN, human_handle=human_handle, device_label=device_label, ) now = pendulum_now() user_certificate = UserCertificateContent( author=None, timestamp=now, user_id=device.user_id, human_handle=device.human_handle, public_key=device.public_key, profile=device.profile, ) redacted_user_certificate = user_certificate.evolve(human_handle=None) device_certificate = DeviceCertificateContent( author=None, timestamp=now, device_id=device.device_id, device_label=device.device_label, verify_key=device.verify_key, ) redacted_device_certificate = device_certificate.evolve(device_label=None) user_certificate = user_certificate.dump_and_sign(root_signing_key) redacted_user_certificate = redacted_user_certificate.dump_and_sign( root_signing_key) device_certificate = device_certificate.dump_and_sign(root_signing_key) redacted_device_certificate = redacted_device_certificate.dump_and_sign( root_signing_key) rep = await cmds.organization_bootstrap( organization_id=cmds.addr.organization_id, bootstrap_token=cmds.addr.token, root_verify_key=root_verify_key, user_certificate=user_certificate, device_certificate=device_certificate, redacted_user_certificate=redacted_user_certificate, redacted_device_certificate=redacted_device_certificate, ) _check_rep(rep, step_name="organization bootstrap") return device
async def test_handshake_unknown_organization(running_backend, coolorg): unknown_org_addr = BackendOrganizationAddr.build( backend_addr=coolorg.addr, organization_id="dummy", root_verify_key=coolorg.root_verify_key ) with pytest.raises(BackendConnectionRefused) as exc: async with apiv1_backend_anonymous_cmds_factory(unknown_org_addr) as cmds: await cmds.ping() assert str(exc.value) == "Invalid handshake information"
async def do_claim_user( self, requested_device_label: Optional[DeviceLabel], requested_human_handle: Optional[HumanHandle], ) -> LocalDevice: # User&device keys are generated here and kept in memory until the end of # the enrollment process. This mean we can lost it if something goes wrong. # This has no impact until step 4 (somewhere between data exchange and # confirmation exchange steps) where greeter upload our certificates in # the server. # This is considered acceptable given 1) the error window is small and # 2) if this occurs the inviter can revoke the user and retry the # enrollment process to fix this private_key = PrivateKey.generate() signing_key = SigningKey.generate() try: payload = InviteUserData( requested_device_label=requested_device_label, requested_human_handle=requested_human_handle, public_key=private_key.public_key, verify_key=signing_key.verify_key, ).dump_and_encrypt(key=self._shared_secret_key) except DataError as exc: raise InviteError( "Cannot generate InviteUserData payload") from exc rep = await self._cmds.invite_4_claimer_communicate(payload=payload) _check_rep(rep, step_name="step 4 (data exchange)") rep = await self._cmds.invite_4_claimer_communicate(payload=b"") _check_rep(rep, step_name="step 4 (confirmation exchange)") try: confirmation = InviteUserConfirmation.decrypt_and_load( rep["payload"], key=self._shared_secret_key) except DataError as exc: raise InviteError( "Invalid InviteUserConfirmation payload provided by peer" ) from exc organization_addr = BackendOrganizationAddr.build( backend_addr=self._cmds.addr.get_backend_addr(), organization_id=self._cmds.addr.organization_id, root_verify_key=confirmation.root_verify_key, ) new_device = generate_new_device( organization_addr=organization_addr, device_id=confirmation.device_id, device_label=confirmation.device_label, human_handle=confirmation.human_handle, profile=confirmation.profile, private_key=private_key, signing_key=signing_key, ) return new_device
async def bootstrap_organization( addr: BackendOrganizationBootstrapAddr, human_handle: Optional[HumanHandle], device_label: Optional[DeviceLabel], ) -> LocalDevice: root_signing_key = SigningKey.generate() root_verify_key = root_signing_key.verify_key organization_addr = BackendOrganizationAddr.build( backend_addr=addr.get_backend_addr(), organization_id=addr.organization_id, root_verify_key=root_verify_key, ) device = generate_new_device( organization_addr=organization_addr, profile=UserProfile.ADMIN, human_handle=human_handle, device_label=device_label, ) timestamp = device.timestamp() user_certificate = UserCertificateContent( author=None, timestamp=timestamp, user_id=device.user_id, human_handle=device.human_handle, public_key=device.public_key, profile=device.profile, ) redacted_user_certificate = user_certificate.evolve(human_handle=None) device_certificate = DeviceCertificateContent( author=None, timestamp=timestamp, device_id=device.device_id, device_label=device.device_label, verify_key=device.verify_key, ) redacted_device_certificate = device_certificate.evolve(device_label=None) user_certificate = user_certificate.dump_and_sign(root_signing_key) redacted_user_certificate = redacted_user_certificate.dump_and_sign(root_signing_key) device_certificate = device_certificate.dump_and_sign(root_signing_key) redacted_device_certificate = redacted_device_certificate.dump_and_sign(root_signing_key) rep = await failsafe_organization_bootstrap( addr=addr, root_verify_key=root_verify_key, user_certificate=user_certificate, device_certificate=device_certificate, redacted_user_certificate=redacted_user_certificate, redacted_device_certificate=redacted_device_certificate, ) _check_rep(rep, step_name="organization bootstrap") return device
async def do_claim_user( self, requested_device_label: Optional[str], requested_human_handle: Optional[HumanHandle]) -> LocalDevice: private_key = PrivateKey.generate() signing_key = SigningKey.generate() try: payload = InviteUserData( requested_device_label=requested_device_label, requested_human_handle=requested_human_handle, public_key=private_key.public_key, verify_key=signing_key.verify_key, ).dump_and_encrypt(key=self._shared_secret_key) except DataError as exc: raise InviteError( "Cannot generate InviteUserData payload") from exc rep = await self._cmds.invite_4_claimer_communicate(payload=payload) if rep["status"] == "invalid_state": raise InvitePeerResetError() elif rep["status"] != "ok": raise InviteError( f"Backend error during step 4 (data exchange): {rep}") rep = await self._cmds.invite_4_claimer_communicate(payload=b"") if rep["status"] == "invalid_state": raise InvitePeerResetError() elif rep["status"] != "ok": raise InviteError( f"Backend error during step 4 (confirmation exchange): {rep}") try: confirmation = InviteUserConfirmation.decrypt_and_load( rep["payload"], key=self._shared_secret_key) except DataError as exc: raise InviteError( "Invalid InviteUserConfirmation payload provided by peer" ) from exc organization_addr = BackendOrganizationAddr.build( backend_addr=self._cmds.addr, organization_id=self._cmds.addr.organization_id, root_verify_key=confirmation.root_verify_key, ) new_device = generate_new_device( organization_addr=organization_addr, device_id=confirmation.device_id, device_label=confirmation.device_label, human_handle=confirmation.human_handle, profile=confirmation.profile, private_key=private_key, signing_key=signing_key, ) return new_device
async def test_handshake_rvk_mismatch(running_backend, coolorg, otherorg): bad_rvk_org_addr = BackendOrganizationAddr.build( backend_addr=coolorg.addr, organization_id=coolorg.organization_id, root_verify_key=otherorg.root_verify_key, ) with pytest.raises(BackendConnectionRefused) as exc: async with apiv1_backend_anonymous_cmds_factory(bad_rvk_org_addr) as cmds: await cmds.ping() assert str(exc.value) == "Root verify key for organization differs between client and server"
async def test_handshake_unknown_organization(running_backend, alice): unknown_org_addr = BackendOrganizationAddr.build( backend_addr=alice.organization_addr.get_backend_addr(), organization_id=OrganizationID("dummy"), root_verify_key=alice.organization_addr.root_verify_key, ) with pytest.raises(BackendConnectionRefused) as exc: async with backend_authenticated_cmds_factory( unknown_org_addr, alice.device_id, alice.signing_key) as cmds: await cmds.ping() assert str(exc.value) == "Invalid handshake information"
async def test_handshake_rvk_mismatch(running_backend, alice, otherorg): bad_rvk_org_addr = BackendOrganizationAddr.build( backend_addr=alice.organization_addr, organization_id=alice.organization_id, root_verify_key=otherorg.root_verify_key, ) with pytest.raises(BackendConnectionRefused) as exc: async with backend_authenticated_cmds_factory( bad_rvk_org_addr, alice.device_id, alice.signing_key ) as cmds: await cmds.ping() assert str(exc.value) == "Root verify key for organization differs between client and server"
def test_backend_organization_addr_good(base_url, expected, verify_key): org = OrganizationID("org") backend_addr = BackendAddr.from_url(base_url) addr = BackendOrganizationAddr.build(backend_addr, organization_id=org, root_verify_key=verify_key) assert addr.hostname == "foo" assert addr.port == expected["port"] assert addr.use_ssl == expected["ssl"] assert addr.organization_id == org assert addr.root_verify_key == verify_key addr2 = BackendOrganizationAddr.from_url(addr.to_url()) assert addr == addr2
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 do_claim_device( self, requested_device_label: Optional[str]) -> LocalDevice: signing_key = SigningKey.generate() try: payload = InviteDeviceData( requested_device_label=requested_device_label, verify_key=signing_key.verify_key).dump_and_encrypt( key=self._shared_secret_key) except DataError as exc: raise InviteError( "Cannot generate InviteDeviceData payload") from exc rep = await self._cmds.invite_4_claimer_communicate(payload=payload) _check_rep(rep, step_name="step 4 (data exchange)") rep = await self._cmds.invite_4_claimer_communicate(payload=b"") _check_rep(rep, step_name="step 4 (confirmation exchange)") try: confirmation = InviteDeviceConfirmation.decrypt_and_load( rep["payload"], key=self._shared_secret_key) except DataError as exc: raise InviteError( "Invalid InviteDeviceConfirmation payload provided by peer" ) from exc organization_addr = BackendOrganizationAddr.build( backend_addr=self._cmds.addr, organization_id=self._cmds.addr.organization_id, root_verify_key=confirmation.root_verify_key, ) return LocalDevice( organization_addr=organization_addr, device_id=confirmation.device_id, device_label=confirmation.device_label, human_handle=confirmation.human_handle, profile=confirmation.profile, private_key=confirmation.private_key, signing_key=signing_key, user_manifest_id=confirmation.user_manifest_id, user_manifest_key=confirmation.user_manifest_key, local_symkey=SecretKey.generate(), )
def _correct_local_device_backend_addr(device): organization_addr = BackendOrganizationAddr.build( backend_addr, organization_id=device.organization_addr.organization_id, root_verify_key=device.organization_addr.root_verify_key, ) return LocalDevice( organization_addr=organization_addr, device_id=device.device_id, device_label=device.device_label, human_handle=device.human_handle, signing_key=device.signing_key, private_key=device.private_key, profile=device.profile, user_manifest_id=device.user_manifest_id, user_manifest_key=device.user_manifest_key, local_symkey=device.local_symkey, )