async def _run_claimer(): async with backend_invited_cmds_factory(addr=invitation_addr) as cmds: initial_ctx = await claimer_retrieve_info(cmds) assert isinstance(initial_ctx, UserClaimInitialCtx) assert initial_ctx.claimer_email == claimer_email assert initial_ctx.greeter_user_id == alice.user_id assert initial_ctx.greeter_human_handle == alice.human_handle in_progress_ctx = await initial_ctx.do_wait_peer() choices = in_progress_ctx.generate_greeter_sas_choices(size=4) assert len(choices) == 4 assert in_progress_ctx.greeter_sas in choices greeter_sas = await oob_recv.receive() assert greeter_sas == in_progress_ctx.greeter_sas in_progress_ctx = await in_progress_ctx.do_signify_trust() await oob_send.send(in_progress_ctx.claimer_sas) in_progress_ctx = await in_progress_ctx.do_wait_peer_trust() nonlocal new_device new_device = await in_progress_ctx.do_claim_user( requested_device_label=requested_device_label, requested_human_handle=requested_human_handle, ) assert isinstance(new_device, LocalDevice) # User storage should be populated with non-speculative user manifest # before save the device await user_storage_non_speculative_init( data_base_dir=data_base_dir, device=new_device)
async def _run_claimer(): nonlocal greeter_sas nonlocal claimer_sas async with backend_invited_cmds_factory( addr=gdi_w.invite_addr) as cmds: await start_claimer.wait() initial_ctx = await claimer_retrieve_info(cmds) in_progress_ctx = await initial_ctx.do_wait_peer() greeter_sas = in_progress_ctx.greeter_sas greeter_sas_available.set() await start_claimer_trust.wait() in_progress_ctx = await in_progress_ctx.do_signify_trust() claimer_sas = in_progress_ctx.claimer_sas claimer_sas_available.set() in_progress_ctx = await in_progress_ctx.do_wait_peer_trust() await start_claimer_claim_user.wait() await in_progress_ctx.do_claim_device( requested_device_label=requested_device_label) claimer_done.set()
async def _reset_claimer(self): async with backend_invited_cmds_factory(addr=self.invitation_addr) as cmds: claimer_initial_ctx = await claimer_retrieve_info(cmds) async with trio.open_nursery() as nursery: nursery.start_soon(claimer_initial_ctx.do_wait_peer) yield nursery.cancel_scope.cancel()
async def _run_claimer(): async with backend_invited_cmds_factory(addr=invitation_addr) as cmds: initial_ctx = await claimer_retrieve_info(cmds) assert isinstance(initial_ctx, DeviceClaimInitialCtx) assert initial_ctx.greeter_user_id == alice.user_id assert initial_ctx.greeter_human_handle == alice.human_handle in_progress_ctx = await initial_ctx.do_wait_peer() choices = in_progress_ctx.generate_greeter_sas_choices(size=4) assert len(choices) == 4 assert in_progress_ctx.greeter_sas in choices greeter_sas = await oob_recv.receive() assert greeter_sas == in_progress_ctx.greeter_sas in_progress_ctx = await in_progress_ctx.do_signify_trust() await oob_send.send(in_progress_ctx.claimer_sas) in_progress_ctx = await in_progress_ctx.do_wait_peer_trust() nonlocal new_device new_device = await in_progress_ctx.do_claim_device( requested_device_label=requested_device_label) assert isinstance(new_device, LocalDevice)
async def _claim_invitation(config, addr, password): async with backend_invited_cmds_factory(addr=addr) as cmds: try: async with spinner("Retrieving invitation info"): initial_ctx = await claimer_retrieve_info(cmds) except BackendConnectionRefused: raise RuntimeError("Invitation not found") if initial_ctx.greeter_human_handle: display_greeter = click.style(initial_ctx.greeter_human_handle, fg="yellow") else: display_greeter = click.style(initial_ctx.greeter_user_id, fg="yellow") click.echo(f"Invitation greeter: {display_greeter}") while True: try: if isinstance(initial_ctx, DeviceClaimInitialCtx): new_device = await _do_claim_device(initial_ctx) else: assert isinstance(initial_ctx, UserClaimInitialCtx) new_device = await _do_claim_user(initial_ctx) if new_device: break except InviteError as exc: click.secho(str(exc), fg="red") click.secho("Restarting the invitation process", fg="red") device_display = click.style(new_device.slughash, fg="yellow") with operation(f"Saving device {device_display}"): save_device_with_password(config.config_dir, new_device, password)
async def _run_claimer(): async with backend_invited_cmds_factory(addr=invitation_addr) as cmds: initial_ctx = await claimer_retrieve_info(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() await in_progress_ctx.do_claim_user(requested_device_label=None, requested_human_handle=None)
async def test_handshake_unknown_organization(running_backend, coolorg): invitation_addr = BackendInvitationAddr.build( backend_addr=running_backend.addr, organization_id=coolorg.organization_id, invitation_type=InvitationType.DEVICE, token=uuid4(), ) with pytest.raises(BackendInvitationNotFound) as exc: async with backend_invited_cmds_factory(invitation_addr) as cmds: await cmds.ping() assert str(exc.value) == "Invalid handshake: Invitation not found"
async def test_handshake_already_used_invitation(running_backend, coolorg, invitation_addr, alice): await running_backend.backend.invite.delete( organization_id=alice.organization_id, greeter=alice.user_id, token=invitation_addr.token, on=pendulum_now(), reason=InvitationDeletedReason.CANCELLED, ) with pytest.raises(BackendInvitationAlreadyUsed) as exc: async with backend_invited_cmds_factory(invitation_addr) as cmds: await cmds.ping() assert str(exc.value) == "Invalid handshake: Invitation already deleted"
async def run(self): await self.bootstrap() async with trio.open_nursery() as self.nursery: async with backend_invited_cmds_factory(addr=self.invitation_addr) as self.cmds: next_step = "step_1_start_greet" while True: current_step = next_step next_step = await getattr(self, current_step)() self.steps_done.append(current_step) if next_step is None: break if self.claimer_claim_task: await self.claimer_claim_task.cancel_and_join()
async def run(self): await self.bootstrap() async with backend_invited_cmds_factory( addr=self.invitation_addr) as self.cmds: # Nursery used to run the claimer, should be closed before tearing down # invited cmds otherwise deadlock may occur async with trio.open_nursery() as self.nursery: next_step = "step_1_start_greet" while True: current_step = next_step next_step = await getattr(self, current_step)() self.steps_done.append(current_step) if next_step is None: break if self.claimer_claim_task: await self.claimer_claim_task.cancel_and_join()
async def test_handshake_organization_expired(running_backend, expiredorg, expiredorgalice): invitation = await running_backend.backend.invite.new_for_device( organization_id=expiredorgalice.organization_id, greeter_user_id=expiredorgalice.user_id) invitation_addr = BackendInvitationAddr.build( backend_addr=running_backend.addr, organization_id=expiredorgalice.organization_id, invitation_type=InvitationType.DEVICE, token=invitation.token, ) with pytest.raises(BackendConnectionRefused) as exc: async with backend_invited_cmds_factory(invitation_addr) as cmds: await cmds.ping() assert str(exc.value) == "Trial organization has expired"
async def _claim_invitation( config: CoreConfig, addr: BackendInvitationAddr, save_device_with_selected_auth: Callable ) -> None: async with backend_invited_cmds_factory( addr=addr, keepalive=config.backend_connection_keepalive ) as cmds: try: async with spinner("Retrieving invitation info"): initial_ctx = await claimer_retrieve_info(cmds) except BackendConnectionRefused: raise RuntimeError("Invitation not found") if initial_ctx.greeter_human_handle: display_greeter = click.style(str(initial_ctx.greeter_human_handle), fg="yellow") else: display_greeter = click.style(initial_ctx.greeter_user_id, fg="yellow") click.echo(f"Invitation greeter: {display_greeter}") while True: try: if isinstance(initial_ctx, DeviceClaimInitialCtx): new_device = await _do_claim_device(initial_ctx) else: assert isinstance(initial_ctx, UserClaimInitialCtx) new_device = await _do_claim_user(initial_ctx) if new_device: break except InviteError as exc: click.secho(str(exc), fg="red") click.secho("Restarting the invitation process", fg="red") # Claiming a user means we are it first device, hence we know there # is no existing user manifest (hence our placeholder is non-speculative) if addr.invitation_type == InvitationType.USER: await user_storage_non_speculative_init( data_base_dir=config.data_base_dir, device=new_device ) await save_device_with_selected_auth(config_dir=config.config_dir, device=new_device)
async def test_backend_switch_offline(running_backend, invitation_addr): async with backend_invited_cmds_factory(invitation_addr) as cmds: await cmds.ping() with running_backend.offline(): with pytest.raises(BackendNotAvailable): await cmds.ping()
async def run(self, addr, config): try: async with backend_invited_cmds_factory( addr=addr, keepalive=config.backend_connection_keepalive) as cmds: # Trigger handshake await cmds.ping() r = await self.main_oob_recv.receive() assert r == self.Step.RetrieveInfo try: initial_ctx = await claimer_retrieve_info(cmds) await self.job_oob_send.send( (True, None, initial_ctx.claimer_email)) except Exception as exc: await self.job_oob_send.send((False, exc, None)) r = await self.main_oob_recv.receive() assert r == self.Step.WaitPeer try: in_progress_ctx = await initial_ctx.do_wait_peer() await self.job_oob_send.send((True, None)) except Exception as exc: await self.job_oob_send.send((False, exc)) r = await self.main_oob_recv.receive() assert r == self.Step.GetGreeterSas try: choices = in_progress_ctx.generate_greeter_sas_choices( size=4) await self.job_oob_send.send( (True, None, in_progress_ctx.greeter_sas, choices)) except Exception as exc: await self.job_oob_send.send((False, exc, None, None)) r = await self.main_oob_recv.receive() assert r == self.Step.SignifyTrust try: in_progress_ctx = await in_progress_ctx.do_signify_trust() await self.job_oob_send.send((True, None)) except Exception as exc: await self.job_oob_send.send((False, exc)) r = await self.main_oob_recv.receive() assert r == self.Step.GetClaimerSas await self.job_oob_send.send(in_progress_ctx.claimer_sas) r = await self.main_oob_recv.receive() assert r == self.Step.WaitPeerTrust try: in_progress_ctx = await in_progress_ctx.do_wait_peer_trust( ) await self.job_oob_send.send((True, None)) except Exception as exc: await self.job_oob_send.send((False, exc)) r = await self.main_oob_recv.receive() assert r == self.Step.ClaimUser try: device_label, human_handle = await self.main_oob_recv.receive( ) new_device = await in_progress_ctx.do_claim_user( requested_device_label=device_label, requested_human_handle=human_handle) # Claiming a user means we are it first device, hence we know there # is no existing user manifest (hence our placeholder is non-speculative) await user_storage_non_speculative_init( data_base_dir=config.data_base_dir, device=new_device) await self.job_oob_send.send((True, None, new_device)) except Exception as exc: await self.job_oob_send.send((False, exc, None)) except BackendNotAvailable as exc: raise JobResultError(status="backend-not-available", origin=exc) except BackendInvitationAlreadyUsed as exc: raise JobResultError(status="invitation-already-used", origin=exc) except BackendConnectionRefused as exc: raise JobResultError(status="invitation-not-found", origin=exc) except BackendOutOfBallparkError as exc: raise JobResultError(status="out-of-ballpark", origin=exc)
async def run(self, addr, config): try: async with backend_invited_cmds_factory( addr=addr, keepalive=config.backend_connection_keepalive) as cmds: # Trigger handshake await cmds.ping() r = await self.main_oob_recv.receive() assert r == self.Step.RetrieveInfo try: initial_ctx = await claimer_retrieve_info(cmds) await self.job_oob_send.send( (True, None, initial_ctx.claimer_email)) except Exception as exc: await self.job_oob_send.send((False, exc, None)) r = await self.main_oob_recv.receive() assert r == self.Step.WaitPeer try: in_progress_ctx = await initial_ctx.do_wait_peer() await self.job_oob_send.send((True, None)) except Exception as exc: await self.job_oob_send.send((False, exc)) r = await self.main_oob_recv.receive() assert r == self.Step.GetGreeterSas try: choices = in_progress_ctx.generate_greeter_sas_choices( size=4) await self.job_oob_send.send( (True, None, in_progress_ctx.greeter_sas, choices)) except Exception as exc: await self.job_oob_send.send((False, exc, None, None)) r = await self.main_oob_recv.receive() assert r == self.Step.SignifyTrust try: in_progress_ctx = await in_progress_ctx.do_signify_trust() await self.job_oob_send.send((True, None)) except Exception as exc: await self.job_oob_send.send((False, exc)) r = await self.main_oob_recv.receive() assert r == self.Step.GetClaimerSas await self.job_oob_send.send(in_progress_ctx.claimer_sas) r = await self.main_oob_recv.receive() assert r == self.Step.WaitPeerTrust try: in_progress_ctx = await in_progress_ctx.do_wait_peer_trust( ) await self.job_oob_send.send((True, None)) except Exception as exc: await self.job_oob_send.send((False, exc)) r = await self.main_oob_recv.receive() assert r == self.Step.ClaimUser try: device_label, human_handle = await self.main_oob_recv.receive( ) new_device = await in_progress_ctx.do_claim_user( requested_device_label=device_label, requested_human_handle=human_handle) await self.job_oob_send.send((True, None, new_device)) except Exception as exc: await self.job_oob_send.send((False, exc, None)) except BackendNotAvailable as exc: raise JobResultError(status="backend-not-available", origin=exc) except BackendInvitationAlreadyUsed as exc: raise JobResultError(status="invitation-already-used", origin=exc) except BackendConnectionRefused as exc: raise JobResultError(status="invitation-not-found", origin=exc)
def _cmds_factory(keepalive): return backend_invited_cmds_factory(invitation_addr, keepalive=keepalive)
async def test_ping(running_backend, invitation_addr): async with backend_invited_cmds_factory(invitation_addr) as cmds: rep = await cmds.ping("Hello World !") assert rep == {"status": "ok", "pong": "Hello World !"}
async def test_claimer_handle_reset(backend, running_backend, alice, alice_backend_cmds): invitation = await backend.invite.new_for_device( organization_id=alice.organization_id, greeter_user_id=alice.user_id) invitation_addr = BackendInvitationAddr.build( backend_addr=alice.organization_addr.get_backend_addr(), organization_id=alice.organization_id, invitation_type=InvitationType.DEVICE, token=invitation.token, ) async with backend_invited_cmds_factory( addr=invitation_addr) as claimer_cmds: greeter_initial_ctx = UserGreetInitialCtx(cmds=alice_backend_cmds, token=invitation_addr.token) claimer_initial_ctx = await claimer_retrieve_info(claimer_cmds) claimer_in_progress_ctx = None greeter_in_progress_ctx = None # Step 1 async with real_clock_timeout(): async with trio.open_nursery() as nursery: async def _do_claimer(): nonlocal claimer_in_progress_ctx claimer_in_progress_ctx = await claimer_initial_ctx.do_wait_peer( ) async def _do_greeter(): nonlocal greeter_in_progress_ctx greeter_in_progress_ctx = await greeter_initial_ctx.do_wait_peer( ) nursery.start_soon(_do_claimer) nursery.start_soon(_do_greeter) # Claimer restart the conduit while greeter try to do step 2 async with real_clock_timeout(): async with trio.open_nursery() as nursery: async def _do_claimer(): nonlocal claimer_in_progress_ctx claimer_in_progress_ctx = await claimer_initial_ctx.do_wait_peer( ) nursery.start_soon(_do_claimer) with pytest.raises(InvitePeerResetError): await greeter_in_progress_ctx.do_wait_peer_trust() # Greeter redo step 1 greeter_in_progress_ctx = await greeter_initial_ctx.do_wait_peer( ) # Now do the other way around: greeter restart conduit while claimer try step 2 async with real_clock_timeout(): async with trio.open_nursery() as nursery: async def _do_greeter(): nonlocal greeter_in_progress_ctx greeter_in_progress_ctx = await greeter_initial_ctx.do_wait_peer( ) nursery.start_soon(_do_greeter) with pytest.raises(InvitePeerResetError): await claimer_in_progress_ctx.do_signify_trust() # Claimer redo step 1 claimer_in_progress_ctx = await claimer_initial_ctx.do_wait_peer( )
async def test_backend_closed_cmds(running_backend, invitation_addr): async with backend_invited_cmds_factory(invitation_addr) as cmds: pass with pytest.raises(trio.ClosedResourceError): await cmds.ping()
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 test_invited_cmds_has_right_methods(running_backend, coolorg): async with backend_invited_cmds_factory(coolorg.addr) as cmds: for method_name in INVITED_CMDS: assert hasattr(cmds, method_name) for method_name in ALL_CMDS - INVITED_CMDS: assert not hasattr(cmds, method_name)
async def test_claimer_handle_cancel_event(backend, running_backend, alice, alice_backend_cmds, fail_on_step): invitation = await backend.invite.new_for_device( organization_id=alice.organization_id, greeter_user_id=alice.user_id) invitation_addr = BackendInvitationAddr.build( backend_addr=alice.organization_addr.get_backend_addr(), organization_id=alice.organization_id, invitation_type=InvitationType.DEVICE, token=invitation.token, ) async def _cancel_invitation(): await backend.invite.delete( organization_id=alice.organization_id, greeter=alice.user_id, token=invitation_addr.token, on=pendulum_now(), reason=InvitationDeletedReason.CANCELLED, ) async with backend_invited_cmds_factory( addr=invitation_addr) as claimer_cmds: greeter_initial_ctx = UserGreetInitialCtx(cmds=alice_backend_cmds, token=invitation_addr.token) claimer_initial_ctx = await claimer_retrieve_info(claimer_cmds) claimer_in_progress_ctx = None greeter_in_progress_ctx = None async def _do_claimer(): nonlocal claimer_in_progress_ctx if fail_on_step == "wait_peer": return claimer_in_progress_ctx = await claimer_initial_ctx.do_wait_peer() if fail_on_step == "signify_trust": return claimer_in_progress_ctx = await claimer_in_progress_ctx.do_signify_trust( ) if fail_on_step == "wait_peer_trust": return claimer_in_progress_ctx = await claimer_in_progress_ctx.do_wait_peer_trust( ) async def _do_greeter(): nonlocal greeter_in_progress_ctx if fail_on_step == "wait_peer": return greeter_in_progress_ctx = await greeter_initial_ctx.do_wait_peer() if fail_on_step == "signify_trust": return greeter_in_progress_ctx = await greeter_in_progress_ctx.do_wait_peer_trust( ) if fail_on_step == "wait_peer_trust": return greeter_in_progress_ctx = await greeter_in_progress_ctx.do_signify_trust( ) async with real_clock_timeout(): async with trio.open_nursery() as nursery: nursery.start_soon(_do_claimer) nursery.start_soon(_do_greeter) async with real_clock_timeout(): async with trio.open_nursery() as nursery: async def _do_claimer_wait_peer(): with pytest.raises( BackendInvitationAlreadyUsed) as exc_info: await claimer_initial_ctx.do_wait_peer() assert str( exc_info.value ) == "Invalid handshake: Invitation already deleted" async def _do_claimer_signify_trust(): with pytest.raises( BackendInvitationAlreadyUsed) as exc_info: await claimer_in_progress_ctx.do_signify_trust() assert str( exc_info.value ) == "Invalid handshake: Invitation already deleted" async def _do_claimer_wait_peer_trust(): with pytest.raises( BackendInvitationAlreadyUsed) as exc_info: await claimer_in_progress_ctx.do_wait_peer_trust() assert str( exc_info.value ) == "Invalid handshake: Invitation already deleted" async def _do_claimer_claim_device(): with pytest.raises( BackendInvitationAlreadyUsed) as exc_info: await claimer_in_progress_ctx.do_claim_device( requested_device_label=DeviceLabel( "TheSecretDevice")) assert str( exc_info.value ) == "Invalid handshake: Invitation already deleted" steps = { "wait_peer": _do_claimer_wait_peer, "signify_trust": _do_claimer_signify_trust, "wait_peer_trust": _do_claimer_wait_peer_trust, "claim_device": _do_claimer_claim_device, } _do_claimer = steps[fail_on_step] with backend.event_bus.listen() as spy: nursery.start_soon(_do_claimer) # Be sure that _do_claimer got valid invitations before cancelation await spy.wait_with_timeout( BackendEvent.INVITE_CONDUIT_UPDATED) await _cancel_invitation() await spy.wait_with_timeout( BackendEvent.INVITE_STATUS_CHANGED)
async def test_claimer_handle_command_failure(backend, running_backend, alice, alice_backend_cmds, monkeypatch, fail_on_step): invitation = await backend.invite.new_for_device( organization_id=alice.organization_id, greeter_user_id=alice.user_id) invitation_addr = BackendInvitationAddr.build( backend_addr=alice.organization_addr.get_backend_addr(), organization_id=alice.organization_id, invitation_type=InvitationType.DEVICE, token=invitation.token, ) async def _cancel_invitation(): await backend.invite.delete( organization_id=alice.organization_id, greeter=alice.user_id, token=invitation_addr.token, on=pendulum_now(), reason=InvitationDeletedReason.CANCELLED, ) async with backend_invited_cmds_factory( addr=invitation_addr) as claimer_cmds: greeter_initial_ctx = UserGreetInitialCtx(cmds=alice_backend_cmds, token=invitation_addr.token) claimer_initial_ctx = await claimer_retrieve_info(claimer_cmds) claimer_in_progress_ctx = None greeter_in_progress_ctx = None async def _do_claimer(): nonlocal claimer_in_progress_ctx if fail_on_step == "wait_peer": return claimer_in_progress_ctx = await claimer_initial_ctx.do_wait_peer() if fail_on_step == "signify_trust": return claimer_in_progress_ctx = await claimer_in_progress_ctx.do_signify_trust( ) if fail_on_step == "wait_peer_trust": return claimer_in_progress_ctx = await claimer_in_progress_ctx.do_wait_peer_trust( ) async def _do_greeter(): nonlocal greeter_in_progress_ctx if fail_on_step == "wait_peer": return greeter_in_progress_ctx = await greeter_initial_ctx.do_wait_peer() if fail_on_step == "signify_trust": return greeter_in_progress_ctx = await greeter_in_progress_ctx.do_wait_peer_trust( ) if fail_on_step == "wait_peer_trust": return greeter_in_progress_ctx = await greeter_in_progress_ctx.do_signify_trust( ) async with real_clock_timeout(): async with trio.open_nursery() as nursery: nursery.start_soon(_do_claimer) nursery.start_soon(_do_greeter) deleted_event = trio.Event() async def _send_event(*args, **kwargs): if BackendEvent.INVITE_STATUS_CHANGED in args and ( kwargs.get("status") == InvitationStatus.DELETED): deleted_event.set() await trio.sleep(0) backend.invite._send_event = _send_event monkeypatch.setattr("parsec.backend.postgresql.invite.send_signal", _send_event) async with real_clock_timeout(): await _cancel_invitation() await deleted_event.wait() with pytest.raises(BackendInvitationAlreadyUsed) as exc_info: if fail_on_step == "wait_peer": await claimer_initial_ctx.do_wait_peer() elif fail_on_step == "signify_trust": await claimer_in_progress_ctx.do_signify_trust() elif fail_on_step == "wait_peer_trust": await claimer_in_progress_ctx.do_wait_peer_trust() elif fail_on_step == "claim_device": await claimer_in_progress_ctx.do_claim_device( requested_device_label=DeviceLabel("TheSecretDevice")) else: raise AssertionError(f"Unknown step {fail_on_step}") assert str(exc_info.value ) == "Invalid handshake: Invitation already deleted"