async def test_user_invite_claim_invalid_token(running_backend, backend, alice): new_device_id = DeviceID("zack@pc1") token = generate_invitation_token() bad_token = generate_invitation_token() invite_exception_occured = False claim_exception_occured = False async def _from_alice(): nonlocal invite_exception_occured with pytest.raises(InviteClaimInvalidTokenError) as exc: await invite_and_create_user(alice, new_device_id.user_id, is_admin=False, token=token) assert ( str(exc.value) == f"Invalid claim token provided by peer: `{bad_token}` (was expecting `{token}`)" ) invite_exception_occured = True async def _from_new_device(): nonlocal claim_exception_occured with pytest.raises(InviteClaimError) as exc: await claim_user(alice.organization_addr, new_device_id, token=bad_token) assert ( str(exc.value) == "Cannot claim user: {'reason': 'Invitation creator rejected us.', 'status': 'denied'}" ) claim_exception_occured = True await _invite_and_claim(running_backend, _from_alice, _from_new_device) assert invite_exception_occured assert claim_exception_occured
async def test_invite_claim_3_chained_users(running_backend, backend, alice): # Zeta will be invited by Zoe, Zoe will be invited by Zack new_device_id_1 = DeviceID("zack@pc1") new_device_1 = None token_1 = generate_invitation_token() new_device_id_2 = DeviceID("zoe@pc2") new_device_2 = None token_2 = generate_invitation_token() new_device_id_3 = DeviceID("zeta@pc3") new_device_3 = None token_3 = generate_invitation_token() async def _invite_from_alice(): await invite_and_create_user(alice, new_device_id_1.user_id, token=token_1, is_admin=True) async def _claim_from_1(): nonlocal new_device_1 new_device_1 = await claim_user(alice.organization_addr, new_device_id_1, token=token_1) async def _invite_from_1(): await invite_and_create_user( new_device_1, new_device_id_2.user_id, token=token_2, is_admin=True ) async def _claim_from_2(): nonlocal new_device_2 new_device_2 = await claim_user(alice.organization_addr, new_device_id_2, token=token_2) async def _invite_from_2(): await invite_and_create_user( new_device_2, new_device_id_3.user_id, token=token_3, is_admin=False ) async def _claim_from_3(): nonlocal new_device_3 new_device_3 = await claim_user(alice.organization_addr, new_device_id_3, token=token_3) await _invite_and_claim(running_backend, _invite_from_alice, _claim_from_1) await _invite_and_claim(running_backend, _invite_from_1, _claim_from_2) await _invite_and_claim(running_backend, _invite_from_2, _claim_from_3) assert new_device_1.is_admin assert new_device_2.is_admin assert not new_device_3.is_admin # Now connect as the last user async with backend_authenticated_cmds_factory( new_device_2.organization_addr, new_device_2.device_id, new_device_2.signing_key ) as cmds: await cmds.ping("foo")
async def test_invite_claim_multiple_devices_from_chained_user(running_backend, backend, alice): # The devices are invited from one another new_device_id_1 = DeviceID("zack@pc1") new_device_1 = None token_1 = generate_invitation_token() new_device_id_2 = DeviceID("zack@pc2") new_device_2 = None token_2 = generate_invitation_token() new_device_id_3 = DeviceID("zack@pc3") new_device_3 = None token_3 = generate_invitation_token() async def _invite_from_alice(): await invite_and_create_user(alice, new_device_id_1.user_id, token=token_1, is_admin=True) async def _claim_from_1(): nonlocal new_device_1 new_device_1 = await claim_user(alice.organization_addr, new_device_id_1, token=token_1) async def _invite_from_1(): await invite_and_create_device(new_device_1, new_device_id_2.device_name, token=token_2) async def _claim_from_2(): nonlocal new_device_2 new_device_2 = await claim_device(alice.organization_addr, new_device_id_2, token=token_2) async def _invite_from_2(): await invite_and_create_device(new_device_2, new_device_id_3.device_name, token=token_3) async def _claim_from_3(): nonlocal new_device_3 new_device_3 = await claim_device(alice.organization_addr, new_device_id_3, token=token_3) await _invite_and_claim(running_backend, _invite_from_alice, _claim_from_1) await _invite_and_claim( running_backend, _invite_from_1, _claim_from_2, event_name="device.claimed" ) await _invite_and_claim( running_backend, _invite_from_2, _claim_from_3, event_name="device.claimed" ) # Now connect as the last device async with backend_authenticated_cmds_factory( new_device_3.organization_addr, new_device_3.device_id, new_device_3.signing_key ) as cmds: await cmds.ping("foo")
async def test_invite_claim_device(running_backend, backend, alice): new_device_id = DeviceID(f"{alice.user_id}@NewDevice") new_device = None token = generate_invitation_token() async def _from_alice(): await invite_and_create_device(alice, new_device_id.device_name, token=token) async def _from_new_device(): nonlocal new_device new_device = await claim_device(alice.organization_addr, new_device_id, token=token) await _invite_and_claim(running_backend, _from_alice, _from_new_device, event_name="device.claimed") # Now connect as the new device async with backend_authenticated_cmds_factory( new_device.organization_addr, new_device.device_id, new_device.signing_key) as cmds: await cmds.ping("foo")
async def test_user_invite_claim_cancel_invitation(monitor, running_backend, backend, alice): new_device_id = DeviceID("zack@pc1") token = generate_invitation_token() invite_and_claim_cancel_scope = None async def _from_alice(): nonlocal invite_and_claim_cancel_scope with trio.CancelScope() as invite_and_claim_cancel_scope: await invite_and_create_user(alice, new_device_id.user_id, is_admin=False, token=token) async def _cancel_invite_and_claim(): invite_and_claim_cancel_scope.cancel() await _invite_and_claim(running_backend, _from_alice, _cancel_invite_and_claim) # Now make sure the invitation cannot be used with trio.fail_after(1): with pytest.raises(InviteClaimError) as exc: await claim_user(alice.organization_addr, new_device_id, token=token) assert ( str(exc.value) == "Cannot retrieve invitation creator: User `zack` doesn't exist in backend" )
async def test_device_invite_claim_cancel_invitation(running_backend, backend, alice): new_device_id = DeviceID(f"{alice.user_id}@NewDevice") token = generate_invitation_token() invite_and_claim_cancel_scope = None async def _from_alice(): nonlocal invite_and_claim_cancel_scope with trio.CancelScope() as invite_and_claim_cancel_scope: await invite_and_create_device(alice, new_device_id.device_name, token=token) async def _cancel_invite_and_claim(): invite_and_claim_cancel_scope.cancel() await _invite_and_claim(running_backend, _from_alice, _cancel_invite_and_claim, event_name="device.claimed") # Now make sure the invitation cannot be used with trio.fail_after(1): with pytest.raises(InviteClaimError) as exc: await claim_device(alice.organization_addr, new_device_id, token=token) assert ( str(exc.value) == "Cannot retrieve invitation creator: User `alice@NewDevice` doesn't exist in backend" )
async def test_invite_claim_admin_user(running_backend, backend, alice): new_device_id = DeviceID("zack@pc1") new_device = None token = generate_invitation_token() async def _from_alice(): await invite_and_create_user(alice, new_device_id.user_id, token=token, is_admin=True) async def _from_new_device(): nonlocal new_device new_device = await claim_user(alice.organization_addr, new_device_id, token=token) await _invite_and_claim(running_backend, _from_alice, _from_new_device) assert new_device.is_admin # Now connect as the new user async with backend_authenticated_cmds_factory( new_device.organization_addr, new_device.device_id, new_device.signing_key) as cmds: await cmds.ping("foo")
async def _do_test(): nonlocal device_count device_count += 1 new_device_id = DeviceID(f"{alice.user_id}@newdev{device_count}") token = generate_invitation_token() exception_occured = False async def _from_alice(): await invite_and_create_device(alice, new_device_id.device_name, token=token) async def _from_new_device(): nonlocal exception_occured with pytest.raises(InviteClaimCryptoError): await claim_device(alice.organization_addr, new_device_id, token=token) exception_occured = True await _invite_and_claim(running_backend, _from_alice, _from_new_device, event_name="device.claimed") assert exception_occured
async def test_invite_claim_non_admin_user(running_backend, backend, alice): new_device_id = DeviceID("zack@pc1") new_device = None token = generate_invitation_token() async def _from_alice(): await invite_and_create_user(alice, new_device_id.user_id, token=token, is_admin=False) async def _from_new_device(): nonlocal new_device new_device = await claim_user(alice.organization_addr, new_device_id, token=token) await _invite_and_claim(running_backend, _from_alice, _from_new_device)
def register_device(self): def _run_registration(device_name, token): self.portal.run( _handle_invite_and_create_device, self.register_queue, self.on_registered, self.on_register_error, self.core, device_name, token, ) if not self.line_edit_device_name.text(): show_warning( self, QCoreApplication.translate("RegisterDeviceDialog", "Please enter a device name."), ) return try: token = generate_invitation_token() self.line_edit_device.setText(self.line_edit_device_name.text()) self.line_edit_device.setCursorPosition(0) self.line_edit_token.setText(token) self.line_edit_token.setCursorPosition(0) self.line_edit_url.setText(self.core.device.organization_addr) self.line_edit_url.setCursorPosition(0) self.button_cancel.setFocus() self.widget_registration.show() self.register_thread = threading.Thread( target=_run_registration, args=(self.line_edit_device_name.text(), token)) self.register_thread.start() self.cancel_scope = self.register_queue.get() self.button_cancel.show() self.line_edit_device_name.hide() self.button_register.hide() self.closing_allowed = False except: show_warning( self, QCoreApplication.translate("RegisterDeviceDialog", "Could not register the device."), )
async def _invite_user(config, device, invited_user_id, admin): async with backend_cmds_factory(device.organization_addr, device.device_id, device.signing_key) as cmds: token = generate_invitation_token() organization_addr_display = click.style(device.organization_addr, fg="yellow") token_display = click.style(token, fg="yellow") click.echo(f"Backend url: {organization_addr_display}") click.echo(f"Invitation token: {token_display}") async with spinner("Waiting for invitation reply"): invite_device_id = await invite_and_create_user( device, cmds, invited_user_id, token, admin) display_device = click.style(invite_device_id, fg="yellow") click.echo(f"Device {display_device} has been created")
async def _do_test(): nonlocal device_count device_count += 1 new_device_id = DeviceID(f"user{device_count}@dev") token = generate_invitation_token() exception_occured = False async def _from_alice(): await invite_and_create_user(alice, new_device_id.user_id, token=token, is_admin=False) async def _from_new_device(): nonlocal exception_occured with pytest.raises(InviteClaimCryptoError): await claim_user(alice.organization_addr, new_device_id, token=token) exception_occured = True await _invite_and_claim(running_backend, _from_alice, _from_new_device) assert exception_occured
async def _invite_device(config, device, new_device_name): token = generate_invitation_token() organization_addr_display = click.style(device.organization_addr, fg="yellow") token_display = click.style(token, fg="yellow") click.echo(f"Backend url: {organization_addr_display}") click.echo(f"Invitation token: {token_display}") async with backend_cmds_factory(device.organization_addr, device.device_id, device.signing_key) as cmds: async with spinner("Waiting for invitation reply"): await invite_and_create_device(device, cmds, new_device_name, token) display_device = click.style(f"{device.device_name}@{new_device_name}", fg="yellow") click.echo(f"Device {display_device} is ready !")
async def _invite_user(config, device, invited_user_id, admin): action_addr = BackendOrganizationClaimUserAddr.build( organization_addr=device.organization_addr, user_id=invited_user_id) token = generate_invitation_token() action_addr_display = click.style(action_addr.to_url(), fg="yellow") token_display = click.style(token, fg="yellow") click.echo(f"url: {action_addr_display}") click.echo(f"token: {token_display}") async with spinner("Waiting for invitation reply"): invite_device_id = await invite_and_create_user( device=device, user_id=invited_user_id, token=token, is_admin=admin, keepalive=config.backend_connection_keepalive, ) display_device = click.style(invite_device_id, fg="yellow") click.echo(f"Device {display_device} has been created")
async def _invite_device(config, device, new_device_name): action_addr = BackendOrganizationClaimDeviceAddr.build( organization_addr=device.organization_addr, device_id=DeviceID(f"{device.user_id}@{new_device_name}"), ) token = generate_invitation_token() action_addr_display = click.style(action_addr.to_url(), fg="yellow") token_display = click.style(token, fg="yellow") click.echo(f"url: {action_addr_display}") click.echo(f"token: {token_display}") async with spinner("Waiting for invitation reply"): await invite_and_create_device( device=device, new_device_name=new_device_name, token=token, keepalive=config.backend_connection_keepalive, ) display_device = click.style(f"{device.device_name}@{new_device_name}", fg="yellow") click.echo(f"Device {display_device} is ready !")
def register_user(self): def _run_registration(username, token, is_admin): self.portal.run( _handle_invite_and_create_user, self.register_queue, self.on_registered, self.on_register_error, self.core, username, token, is_admin, ) if not self.line_edit_username.text(): show_warning( self, QCoreApplication.translate("RegisterUserDialog", "Please enter a username.")) return users = self.portal.run(self.core.fs.backend_cmds.user_find, self.line_edit_username.text()) if len(users): show_warning( self, QCoreApplication.translate( "RegisterUserDialog", "A user with the same name already exists."), ) return try: token = generate_invitation_token() self.line_edit_user.setText(self.line_edit_username.text()) self.line_edit_user.setCursorPosition(0) self.line_edit_token.setText(token) self.line_edit_token.setCursorPosition(0) self.line_edit_url.setText(self.core.device.organization_addr) self.line_edit_url.setCursorPosition(0) self.button_cancel.setFocus() self.widget_registration.show() self.register_thread = threading.Thread( target=_run_registration, args=(self.line_edit_username.text(), token, self.checkbox_is_admin.isChecked()), ) self.register_thread.start() self.cancel_scope = self.register_queue.get() self.button_cancel.show() self.line_edit_username.hide() self.checkbox_is_admin.hide() self.button_register.hide() self.closing_allowed = False except: import traceback traceback.print_exc() show_warning( self, QCoreApplication.translate("RegisterUserDialog", "Could not register the user."), )
async def amain( backend_address="ws://*****:*****@laptop", bob_device_id="bob@laptop", other_device_name="pc", alice_workspace="alicews", bob_workspace="bobws", password="******", administrator_token=DEFAULT_ADMINISTRATOR_TOKEN, force=False, ): configure_logging("WARNING") config_dir = get_default_config_dir(os.environ) organization_id = OrganizationID(organization_id) backend_address = BackendAddr(backend_address) alice_device_id = DeviceID(alice_device_id) bob_device_id = DeviceID(bob_device_id) alice_slugid = f"{organization_id}:{alice_device_id}" bob_slugid = f"{organization_id}:{bob_device_id}" # Create organization async with backend_administrator_cmds_factory(backend_address, administrator_token) as cmds: bootstrap_token = await cmds.organization_create(organization_id) organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build( backend_address, organization_id, bootstrap_token ) # Bootstrap organization and Alice user async with backend_anonymous_cmds_factory(organization_bootstrap_addr) as cmds: root_signing_key = SigningKey.generate() root_verify_key = root_signing_key.verify_key organization_addr = organization_bootstrap_addr.generate_organization_addr(root_verify_key) alice_device = generate_new_device(alice_device_id, organization_addr) save_device_with_password(config_dir, alice_device, password, force=force) now = pendulum.now() certified_user = certify_user( None, root_signing_key, alice_device.user_id, alice_device.public_key, now ) certified_device = certify_device( None, root_signing_key, alice_device_id, alice_device.verify_key, now ) await cmds.organization_bootstrap( organization_bootstrap_addr.organization_id, organization_bootstrap_addr.bootstrap_token, root_verify_key, certified_user, certified_device, ) # 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: await core.fs.workspace_create(f"/{alice_workspace}") # Register a new device for Alice token = generate_invitation_token() other_alice_device_id = DeviceID("@".join((alice_device.user_id, other_device_name))) other_alice_slugid = f"{organization_id}:{other_alice_device_id}" async def invite_task(): async with backend_cmds_factory( alice_device.organization_addr, alice_device.device_id, alice_device.signing_key ) as cmds: await invite_and_create_device(alice_device, cmds, other_device_name, token) async def claim_task(): async with backend_anonymous_cmds_factory(alice_device.organization_addr) as cmds: other_alice_device = await retry(claim_device, cmds, other_alice_device_id, token) save_device_with_password(config_dir, other_alice_device, password, force=force) async with trio.open_nursery() as nursery: nursery.start_soon(invite_task) nursery.start_soon(claim_task) # Invite Bob in token = generate_invitation_token() async def invite_task(): async with backend_cmds_factory( alice_device.organization_addr, alice_device.device_id, alice_device.signing_key ) as cmds: await invite_and_create_user( alice_device, cmds, bob_device_id.user_id, token, is_admin=True ) async def claim_task(): async with backend_anonymous_cmds_factory(alice_device.organization_addr) as cmds: bob_device = await retry(claim_user, cmds, bob_device_id, token) save_device_with_password(config_dir, bob_device, password, force=force) async with trio.open_nursery() as nursery: nursery.start_soon(invite_task) nursery.start_soon(claim_task) # Create bob workspace and share with Alice bob_device = load_device_with_password( config.config_dir, organization_id, bob_device_id, password ) async with logged_core_factory(config, bob_device) as core: await core.fs.workspace_create(f"/{bob_workspace}") await core.fs.share(f"/{bob_workspace}", alice_device_id.user_id) # Share Alice workspace with bob async with logged_core_factory(config, alice_device) as core: await core.fs.share(f"/{alice_workspace}", bob_device_id.user_id) # Print out click.echo( f""" Mount alice and bob drives using: $ parsec core run -P {password} -D {alice_slugid} $ parsec core run -P {password} -D {other_alice_slugid} $ parsec core run -P {password} -D {bob_slugid} """ )
async def initialize_test_organization( backend_address, organization_id, alice_device_id, bob_device_id, other_device_name, alice_workspace, bob_workspace, password, administration_token, force, ): configure_logging("WARNING") config_dir = get_default_config_dir(os.environ) alice_slugid = f"{organization_id}:{alice_device_id}" bob_slugid = f"{organization_id}:{bob_device_id}" # Create organization async with backend_administration_cmds_factory( backend_address, administration_token) as cmds: rep = await cmds.organization_create(organization_id) assert rep["status"] == "ok" bootstrap_token = rep["bootstrap_token"] organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build( backend_address, organization_id, bootstrap_token) # Bootstrap organization and Alice user async with backend_anonymous_cmds_factory( organization_bootstrap_addr) as cmds: root_signing_key = SigningKey.generate() root_verify_key = root_signing_key.verify_key organization_addr = organization_bootstrap_addr.generate_organization_addr( root_verify_key) alice_device = generate_new_device(alice_device_id, organization_addr, True) save_device_with_password(config_dir, alice_device, password, force=force) now = pendulum.now() user_certificate = UserCertificateContent( author=None, timestamp=now, user_id=alice_device.user_id, public_key=alice_device.public_key, is_admin=True, ).dump_and_sign(author_signkey=root_signing_key) device_certificate = DeviceCertificateContent( author=None, timestamp=now, device_id=alice_device.device_id, verify_key=alice_device.verify_key, ).dump_and_sign(author_signkey=root_signing_key) rep = await cmds.organization_bootstrap( organization_bootstrap_addr.organization_id, organization_bootstrap_addr.token, root_verify_key, user_certificate, device_certificate, ) assert rep["status"] == "ok" # Create a workspace for Alice config = load_config(config_dir, debug="DEBUG" in os.environ) async with logged_core_factory(config, alice_device) as core: alice_ws_id = await core.user_fs.workspace_create(f"{alice_workspace}") await core.user_fs.sync() # Register a new device for Alice token = generate_invitation_token() other_alice_device_id = DeviceID( f"{alice_device.user_id}@{other_device_name}") other_alice_slugid = f"{organization_id}:{other_alice_device_id}" async def invite_task(): await invite_and_create_device(alice_device, other_device_name, token) other_alice_device = None async def claim_task(): nonlocal other_alice_device other_alice_device = await retry_claim(claim_device, alice_device.organization_addr, other_alice_device_id, token) save_device_with_password(config_dir, other_alice_device, password, force=force) async with trio.open_service_nursery() as nursery: nursery.start_soon(invite_task) nursery.start_soon(claim_task) # Invite Bob in token = generate_invitation_token() bob_device = None async def invite_task(): await invite_and_create_user(alice_device, bob_device_id.user_id, token, is_admin=False) async def claim_task(): nonlocal bob_device bob_device = await retry_claim(claim_user, alice_device.organization_addr, bob_device_id, token) save_device_with_password(config_dir, bob_device, password, force=force) async with trio.open_service_nursery() as nursery: nursery.start_soon(invite_task) nursery.start_soon(claim_task) # Create bob workspace and share with Alice async with logged_core_factory(config, bob_device) as core: bob_ws_id = await core.user_fs.workspace_create(f"{bob_workspace}") await core.user_fs.workspace_share(bob_ws_id, alice_device_id.user_id, WorkspaceRole.MANAGER) # Share Alice workspace with bob async with logged_core_factory(config, alice_device) as core: await core.user_fs.workspace_share(alice_ws_id, bob_device_id.user_id, WorkspaceRole.MANAGER) # Synchronize every device for device in (alice_device, other_alice_device, bob_device): async with logged_core_factory(config, device) as core: await core.user_fs.process_last_messages() await core.user_fs.sync() return alice_slugid, other_alice_slugid, bob_slugid