async def _alice_nd_claim(): async with apiv1_backend_anonymous_cmds_factory(alice.organization_addr) as cmds: ret = await cmds.device_get_invitation_creator(nd_id) assert ret["status"] == "ok" assert ret["trustchain"] == {"devices": [], "revoked_users": [], "users": []} creator = UserCertificateContent.unsecure_load(ret["user_certificate"]) creator_device = DeviceCertificateContent.unsecure_load(ret["device_certificate"]) assert creator_device.device_id.user_id == creator.user_id answer_private_key = PrivateKey.generate() encrypted_claim = APIV1_DeviceClaimContent( token=token, device_id=nd_id, verify_key=nd_signing_key.verify_key, answer_public_key=answer_private_key.public_key, ).dump_and_encrypt_for(recipient_pubkey=creator.public_key) with trio.fail_after(1): ret = await cmds.device_claim(nd_id, encrypted_claim) assert ret["status"] == "ok" assert ret["device_certificate"] == device_certificate answer = APIV1_DeviceClaimAnswerContent.decrypt_and_load_for( ret["encrypted_answer"], recipient_privkey=answer_private_key ) assert answer == APIV1_DeviceClaimAnswerContent( private_key=alice.private_key, user_manifest_id=alice.user_manifest_id, user_manifest_key=alice.user_manifest_key, )
async def test_create_organization_same_name( gui, aqtbot, running_backend, catch_create_org_widget, autoclose_dialog, organization_bootstrap_addr, ): # Create an org human_handle = HumanHandle(email="*****@*****.**", label="Zack") async with apiv1_backend_anonymous_cmds_factory( addr=organization_bootstrap_addr) as cmds: await bootstrap_organization(cmds, human_handle=human_handle, device_label="PC1") # Now create an org with the same name await aqtbot.key_click(gui, "n", QtCore.Qt.ControlModifier, 200) co_w = await catch_create_org_widget() assert co_w await _do_creation_process(aqtbot, co_w) def _modal_shown(): assert autoclose_dialog.dialogs == [ ("Error", "This organization name is already used.") ] await aqtbot.wait_until(_modal_shown)
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 test_handshake_organization_expired(running_backend, expiredorg): with running_backend.backend.event_bus.listen() as spy: with pytest.raises(BackendConnectionRefused) as exc: async with apiv1_backend_anonymous_cmds_factory( expiredorg.addr) as cmds: await cmds.ping() await spy.wait_with_timeout(BackendEvent.ORGANIZATION_EXPIRED) assert str(exc.value) == "Trial organization has expired"
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_good( running_backend, backend, alice, bob, alice_backend_cmds, user_fs_factory, with_labels ): org_id = OrganizationID("NewOrg") org_token = "123456" await backend.organization.create(org_id, org_token) organization_addr = BackendOrganizationBootstrapAddr.build( running_backend.addr, org_id, org_token ) if with_labels: human_handle = HumanHandle(email="*****@*****.**", label="Zack") device_label = "PC1" else: human_handle = None device_label = None async with apiv1_backend_anonymous_cmds_factory(addr=organization_addr) as cmds: new_device = await bootstrap_organization( cmds, human_handle=human_handle, device_label=device_label ) assert new_device is not None assert new_device.organization_id == org_id assert new_device.device_label == device_label assert new_device.human_handle == human_handle assert new_device.profile == UserProfile.ADMIN # Test the behavior of this new device async with user_fs_factory(new_device, initialize_in_v0=True) as newfs: await newfs.workspace_create("wa") await newfs.sync() # Test the device in correct in the backend backend_user, backend_device = await backend.user.get_user_with_device( org_id, new_device.device_id ) assert backend_user.user_id == new_device.user_id assert backend_user.human_handle == new_device.human_handle assert backend_user.profile == new_device.profile assert backend_user.user_certifier is None if with_labels: assert backend_user.user_certificate != backend_user.redacted_user_certificate else: assert backend_user.user_certificate == backend_user.redacted_user_certificate assert backend_device.device_id == new_device.device_id assert backend_device.device_label == new_device.device_label assert backend_device.device_certifier is None if with_labels: assert backend_device.device_certificate != backend_device.redacted_device_certificate else: assert backend_device.device_certificate == backend_device.redacted_device_certificate
async def _bootstrap_organization(debug, device_id, organization_bootstrap_addr, config_dir, force, password): root_signing_key = SigningKey.generate() root_verify_key = root_signing_key.verify_key organization_addr = organization_bootstrap_addr.generate_organization_addr( root_verify_key) device_display = click.style(device_id, fg="yellow") device = generate_new_device(device_id, organization_addr, profile=UserProfile.ADMIN) with operation(f"Creating locally {device_display}"): save_device_with_password(config_dir, device, password, force=force) now = pendulum.now() user_certificate = UserCertificateContent( author=None, timestamp=now, user_id=device.user_id, 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_id, verify_key=device.verify_key) redacted_device_certificate = device_certificate.evolve(device_label=None) user_certificate = user_certificate.dump_and_sign(root_signing_key) device_certificate = device_certificate.dump_and_sign(root_signing_key) redacted_user_certificate = redacted_user_certificate.dump_and_sign( root_signing_key) redacted_device_certificate = redacted_device_certificate.dump_and_sign( root_signing_key) async with spinner(f"Sending {device_display} to server"): async with apiv1_backend_anonymous_cmds_factory( organization_bootstrap_addr) as cmds: await cmds.organization_bootstrap( organization_id=organization_bootstrap_addr.organization_id, bootstrap_token=organization_bootstrap_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, ) organization_addr_display = click.style(organization_addr.to_url(), fg="yellow") click.echo(f"Organization url: {organization_addr_display}")
async def test_invalid_token(running_backend, backend): org_id = OrganizationID("NewOrg") old_token = "123456" new_token = "abcdef" await backend.organization.create(org_id, old_token) await backend.organization.create(org_id, new_token) organization_addr = BackendOrganizationBootstrapAddr.build( running_backend.addr, org_id, old_token ) async with apiv1_backend_anonymous_cmds_factory(addr=organization_addr) as cmds: with pytest.raises(InviteNotFoundError): await bootstrap_organization(cmds, human_handle=None, device_label=None)
async def test_already_bootstrapped( running_backend, backend, alice, bob, alice_backend_cmds, user_fs_factory ): org_id = OrganizationID("NewOrg") org_token = "123456" await backend.organization.create(org_id, org_token) organization_addr = BackendOrganizationBootstrapAddr.build( running_backend.addr, org_id, org_token ) async with apiv1_backend_anonymous_cmds_factory(addr=organization_addr) as cmds: await bootstrap_organization(cmds, human_handle=None, device_label=None) with pytest.raises(InviteAlreadyUsedError): await bootstrap_organization(cmds, human_handle=None, device_label=None)
async def _do_create_org(config, human_handle, device_name, password, backend_addr): try: async with apiv1_backend_anonymous_cmds_factory( addr=backend_addr) as cmds: new_device = await bootstrap_organization( cmds=cmds, human_handle=human_handle, device_label=device_name) save_device_with_password(config_dir=config.config_dir, device=new_device, password=password) return new_device, password except InviteAlreadyUsedError as exc: raise JobResultError("invite-already-used", exc=exc) except BackendConnectionRefused as exc: raise JobResultError("connection-refused", exc=exc) except BackendNotAvailable as exc: raise JobResultError("connection-error", exc=exc)
async def _bootstrap_organization(config, addr, password, force): label = await aprompt("User fullname") email = await aprompt("User email") human_handle = HumanHandle(email=email, label=label) device_label = await aprompt("Device label", default=platform.node()) async with apiv1_backend_anonymous_cmds_factory(addr=addr) as cmds: async with spinner("Bootstrapping organization in the backend"): new_device = await do_bootstrap_organization( cmds=cmds, human_handle=human_handle, device_label=device_label) device_display = click.style(new_device.slughash, fg="yellow") with operation(f"Saving device {device_display}"): save_device_with_password(config_dir=config.config_dir, device=new_device, password=password, force=force)
async def failsafe_organization_bootstrap( addr: BackendOrganizationBootstrapAddr, root_verify_key: VerifyKey, user_certificate: bytes, device_certificate: bytes, redacted_user_certificate: bytes, redacted_device_certificate: bytes, ) -> dict: # Try the new anonymous API try: rep = await cmd_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, ) # If we get a 404 error, maybe the backend is too old to know about the anonymous route (API version < 2.6) except BackendNotAvailable as exc: inner_exc = exc.args[0] if getattr(inner_exc, "status", None) != 404: raise # If we get an "unknown_command" status, the backend might be too old to know about the "organization_bootstrap" command (API version < 2.7) else: if rep["status"] != "unknown_command": return rep # Then we try again with the legacy version async with apiv1_backend_anonymous_cmds_factory(addr) as anonymous_cmds: return await anonymous_cmds.organization_bootstrap( organization_id=addr.organization_id, bootstrap_token=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, )
async def _bootstrap_organization(config, addr, password, device_label, human_label, human_email): if not human_label: human_label = await aprompt("User fullname") if not human_email: human_email = await aprompt("User email") human_handle = HumanHandle(email=human_email, label=human_label) if not device_label: device_label = await aprompt("Device label", default=platform.node()) async with apiv1_backend_anonymous_cmds_factory(addr=addr) as cmds: async with spinner("Bootstrapping organization in the backend"): new_device = await do_bootstrap_organization( cmds=cmds, human_handle=human_handle, device_label=device_label ) device_display = click.style(new_device.slughash, fg="yellow") # We don't have to worry about overwritting an existing keyfile # given their names are base on the device's slughash which is intended # to be globally unique. with operation(f"Saving device {device_display}"): save_device_with_password( config_dir=config.config_dir, device=new_device, password=password )
async def _mallory_claim(): async with apiv1_backend_anonymous_cmds_factory( mallory.organization_addr) as cmds: rep = await cmds.user_get_invitation_creator(mallory.user_id) assert rep["trustchain"] == { "devices": [], "revoked_users": [], "users": [] } creator = UserCertificateContent.unsecure_load( rep["user_certificate"]) creator_device = DeviceCertificateContent.unsecure_load( rep["device_certificate"]) assert creator_device.device_id.user_id == creator.user_id encrypted_claim = APIV1_UserClaimContent( device_id=mallory.device_id, token=token, public_key=mallory.public_key, verify_key=mallory.verify_key, ).dump_and_encrypt_for(recipient_pubkey=creator.public_key) with trio.fail_after(1): rep = await cmds.user_claim(mallory.user_id, encrypted_claim) assert rep["status"] == "ok"
async def test_backend_switch_offline(running_backend, coolorg): async with apiv1_backend_anonymous_cmds_factory(coolorg.addr) as cmds: await cmds.ping() with running_backend.offline(): with pytest.raises(BackendNotAvailable): await cmds.ping()
async def apiv1_anonymous_backend_cmds(running_backend, coolorg): async with apiv1_backend_anonymous_cmds_factory(coolorg.addr) as cmds: yield cmds
def _cmds_factory(keepalive): return apiv1_backend_anonymous_cmds_factory(coolorg.addr, keepalive=keepalive)
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 claim_user( organization_addr: BackendOrganizationAddr, new_device_id: DeviceID, token: str, keepalive: Optional[int] = None, ) -> LocalDevice: """ Raises: InviteClaimError InviteClaimBackendOfflineError InviteClaimValidationError InviteClaimPackingError InviteClaimCryptoError """ new_device = generate_new_device(new_device_id, organization_addr) try: async with apiv1_backend_anonymous_cmds_factory( organization_addr, keepalive=keepalive) as cmds: # 1) Retrieve invitation creator try: invitation_creator_user, invitation_creator_device = await get_user_invitation_creator( cmds, new_device.root_verify_key, new_device.user_id) except RemoteDevicesManagerBackendOfflineError as exc: raise InviteClaimBackendOfflineError(str(exc)) from exc except RemoteDevicesManagerError as exc: raise InviteClaimError( f"Cannot retrieve invitation creator: {exc}") from exc # 2) Generate claim info for invitation creator try: encrypted_claim = APIV1_UserClaimContent( device_id=new_device_id, token=token, public_key=new_device.public_key, verify_key=new_device.verify_key, ).dump_and_encrypt_for( recipient_pubkey=invitation_creator_user.public_key) except DataError as exc: raise InviteClaimError( f"Cannot generate user claim message: {exc}") from exc # 3) Send claim rep = await cmds.user_claim(new_device_id.user_id, encrypted_claim) if rep["status"] != "ok": raise InviteClaimError(f"Cannot claim user: {rep}") # 4) Verify user&device certificates and check admin status try: user = UserCertificateContent.verify_and_load( rep["user_certificate"], author_verify_key=invitation_creator_device.verify_key, expected_author=invitation_creator_device.device_id, expected_user=new_device_id.user_id, ) DeviceCertificateContent.verify_and_load( rep["device_certificate"], author_verify_key=invitation_creator_device.verify_key, expected_author=invitation_creator_device.device_id, expected_device=new_device_id, ) new_device = new_device.evolve(profile=user.profile) except DataError as exc: raise InviteClaimCryptoError(str(exc)) from exc except BackendNotAvailable as exc: raise InviteClaimBackendOfflineError(str(exc)) from exc except BackendConnectionError as exc: raise InviteClaimError(f"Cannot claim user: {exc}") from exc return new_device
async def claim_device( organization_addr: BackendOrganizationAddr, new_device_id: DeviceID, token: str, keepalive: Optional[int] = None, ) -> LocalDevice: """ Raises: InviteClaimError InviteClaimBackendOfflineError InviteClaimValidationError InviteClaimPackingError InviteClaimCryptoError """ device_signing_key = SigningKey.generate() answer_private_key = PrivateKey.generate() try: async with apiv1_backend_anonymous_cmds_factory( organization_addr, keepalive=keepalive) as cmds: # 1) Retrieve invitation creator try: invitation_creator_user, invitation_creator_device = await get_device_invitation_creator( cmds, organization_addr.root_verify_key, new_device_id) except RemoteDevicesManagerBackendOfflineError as exc: raise InviteClaimBackendOfflineError(str(exc)) from exc except RemoteDevicesManagerError as exc: raise InviteClaimError( f"Cannot retrieve invitation creator: {exc}") from exc # 2) Generate claim info for invitation creator try: encrypted_claim = APIV1_DeviceClaimContent( token=token, device_id=new_device_id, verify_key=device_signing_key.verify_key, answer_public_key=answer_private_key.public_key, ).dump_and_encrypt_for( recipient_pubkey=invitation_creator_user.public_key) except DataError as exc: raise InviteClaimError( f"Cannot generate device claim message: {exc}") from exc # 3) Send claim rep = await cmds.device_claim(new_device_id, encrypted_claim) if rep["status"] != "ok": raise InviteClaimError(f"Claim request error: {rep}") # 4) Verify device certificate try: DeviceCertificateContent.verify_and_load( rep["device_certificate"], author_verify_key=invitation_creator_device.verify_key, expected_author=invitation_creator_device.device_id, expected_device=new_device_id, ) except DataError as exc: raise InviteClaimCryptoError(str(exc)) from exc try: answer = APIV1_DeviceClaimAnswerContent.decrypt_and_load_for( rep["encrypted_answer"], recipient_privkey=answer_private_key) except DataError as exc: raise InviteClaimCryptoError( f"Cannot decrypt device claim answer: {exc}") from exc except BackendNotAvailable as exc: raise InviteClaimBackendOfflineError(str(exc)) from exc except BackendConnectionError as exc: raise InviteClaimError(f"Cannot claim device: {exc}") from exc return LocalDevice( organization_addr=organization_addr, device_id=new_device_id, signing_key=device_signing_key, private_key=answer.private_key, profile=UserProfile.ADMIN if invitation_creator_user.is_admin else UserProfile.STANDARD, user_manifest_id=answer.user_manifest_id, user_manifest_key=answer.user_manifest_key, local_symkey=SecretKey.generate(), )
async def test_handshake_organization_expired(running_backend, expiredorg): with pytest.raises(BackendConnectionRefused) as exc: async with apiv1_backend_anonymous_cmds_factory(expiredorg.addr) as cmds: await cmds.ping() assert str(exc.value) == "Trial organization has expired"
async def test_backend_closed_cmds(running_backend, coolorg): async with apiv1_backend_anonymous_cmds_factory(coolorg.addr) as cmds: pass with pytest.raises(trio.ClosedResourceError): await cmds.ping()
async def test_ping(running_backend, coolorg): async with apiv1_backend_anonymous_cmds_factory(coolorg.addr) as cmds: rep = await cmds.ping("Hello World !") assert rep == {"status": "ok", "pong": "Hello World !"}
async def initialize_test_organization( config_dir: Path, backend_address: BackendAddr, password: str, administration_token: str, additional_users_number: int, additional_devices_number: int, ) -> Tuple[LocalDevice, LocalDevice, LocalDevice]: configure_logging(log_level="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", rep bootstrap_token = rep["bootstrap_token"] organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build( backend_address, organization_id, bootstrap_token) # Bootstrap organization and Alice user and create device "laptop" for Alice 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=config_dir, device=alice_device, password=password) config = load_config(config_dir, debug="DEBUG" in os.environ) # Create context manager, alice_core will be needed for the rest of the script async with logged_core_factory(config, alice_device) as alice_core: 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: # Create new device "pc" for Alice other_alice_device = await _register_new_device( cmds=alice_cmds, author=alice_device, device_label="pc") save_device_with_password(config_dir=config_dir, device=other_alice_device, password=password) # Invite Bob in organization bob_device = await _register_new_user( cmds=alice_cmds, author=alice_device, device_label="laptop", human_handle=HumanHandle(email="*****@*****.**", label="Bob"), profile=UserProfile.STANDARD, ) save_device_with_password(config_dir=config_dir, device=bob_device, password=password) # Invite Toto in organization toto_device = await _register_new_user( cmds=alice_cmds, author=alice_device, device_label="laptop", human_handle=HumanHandle(email="*****@*****.**", label="Toto"), profile=UserProfile.OUTSIDER, ) save_device_with_password(config_dir=config_dir, device=toto_device, password=password) # Create Alice workspace alice_ws_id = await alice_core.user_fs.workspace_create( "alice_workspace") # Create context manager async with logged_core_factory(config, bob_device) as bob_core: # Create Bob workspace bob_ws_id = await bob_core.user_fs.workspace_create( "bob_workspace") # Bob share workspace with Alice await bob_core.user_fs.workspace_share(bob_ws_id, alice_device.user_id, WorkspaceRole.MANAGER) # Alice share workspace with Bob await alice_core.user_fs.workspace_share( alice_ws_id, bob_device.user_id, WorkspaceRole.MANAGER) # Add additional random users await _add_random_users( cmds=alice_cmds, author=alice_device, alice_core=alice_core, bob_core=bob_core, alice_ws_id=alice_ws_id, bob_ws_id=bob_ws_id, additional_users_number=additional_users_number, ) # Add additional random device for alice await _add_random_device( cmds=alice_cmds, device=alice_device, additional_devices_number=additional_devices_number, ) # 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_anonymous_cmds_has_right_methods(running_backend, coolorg): async with apiv1_backend_anonymous_cmds_factory(coolorg.addr) as cmds: for method_name in APIV1_ANONYMOUS_CMDS: assert hasattr(cmds, method_name) for method_name in ALL_CMDS - APIV1_ANONYMOUS_CMDS: assert not hasattr(cmds, method_name)
async def _do_bootstrap_organization( config_dir, password: str, password_check: str, user_id: str, device_name: str, bootstrap_addr: BackendOrganizationBootstrapAddr, ): if password != password_check: raise JobResultError("password-mismatch") if len(password) < 8: raise JobResultError("password-size") try: device_id = DeviceID(f"{user_id}@{device_name}") except ValueError as exc: raise JobResultError("bad-device_name") from exc root_signing_key = SigningKey.generate() root_verify_key = root_signing_key.verify_key organization_addr = bootstrap_addr.generate_organization_addr( root_verify_key) try: device = generate_new_device(device_id, organization_addr, profile=UserProfile.ADMIN) save_device_with_password(config_dir, device, password) except LocalDeviceAlreadyExistsError as exc: raise JobResultError("user-exists") from exc now = pendulum.now() user_certificate = UserCertificateContent( author=None, timestamp=now, user_id=device.user_id, public_key=device.public_key, profile=device.profile, ).dump_and_sign(root_signing_key) device_certificate = DeviceCertificateContent( author=None, timestamp=now, device_id=device_id, verify_key=device.verify_key).dump_and_sign(root_signing_key) try: async with apiv1_backend_anonymous_cmds_factory( bootstrap_addr) as cmds: rep = await cmds.organization_bootstrap( bootstrap_addr.organization_id, bootstrap_addr.token, root_verify_key, user_certificate, device_certificate, ) if rep["status"] == "already_bootstrapped": raise JobResultError("already-bootstrapped", info=str(rep)) elif rep["status"] == "not_found": raise JobResultError("invalid-url", info=str(rep)) elif rep["status"] != "ok": raise JobResultError("refused-by-backend", info=str(rep)) return device, password except BackendConnectionRefused as exc: raise JobResultError("invalid-url", info=str(exc)) from exc except BackendNotAvailable as exc: raise JobResultError("backend-offline", info=str(exc)) from exc except BackendConnectionError as exc: raise JobResultError("refused-by-backend", info=str(exc)) from exc