def test_bad_organization_id_user_id_and_device_name(raw): with pytest.raises(ValueError): OrganizationID(raw) with pytest.raises(ValueError): UserID(raw) with pytest.raises(ValueError): DeviceName(raw)
def test_good_invited_handshake(coolorg, invitation_type): organization_id = OrganizationID("Org") token = uuid4() sh = ServerHandshake() ch = InvitedClientHandshake(organization_id=organization_id, invitation_type=invitation_type, token=token) assert sh.state == "stalled" challenge_req = sh.build_challenge_req() assert sh.state == "challenge" answer_req = ch.process_challenge_req(challenge_req) sh.process_answer_req(answer_req) assert sh.state == "answer" assert sh.answer_type == HandshakeType.INVITED assert sh.answer_data == { "client_api_version": API_V2_VERSION, "organization_id": organization_id, "invitation_type": invitation_type, "token": token, } result_req = sh.build_result_req() assert sh.state == "result" ch.process_result_req(result_req) assert sh.client_api_version == API_V2_VERSION
def validate(self, string, pos): try: if len(string) == 0: return QValidator.Intermediate, string, pos OrganizationID(string) return QValidator.Acceptable, string, pos except ValueError: return QValidator.Invalid, string, pos
def test_organization_id_user_id_and_device_name(raw): organization_id = OrganizationID(raw) assert organization_id == raw user_id = UserID(raw) assert user_id == raw device_name = DeviceName(raw) assert device_name == raw
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
def test_backend_organization_addr_good(base_url, expected, verify_key): org = OrganizationID("org") backend_addr = BackendAddr.from_url(base_url) addr = BackendOrganizationAddr.build(backend_addr, organization_id=org, root_verify_key=verify_key) assert addr.hostname == "foo" assert addr.port == expected["port"] assert addr.use_ssl == expected["ssl"] assert addr.organization_id == org assert addr.root_verify_key == verify_key addr2 = BackendOrganizationAddr.from_url(addr.to_url()) assert addr == addr2
async def test_get_redirect_invitation(backend_http_send, backend_addr): invitation_addr = BackendInvitationAddr.build( backend_addr=backend_addr, organization_id=OrganizationID("Org"), invitation_type=InvitationType.USER, token=uuid4(), ) # TODO: should use invitation_addr.to_redirection_url() when available ! *_, target = invitation_addr.to_url().split("/") rep = await backend_http_send(f"/redirect/{target}") assert rep.startswith("HTTP/1.1 200 OK\r\n") location_match = re.search( r'class="parsecLink">\n <div class="urltxt">(.+)</div>', rep) location_addr = BackendInvitationAddr.from_url(location_match.group(1)) assert location_addr == invitation_addr
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)
def test_list_devices_support_legacy_file_with_meaningful_name(config_dir): # Legacy path might exceed the 256 characters limit in some cases (see issue #1356) # So we use the `\\?\` workaround: https://stackoverflow.com/a/57502760/2846140 if os.name == "nt": config_dir = Path("\\\\?\\" + str(config_dir.resolve())) # Device information user_id = uuid4().hex device_name = uuid4().hex organization_id = "Org" rvk_hash = (uuid4().hex)[:10] device_id = f"{user_id}@{device_name}" slug = f"{rvk_hash}#{organization_id}#{device_id}" human_label = "Billy Mc BillFace" human_email = "*****@*****.**" device_label = "My device" # Craft file data without the user_id, organization_id and # root_verify_key_hash fields key_file_data = packb({ "type": "password", "salt": b"12345", "ciphertext": b"whatever", "human_handle": (human_email.encode(), human_label.encode()), "device_label": device_label.encode(), }) key_file_path = get_devices_dir(config_dir) / slug / f"{slug}.keys" key_file_path.parent.mkdir(parents=True) key_file_path.write_bytes(key_file_data) devices = list_available_devices(config_dir) expected_device = AvailableDevice( key_file_path=key_file_path, organization_id=OrganizationID(organization_id), device_id=DeviceID(device_id), human_handle=HumanHandle(human_email, human_label), device_label=device_label, root_verify_key_hash=rvk_hash, ) assert devices == [expected_device]
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 test_handshake_incompatible_version(backend, server_factory): async with server_factory(backend.handle_client) as server: stream = server.connection_factory() transport = await Transport.init_for_client(stream, server.addr.hostname) incompatible_version = ApiVersion(API_VERSION.version + 1, 0) await transport.recv() # Get challenge req = { "handshake": "answer", "type": "anonymous", "client_api_version": incompatible_version, "organization_id": OrganizationID("Org"), "token": "whatever", } await transport.send(packb(req)) result_req = await transport.recv() assert unpackb(result_req) == { "handshake": "result", "result": "bad_protocol", "help": "No overlap between client API versions {3.0} and backend API versions {2.0, 1.2}", }
def test_backend_organization_bootstrap_addr_good(base_url, expected, verify_key): org = OrganizationID("org") backend_addr = BackendAddr.from_url(base_url) addr = BackendOrganizationBootstrapAddr.build(backend_addr, org, "token-123") assert addr.hostname == "foo" assert addr.port == expected["port"] assert addr.use_ssl == expected["ssl"] assert addr.organization_id == org assert addr.token == "token-123" addr2 = BackendOrganizationBootstrapAddr.from_url(str(addr)) assert addr == addr2 org_addr = addr.generate_organization_addr(verify_key) assert isinstance(org_addr, BackendOrganizationAddr) assert org_addr.root_verify_key == verify_key assert org_addr.hostname == addr.hostname assert org_addr.port == addr.port assert org_addr.use_ssl == addr.use_ssl assert org_addr.organization_id == addr.organization_id
def _organization_factory(orgname=None, expiration_date=None): nonlocal count if not orgname: count += 1 orgname = f"Org{count}" assert orgname not in organizations organization_id = OrganizationID(orgname) organizations.add(organization_id) bootstrap_token = f"<{orgname}-bootstrap-token>" bootstrap_addr = BackendOrganizationBootstrapAddr.build( backend_addr, organization_id=organization_id, token=bootstrap_token) root_signing_key = SigningKey.generate() addr = bootstrap_addr.generate_organization_addr( root_signing_key.verify_key) return OrganizationFullData(bootstrap_addr, addr, root_signing_key)
def test_list_devices_support_legacy_file_without_labels(config_dir): # Craft file data without the labels fields key_file_data = packb({ "type": "password", "salt": b"12345", "ciphertext": b"whatever" }) slug = "9d84fbd57a#Org#Zack@PC1" key_file_path = fix_dir( get_devices_dir(config_dir) / slug / f"{slug}.keys") key_file_path.parent.mkdir(parents=True) key_file_path.write_bytes(key_file_data) devices = list_available_devices(config_dir) expected_device = AvailableDevice( key_file_path=key_file_path, organization_id=OrganizationID("Org"), device_id=DeviceID("Zack@PC1"), human_handle=None, device_label=None, root_verify_key_hash="9d84fbd57a", ) assert devices == [expected_device]
async def initialize_test_organization( config_dir: Path, backend_address: BackendAddr, password: str, administration_token: str, force: bool, additional_users_number: int, additional_devices_number: int, ) -> 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 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, alice_device, password, force=force) config = load_config(config_dir, debug="DEBUG" in os.environ) # Create context manager, alice_client will be needed for the rest of the script async with logged_client_factory(config, alice_device) as alice_client: 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, other_alice_device, password, force=force) # Add additional random device for alice if additional_devices_number > 0: print( f"Adding {additional_devices_number} devices in the test group" ) print(" ... please wait ...") await _add_random_device( cmds=alice_cmds, config_dir=config_dir, force=force, password=password, device=alice_device, additional_devices_number=additional_devices_number, ) print(" Done ") # 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, bob_device, password, force=force) # Create Alice workspace alice_ws_id = await alice_client.user_fs.workspace_create( "alice_workspace") # Create context manager async with logged_client_factory(config, bob_device) as bob_client: # Create Bob workspace bob_ws_id = await bob_client.user_fs.workspace_create( "bob_workspace") # Bob share workspace with Alice await bob_client.user_fs.workspace_share( bob_ws_id, alice_device.user_id, WorkspaceRole.MANAGER) # Alice share workspace with Bob await alice_client.user_fs.workspace_share( alice_ws_id, bob_device.user_id, WorkspaceRole.MANAGER) # Add additional random users if additional_users_number > 0: print( f"Adding {additional_users_number} users in the test group" ) print(" ... please wait ...") await _add_random_users( cmds=alice_cmds, author=alice_device, alice_client=alice_client, bob_client=bob_client, alice_ws_id=alice_ws_id, bob_ws_id=bob_ws_id, additional_users_number=additional_users_number, ) print(" Done ") # Synchronize every device for device in (alice_device, other_alice_device, bob_device): async with logged_client_factory(config, device) as client: await client.user_fs.process_last_messages() await client.user_fs.sync() return (alice_device, other_alice_device, bob_device)
def load_slug(slug: str) -> Tuple[str, OrganizationID, DeviceID]: """ Raises: ValueError """ rvk_hash, raw_org_id, raw_device_id = slug.split("#") return rvk_hash, OrganizationID(raw_org_id), DeviceID(raw_device_id)
async def organization_bootstrap_addr(running_backend): org_id = OrganizationID("NewOrg") org_token = "123456" await running_backend.backend.organization.create(org_id, org_token) return BackendOrganizationBootstrapAddr.build(running_backend.addr, org_id, org_token)
def _from_url_parse_path(cls, path): return {"organization_id": OrganizationID(path[1:])}