async def _do_cancel_invitation(client, token): try: await client.delete_invitation(token=token) except BackendNotAvailable as exc: raise JobResultError("offline") from exc except BackendConnectionError as exc: raise JobResultError("error") from exc
async def _do_invite_device(client): try: return await client.new_device_invitation(send_email=False) except BackendNotAvailable as exc: raise JobResultError("offline") from exc except BackendConnectionError as exc: raise JobResultError("error") from exc
async def _do_get_organization_stats(client): try: return await client.get_organization_stats() except BackendNotAvailable as exc: raise JobResultError("offline") from exc except BackendConnectionError as exc: raise JobResultError("error") from exc
async def _do_revoke_user(client, user_info): try: await client.revoke_user(user_info.user_id) user_info = await client.get_user_info(user_info.user_id) return user_info except BackendNotAvailable as exc: raise JobResultError("offline") from exc except BackendConnectionError as exc: raise JobResultError("error") from exc
async def _do_workspace_rename(client, workspace_id, new_name, button): try: new_name = EntryName(new_name) except ValueError: raise JobResultError("invalid-name") try: await client.user_fs.workspace_rename(workspace_id, new_name) return button, new_name except Exception as exc: raise JobResultError("rename-error") from exc
async def _do_invite_user(client, email): try: await client.new_user_invitation(email=email, send_email=True) return email except BackendNotAvailable as exc: raise JobResultError("offline") from exc except BackendConnectionError as exc: raise JobResultError("error") from exc except InviteAlreadyMemberError as exc: raise JobResultError("already_member") from exc
async def _do_rename(workspace_fs, paths): new_names = {} for (old_path, new_path, uuid) in paths: try: await workspace_fs.rename(old_path, new_path) new_names[uuid] = FsPath(new_path).name except FileExistsError as exc: raise JobResultError("already-exists", multi=len(paths) > 1) from exc except OSError as exc: raise JobResultError("not-empty", multi=len(paths) > 1) from exc
async def _do_workspace_create(client, workspace_name): try: workspace_name = EntryName(workspace_name) except ValueError: raise JobResultError("invalid-name") workspace_id = await client.user_fs.workspace_create(workspace_name) return workspace_id
async def claim_device(self, device_label): await self.main_oob_send.send(self.Step.ClaimDevice) await self.main_oob_send.send(device_label) r, exc, new_device = await self.job_oob_recv.receive() if not r: raise JobResultError(status="claim-device-failed", origin=exc) return new_device
async def _do_import(workspace_fs, files, total_size, progress_signal): current_size = 0 for src, dst in files: try: if dst.parent != FsPath("/"): await workspace_fs.mkdir(dst.parent, parents=True, exist_ok=True) progress_signal.emit(src.name, current_size) async with await trio.open_file(src, "rb") as f: async with await workspace_fs.open_file(dst, "wb") as dest_file: read_size = 0 while True: chunk = await f.read(DEFAULT_BLOCK_SIZE) if not chunk: break await dest_file.write(chunk) read_size += len(chunk) progress_signal.emit(src.name, current_size + read_size) current_size += read_size + 1 progress_signal.emit(src.name, current_size) except trio.Cancelled as exc: raise JobResultError("cancelled", last_file=dst) from exc
async def _do_bootstrap_organization( config_dir, password: str, password_check: str, human_handle: HumanHandle, device_label: str, bootstrap_addr: BackendOrganizationBootstrapAddr, ): if password != password_check: raise JobResultError("password-mismatch") if len(password) < 8: raise JobResultError("password-size") try: async with apiv1_backend_anonymous_cmds_factory( addr=bootstrap_addr) as cmds: new_device = await bootstrap_organization( cmds=cmds, human_handle=human_handle, device_label=device_label) save_device_with_password(config_dir, new_device, password) return new_device, password except InviteNotFoundError as exc: raise JobResultError("not-found", info=str(exc)) from exc except InviteAlreadyUsedError as exc: raise JobResultError("already-bootstrapped", info=str(exc)) from exc 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
async def _do_delete(workspace_fs, files, silent=False): for path, file_type in files: try: if file_type == FileType.Folder: await workspace_fs.rmtree(path) else: await workspace_fs.unlink(path) except Exception as exc: if not silent: raise JobResultError("error", multi=len(files) > 1) from exc
def _handle_fs_errors(): try: yield except FSBackendOfflineError as exc: raise JobResultError(ret=workspace_id, status="offline-backend", origin=exc) except FSWorkspaceNoAccess as exc: raise JobResultError(ret=workspace_id, status="access-error", origin=exc) except FSWorkspaceNotFoundError as exc: raise JobResultError(ret=workspace_id, status="not-found", origin=exc) except FSError as exc: raise JobResultError(ret=workspace_id, status="fs-error", origin=exc)
async def _do_list_users_and_invitations(client, page, pattern=None): try: if pattern is None: users, total = await client.find_humans(page=page, per_page=USERS_PER_PAGE) invitations = await client.list_invitations() return total, users, [ inv for inv in invitations if inv["type"] == InvitationType.USER ] else: users, total = await client.find_humans(page=page, per_page=USERS_PER_PAGE, query=pattern) return total, users, [] except BackendNotAvailable as exc: raise JobResultError("offline") from exc except BackendConnectionError as exc: raise JobResultError("error") from exc
async def run(self, client, token): try: r = await self.main_mc_recv.receive() assert r == self.Step.WaitPeer try: in_progress_ctx = await client.start_greeting_device( token=token) await self.job_mc_send.send((True, None)) except Exception as exc: await self.job_mc_send.send((False, exc)) r = await self.main_mc_recv.receive() assert r == self.Step.GetGreeterSas await self.job_mc_send.send(in_progress_ctx.greeter_sas) r = await self.main_mc_recv.receive() assert r == self.Step.WaitPeerTrust try: in_progress_ctx = await in_progress_ctx.do_wait_peer_trust() await self.job_mc_send.send((True, None)) except Exception as exc: await self.job_mc_send.send((False, exc)) r = await self.main_mc_recv.receive() assert r == self.Step.GetClaimerSas try: choices = in_progress_ctx.generate_claimer_sas_choices(size=4) await self.job_mc_send.send( (True, None, in_progress_ctx.claimer_sas, choices)) except Exception as exc: await self.job_mc_send.send((False, exc, None, None)) r = await self.main_mc_recv.receive() assert r == self.Step.SignifyTrust try: 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=client.device, device_label=in_progress_ctx.requested_device_label) await self.job_mc_send.send((True, None)) except InviteError as exc: await self.job_mc_send.send((False, exc)) except Exception as exc: await self.job_mc_send.send((False, exc)) except BackendNotAvailable as exc: raise JobResultError(status="backend-not-available", origin=exc)
async def _do_api_request(email, organization_id): data = json.dumps({"email": email, "organization_id": organization_id}).encode("utf-8") url = os.environ.get("BOOTSTRAP_API_URL", "quickjoin") req = Request(url, method="POST", data=data, headers={"Content-Type": "application/json"}) try: response = await trio.to_thread.run_sync(lambda: urlopen(req)) if response.status != 200: raise JobResultError("invalid_response") try: content = await trio.to_thread.run_sync(lambda: response.read()) content = json.loads(content) if content.get("error", None): raise JobResultError(content["error"]) return ( content["gid"], BackendOrganizationBootstrapAddr.from_url(content["bootstrap_link"]), ) except (TypeError, KeyError) as exc: raise JobResultError("invalid_response", exc=exc) except (HTTPException, URLError) as exc: raise JobResultError("offline", exc=exc)
async def _do_list_devices(client, page, pattern=None): try: devices, total = await client.get_user_devices_info( per_page=DEVICES_PER_PAGE, page=page) if pattern: devices_filtered = filter(lambda x: filter_devices(x, pattern), devices) # return all results without pagination return devices_filtered, DEVICES_PER_PAGE - 1 # When without filter : put the current device first curr_dev = None for i, device in enumerate(devices): if client.device.device_id == device.device_id: curr_dev = devices.pop(i) break if curr_dev: devices.insert(0, curr_dev) return devices, total except BackendNotAvailable as exc: raise JobResultError("offline") from exc except BackendConnectionError as exc: raise JobResultError("error") from exc
async def _do_get_users(client, workspace_fs): ret = {} try: participants = await workspace_fs.get_user_roles() updated_participants = {} for user, role in participants.items(): user_info = await client.get_user_info(user) updated_participants[user_info] = role # TODO: handle pagination users, _ = await client.find_humans() for user_info, role in updated_participants.items(): ret[user_info] = role for user_info in users: if user_info not in ret: ret[user_info] = NOT_SHARED_KEY return ret except BackendNotAvailable as exc: raise JobResultError("offline") from exc
async def _do_folder_create(workspace_fs, path): try: await workspace_fs.mkdir(path) except FileExistsError as exc: raise JobResultError("already-exists") from exc
async def run(self, addr, config): try: async with backend_invited_cmds_factory( addr=addr, keepalive=config.backend_connection_keepalive) as cmds: 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)) except Exception as exc: await self.job_oob_send.send((False, exc)) 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.ClaimDevice try: device_label = await self.main_oob_recv.receive() new_device = await in_progress_ctx.do_claim_device( requested_device_label=device_label) 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 BackendConnectionRefused as exc: raise JobResultError(status="invitation-not-found", origin=exc)
async def _get_reencryption_needs(workspace_fs): try: reenc_needs = await workspace_fs.get_reencryption_need() except FSBackendOfflineError as exc: raise JobResultError("offline") from exc return workspace_fs.workspace_id, reenc_needs
async def wait_peer_trust(self): await self.main_oob_send.send(self.Step.WaitPeerTrust) r, exc = await self.job_oob_recv.receive() if not r: raise JobResultError(status="wait-trust-failed", origin=exc)
async def signify_trust(self): await self.main_oob_send.send(self.Step.SignifyTrust) r, exc = await self.job_oob_recv.receive() if not r: raise JobResultError(status="signify-trust-failed", origin=exc)
async def get_greeter_sas(self): await self.main_oob_send.send(self.Step.GetGreeterSas) r, exc, greeter_sas, choices = await self.job_oob_recv.receive() if not r: raise JobResultError(status="get-greeter-sas-failed", origin=exc) return greeter_sas, choices
async def retrieve_info(self): await self.main_oob_send.send(self.Step.RetrieveInfo) r, exc = await self.job_oob_recv.receive() if not r: raise JobResultError(status="retrieve-info-failed", origin=exc)
async def create_new_user(self, human_handle, device_label, profile): await self.main_mc_send.send(self.Step.CreateNewUser) await self.main_mc_send.send((human_handle, device_label, profile)) r, exc = await self.job_mc_recv.receive() if not r: raise JobResultError(status="create-new-user-failed", origin=exc)
async def get_claim_requests(self): await self.main_mc_send.send(self.Step.GetClaimRequests) r, exc, human_handle, device_label = await self.job_mc_recv.receive() if not r: raise JobResultError(status="get-claim-request-failed", origin=exc) return human_handle, device_label
async def get_claimer_sas(self): await self.main_mc_send.send(self.Step.GetClaimerSas) r, exc, claimer_sas, choices = await self.job_mc_recv.receive() if not r: raise JobResultError(status="get-claimer-sas-failed", origin=exc) return claimer_sas, choices
async def wait_peer(self): await self.main_mc_send.send(self.Step.WaitPeer) r, exc = await self.job_mc_recv.receive() if not r: raise JobResultError(status="wait-peer-failed", origin=exc)