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]: organization_id = OrganizationID("Org") config = load_config(config_dir, debug="DEBUG" in os.environ) # Create organization bootstrap_token = await create_organization_req( organization_id, backend_address, administration_token ) organization_bootstrap_addr = BackendOrganizationBootstrapAddr.build( backend_address, organization_id, bootstrap_token ) # Bootstrap organization and Alice user and create device "laptop" for Alice alice_device = await bootstrap_organization( organization_bootstrap_addr, human_handle=HumanHandle(label="Alice", email="*****@*****.**"), device_label=DeviceLabel("laptop"), ) await user_storage_non_speculative_init(data_base_dir=config.data_base_dir, device=alice_device) save_device_with_password_in_config( config_dir=config_dir, device=alice_device, password=password ) # 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=DeviceLabel("pc") ) save_device_with_password_in_config( 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=DeviceLabel("laptop"), human_handle=HumanHandle(email="*****@*****.**", label="Bob"), profile=UserProfile.STANDARD, ) await user_storage_non_speculative_init( data_base_dir=config.data_base_dir, device=bob_device ) save_device_with_password_in_config( 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=DeviceLabel("laptop"), human_handle=HumanHandle(email="*****@*****.**", label="Toto"), profile=UserProfile.OUTSIDER, ) await user_storage_non_speculative_init( data_base_dir=config.data_base_dir, device=toto_device ) save_device_with_password_in_config( config_dir=config_dir, device=toto_device, password=password ) # Create Alice workspace alice_ws_id = await alice_core.user_fs.workspace_create(EntryName("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(EntryName("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 query_find_humans( conn, organization_id: OrganizationID, query: str, page: int, per_page: int, omit_revoked: bool, omit_non_human: bool, ) -> Tuple[List[HumanFindResultItem], int]: if query: if omit_revoked: q = _q_human_factory(query=True, omit_revoked=True, omit_non_human=omit_non_human) args = (pendulum_now(), organization_id, query) else: q = _q_human_factory(query=True, omit_revoked=False, omit_non_human=omit_non_human) args = (pendulum_now(), organization_id, query) else: if omit_revoked: q = _q_human_factory(query=False, omit_revoked=True, omit_non_human=omit_non_human) args = (pendulum_now(), organization_id) else: q = _q_human_factory(query=False, omit_revoked=False, omit_non_human=omit_non_human) args = (pendulum_now(), organization_id) raw_results = await conn.fetch(q, *args) humans = [ HumanFindResultItem( user_id=UserID(user_id), human_handle=HumanHandle(email=email, label=label), revoked=revoked, ) for user_id, email, label, revoked in raw_results if email is not None ] non_humans = [ HumanFindResultItem(user_id=UserID(user_id), human_handle=None, revoked=revoked) for user_id, email, label, revoked in raw_results if email is None ] results = [ *sorted(humans, key=lambda x: (x.human_handle.label.lower(), x.user_id.lower())), *sorted(non_humans, key=lambda x: x.user_id.lower()), ] # TODO: should user LIMIT and OFFSET in the SQL query instead return results[(page - 1) * per_page:page * per_page], len(results)
def _local_device_factory( base_device_id: Optional[str] = None, org: OrganizationFullData = coolorg, profile: Optional[UserProfile] = None, has_human_handle: bool = True, base_human_handle: Optional[str] = None, has_device_label: bool = True, base_device_label: Optional[str] = None, ): nonlocal count if not base_device_id: count += 1 base_device_id = f"user{count}@dev0" org_devices = devices[org.organization_id] device_id = DeviceID(base_device_id) assert not any(d for d in org_devices if d.device_id == device_id) if not has_device_label: assert base_device_label is None device_label = None elif not base_device_label: device_label = f"My {device_id.device_name} machine" else: device_label = base_device_label if not has_human_handle: assert base_human_handle is None human_handle = None elif base_human_handle: if isinstance(base_human_handle, HumanHandle): human_handle = base_human_handle else: match = re.match(r"(.*) <(.*)>", base_human_handle) if match: label, email = match.groups() else: label = base_human_handle email = f"{device_id.user_id}@example.com" human_handle = HumanHandle(email=email, label=label) else: name = device_id.user_id.capitalize() human_handle = HumanHandle( email=f"{device_id.user_id}@example.com", label=f"{name}y Mc{name}Face") parent_device = None try: # If the user already exists, we must retrieve it data parent_device = next(d for d in org_devices if d.user_id == device_id.user_id) if profile is not None and profile != parent_device.profile: raise ValueError( "profile is set but user already exists, with a different profile value." ) profile = parent_device.profile except StopIteration: profile = profile or UserProfile.STANDARD # Force each device to access the backend trough a different hostname so # tcp stream spy can switch offline certains while keeping the others online org_addr = addr_with_device_subdomain(org.addr, device_id) device = generate_new_device( organization_addr=org_addr, device_id=device_id, profile=profile, human_handle=human_handle, device_label=device_label, ) if parent_device is not None: device = device.evolve( private_key=parent_device.private_key, user_manifest_id=parent_device.user_manifest_id, user_manifest_key=parent_device.user_manifest_key, ) org_devices.append(device) return device
async def test_good_user_claim(backend, running_backend, alice, alice_backend_cmds, user_fs_factory, with_labels): claimer_email = "*****@*****.**" invitation = await backend.invite.new_for_user( organization_id=alice.organization_id, greeter_user_id=alice.user_id, claimer_email=claimer_email, ) invitation_addr = BackendInvitationAddr.build( backend_addr=alice.organization_addr, organization_id=alice.organization_id, invitation_type=InvitationType.USER, token=invitation.token, ) if with_labels: # Let's pretent we invited a Fortnite player... requested_human_handle = HumanHandle(email="*****@*****.**", label="xXx_Z4ck_xXx") requested_device_label = "Ultr4_B00st" granted_human_handle = HumanHandle(email="*****@*****.**", label="Zack") granted_device_label = "Desktop" else: requested_human_handle = None requested_device_label = None granted_human_handle = None granted_device_label = None granted_profile = UserProfile.STANDARD new_device = None # Simulate out-of-bounds canal oob_send, oob_recv = trio.open_memory_channel(0) 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) async def _run_greeter(): initial_ctx = UserGreetInitialCtx(cmds=alice_backend_cmds, token=invitation_addr.token) in_progress_ctx = await initial_ctx.do_wait_peer() await oob_send.send(in_progress_ctx.greeter_sas) in_progress_ctx = await in_progress_ctx.do_wait_peer_trust() choices = in_progress_ctx.generate_claimer_sas_choices(size=5) assert len(choices) == 5 assert in_progress_ctx.claimer_sas in choices claimer_sas = await oob_recv.receive() assert claimer_sas == in_progress_ctx.claimer_sas in_progress_ctx = await in_progress_ctx.do_signify_trust() in_progress_ctx = await in_progress_ctx.do_get_claim_requests() assert in_progress_ctx.requested_device_label == requested_device_label assert in_progress_ctx.requested_human_handle == requested_human_handle await in_progress_ctx.do_create_new_user( author=alice, device_label=granted_device_label, human_handle=granted_human_handle, profile=granted_profile, ) with trio.fail_after(1): async with trio.open_nursery() as nursery: nursery.start_soon(_run_claimer) nursery.start_soon(_run_greeter) assert new_device is not None assert new_device.device_id != alice.device_id assert new_device.device_label == granted_device_label # Label is normally ignored when comparing HumanLabel if with_labels: assert new_device.human_handle.label == granted_human_handle.label assert new_device.human_handle.email == granted_human_handle.email else: assert new_device.human_handle is None assert new_device.profile == granted_profile # Extra check to make sure claimer&greeter data are not mixed assert new_device.user_manifest_id != alice.user_manifest_id assert new_device.user_manifest_key != alice.user_manifest_key assert new_device.local_symkey != alice.local_symkey # Now invitation should have been deleted rep = await alice_backend_cmds.invite_list() assert rep == {"status": "ok", "invitations": []} # Verify user&device data in backend user, device = await backend.user.get_user_with_device( new_device.organization_id, new_device.device_id) assert user.profile == granted_profile assert user.human_handle == granted_human_handle assert device.device_label == granted_device_label if with_labels: assert user.user_certificate != user.redacted_user_certificate assert device.device_certificate != device.redacted_device_certificate else: assert user.user_certificate == user.redacted_user_certificate assert device.device_certificate == device.redacted_device_certificate # Test the behavior of this new user device async with user_fs_factory(alice) as alicefs: async with user_fs_factory(new_device, initialize_in_v0=True) as newfs: # Share a workspace with new user aw_id = await alicefs.workspace_create("alice_workspace") await alicefs.workspace_share(aw_id, new_device.user_id, WorkspaceRole.CONTRIBUTOR) # New user cannot create a new workspace zw_id = await newfs.workspace_create("zack_workspace") await newfs.workspace_share(zw_id, alice.user_id, WorkspaceRole.READER) # Now both users should have the same workspaces await alicefs.process_last_messages() await newfs.process_last_messages() await newfs.sync() # Not required, but just to make sure it works alice_um = alicefs.get_user_manifest() zack_um = newfs.get_user_manifest() assert {(w.id, w.key) for w in alice_um.workspaces } == {(w.id, w.key) for w in zack_um.workspaces}
def human_handle(self): user_name = validators.trim_user_name( self.line_edit_user_full_name.text()) return HumanHandle(label=user_name, email=self.line_edit_user_email.text())
def test_valid_human_handle(email, label): HumanHandle(email, label)
def test_invalid_human_handle(email, label): with pytest.raises(ValueError): HumanHandle(email, label)