def on_valid_request(self, request_dict): """ Called when a valid JSON request has been sent from a client :param request_dict: JSON request converted to dict object """ cred_store = self.server.get_cred_store() auth_enabled = cred_store is not None has_auth_header = auth_enabled and "Authorization" in self.headers is_authorized = not auth_enabled or ( has_auth_header and cred_store.has_cred(self.headers["Authorization"])) if "User-Agent" not in self.headers or not self.headers[ "User-Agent"].startswith("HttpFsClient"): raise RuntimeError( "Invalid User-Agent header: Client is not an HttpFsClient") if not is_authorized: logging.error("{} is not authorized".format( self.client_address[0])) response = HttpFsResponse(errno.EACCES, {"message": "Invalid API key"}) self.send_json_response(http.HTTPStatus.UNAUTHORIZED, response.as_dict()) else: try: request = HttpFsRequest.from_dict(request_dict) self._delegate_request(request) except Exception as e: response = HttpFsResponse(errno.EIO, {"message": str(e)}) self.send_json_response(http.HTTPStatus.BAD_REQUEST, response.as_dict())
def on_readdir(self, httpfs_request_args): """ Called when HttpFsRequest.OP_READDIR is requested :param httpfs_request_args: The client request args dict """ path = self.get_abs_path(httpfs_request_args["path"]) uid = httpfs_request_args["uid"] gid = httpfs_request_args["gid"] file_stats = os.stat(path) is_owner = file_stats.st_uid == uid is_group = file_stats.st_gid == gid response_obj = HttpFsResponse() if uid == 0: access_ok = True elif is_owner: access_ok = file_stats.st_mode & stat.S_IRUSR elif is_group: access_ok = file_stats.st_mode & stat.S_IRGRP else: access_ok = file_stats.st_mode & stat.S_IROTH if access_ok: dir_listing = os.listdir(path) dir_listing = [".", ".."] + dir_listing response_obj.set_data({"dir_listing": dir_listing}) else: logging.warning("Error during readdir request: Access denied") response_obj.set_err_no(errno.EACCES) response_obj.set_data({"message": "Access denied"}) self.send_json_response(200, response_obj.as_dict())
def fake_send_request(request_type, **kwargs): assert request_type == HttpFsRequest.OP_CHMOD for required_key in ["uid", "gid"]: assert required_key in kwargs.keys() assert kwargs["path"] == fake_path assert kwargs["mode"] == fake_mode return HttpFsResponse()
def fake_send_request(request_type, **kwargs): for arg in ["uid", "gid"]: assert arg in kwargs.keys() assert request_type == HttpFsRequest.OP_OPEN assert kwargs["path"] == fake_path assert kwargs["flags"] == fake_flags return HttpFsResponse()
def fake_send_request(request_type, **kwargs): for arg in ["uid", "gid"]: assert arg in kwargs.keys() assert request_type == HttpFsRequest.OP_RENAME assert kwargs["old_path"] == fake_old_path assert kwargs["new_path"] == fake_new_path return HttpFsResponse()
def fake_send_request(request_type, **kwargs): for arg in ["uid", "gid"]: assert arg in kwargs.keys() assert request_type == HttpFsRequest.OP_UTIMENS assert kwargs["path"] == fake_path assert kwargs["times"] == fake_times return HttpFsResponse()
def on_rmdir(self, httpfs_request_args): """ Called when HttpFsRequest.OP_RMDIR is requested :param httpfs_request_args: The client request args dict """ response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) _error = None _err = None try: os.rmdir(path) except FileNotFoundError as e: logging.error("{} not found".format(path)) err = errno.ENOENT _error = e except OSError as e: err = e.errno _error = e except Exception as e: err = errno.EIO _error = e if _error != None: logging.error("Error during rmdir request: {}".format(_error)) response_obj.set_err_no(_err) response_obj.set_data({"message": str(_error)}) self.send_json_response(200, response_obj.as_dict())
def fake_send_request(request_type, **kwargs): for arg in ["uid", "gid"]: assert arg in kwargs.keys() assert request_type == HttpFsRequest.OP_READDIR assert kwargs["path"] == fake_path resp = HttpFsResponse() resp._response_data["dir_listing"] = list() return resp
def fake_send_request(request_type, **kwargs): assert request_type == HttpFsRequest.OP_CREATE for required_key in ["uid", "gid"]: assert required_key in kwargs.keys() assert kwargs["path"] == fake_path assert kwargs["mode"] == fake_mode resp = HttpFsResponse() resp._response_data["file_descriptor"] = 0 return resp
def fake_send_request(request_type, **kwargs): for arg in ["uid", "gid"]: assert arg in kwargs.keys() assert request_type == HttpFsRequest.OP_WRITE assert kwargs["file_descriptor"] == fake_fd assert kwargs["data"] == base64.standard_b64encode(fake_data).decode("utf-8") assert kwargs["offset"] == fake_offset resp = HttpFsResponse() resp._response_data["bytes_written"] = 10 return resp
def on_invalid_request(self, err_msg): """ Called when invalid JSON has been sent as a request from a client :param err_msg: The error message """ logging.debug("Invalid request received from {}: '{}'".format( self.client_address[0], err_msg)) response_obj = HttpFsResponse(errno.EIO, {"message": err_msg}) self.send_json_response(http.HTTPStatus.BAD_REQUEST, response_obj.as_dict())
def fake_send_request(request_type, **kwargs): for arg in ["uid", "gid"]: assert arg in kwargs.keys() assert request_type == HttpFsRequest.OP_READ assert kwargs["file_descriptor"] == fake_fd assert kwargs["size"] == fake_size assert kwargs["offset"] == fake_offset resp = HttpFsResponse() resp._response_data["bytes_read"] = bytes() return resp
def on_statfs(self, httpfs_request_args): """ Called when HttpFsRequest.OP_STAT_FS is received from the client :param httpfs_request_args: The client request arg dict """ fs_path = self.get_abs_path(httpfs_request_args["path"]) statfs_os_result = os.statvfs(fs_path) statfs_result = dict() for k in _HttpFsRequestHandler.STAT_FS_KEYS: statfs_result[k] = getattr(statfs_os_result, k) response_obj = HttpFsResponse(response_data=statfs_result) self.send_json_response(200, response_obj.as_dict())
def on_release(self, httpfs_request_args): """ Called when HttpFsRequest.OP_RELEASE is requested :param httpfs_request_args: The client request args dict """ response_obj = HttpFsResponse() try: os.close(httpfs_request_args["file_descriptor"]) except Exception as e: logging.error("Error during release request: {}".format(e)) response_obj.set_err_no(errno.EIO) self.send_json_response(200, response_obj.as_dict())
def on_flush(self, httpfs_request_args): """ Called when HttpFsRequest.OP_FLUSH is received from the client :param httpfs_request_args: The client request arg dict """ response_obj = HttpFsResponse() try: os.fsync(httpfs_request_args["file_descriptor"]) except Exception as e: logging.error("Error during flush request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_mkdir(self, httpfs_request_args): """ Called when HttpFsRequest.OP_MKDIR is requested :param httpfs_request_args: The client request args dict """ response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) try: os.mkdir(path, mode=httpfs_request_args["mode"]) except Exception as e: logging.error("Error during mkdir request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_symlink(self, httpfs_request_args): """ Called when HttpFsRequest.OP_SYMLINK is received from the client :param httpfs_request_args: The client request arg dict """ response_obj = HttpFsResponse() source = self.get_abs_path(httpfs_request_args["source"]) target = self.get_abs_path(httpfs_request_args["target"]) try: os.symlink(source, target) except Exception as e: logging.error("Error during symlink request: {}".format(e)) response_obj.set_err_no(errno.EIO) self.send_json_response(200, response_obj.as_dict())
def on_read(self, httpfs_request_args): """ Called when HttpFsRequest.OP_READ is received from the client :param httpfs_request_args: The client request arg dict """ response_obj = HttpFsResponse() file_descriptor = httpfs_request_args["file_descriptor"] offset = httpfs_request_args["offset"] size = httpfs_request_args["size"] uid = httpfs_request_args["uid"] gid = httpfs_request_args["gid"] file_stats = os.stat(file_descriptor) is_owner = file_stats.st_uid == uid is_group = file_stats.st_gid == gid if uid == 0: access_ok = True elif is_owner: access_ok = file_stats.st_mode & stat.S_IRUSR elif is_group: access_ok = file_stats.st_mode & stat.S_IRGRP else: access_ok = file_stats.st_mode & stat.S_IROTH try: if access_ok: with self.server.get_fs_lock(): os.lseek(file_descriptor, offset, os.SEEK_SET) bytes_read = os.read(file_descriptor, size) response_obj.set_data({ "bytes_read": base64.standard_b64encode(bytes_read).decode("utf-8") }) else: logging.warning("Error during read request: Access denied") response_obj.set_err_no(errno.EACCES) response_obj.set_data({"message": "Access denied"}) except Exception as e: logging.error("Error during read request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_link(self, httpfs_request_args): """ Called when HttpFsRequest.OP_LINK is requested :param httpfs_request_args: The client request args dict """ target_path = self.get_abs_path(httpfs_request_args["target"]) source_path = self.get_abs_path(httpfs_request_args["source"]) response_obj = HttpFsResponse() try: os.link(source_path, target_path) except Exception as e: logging.error("Error during link request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_truncate(self, httpfs_request_args): """ Called when HttpFsRequest.OP_TRUNCATE is received from the client :param httpfs_request_args: The client request arg dict """ response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) length = httpfs_request_args["length"] try: with open(path, 'r+') as f: f.truncate(length) except Exception as e: logging.error("Error during truncate request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(200, response_obj.as_dict())
def on_readlink(self, httpfs_request_args): # TODO: This doesn't work """ Called when HttpFsRequest.OP_RELEASE is requested :param httpfs_request_args: The client request args dict """ response_obj = HttpFsResponse() link_path = self.get_abs_path(httpfs_request_args["link_path"]) try: target = os.readlink(link_path) if target.startswith("/"): target = os.path.relpath(target, self.server.get_fs_root()) response_obj.set_data({"target": target}) except Exception as e: logging.error("Error during readlink request: {}".format(e)) response_obj.set_err_no(errno.EIO) self.send_json_response(200, response_obj.as_dict())
def on_create(self, httpfs_request_args): """ Called when HttpFsRequest.OP_ACCESS is received from the client :param httpfs_request_args: The client request arg dict """ response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) uid = httpfs_request_args["uid"] gid = httpfs_request_args["gid"] dir_stats = os.stat(os.path.dirname(path)) is_dir_owner = dir_stats.st_uid == uid is_dir_group = dir_stats.st_gid == gid if uid == 0: access_ok = True elif is_dir_owner: access_ok = dir_stats.st_mode & stat.S_IWUSR elif is_dir_group: access_ok = dir_stats.st_mode & stat.S_IWGRP else: access_ok = dir_stats.st_mode & stat.S_IWOTH try: if access_ok: fd = os.open(path, flags=os.O_WRONLY | os.O_CREAT | os.O_TRUNC | os.O_ASYNC | os.O_NOATIME, mode=httpfs_request_args["mode"]) os.chown(path, uid, gid) response_obj.set_data({"file_descriptor": fd}) else: logging.warning("Error during create request: Access denied") response_obj.set_err_no(errno.EACCES) response_obj.set_data({"message": "Access denied"}) except Exception as e: logging.error("Error during create request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_chown(self, httpfs_request_args): """ Called when HttpFsRequest.OP_FLUSH is received from the client :param httpfs_request_args: The client request arg dict """ client = self.client_address[0] response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) uid = httpfs_request_args["uid"] gid = httpfs_request_args["gid"] caller_uid = httpfs_request_args["caller_uid"] caller_gid = httpfs_request_args["caller_gid"] file_stats = os.stat(path) is_owner = file_stats.st_uid == caller_uid is_group = file_stats.st_gid == caller_gid if caller_uid == 0: access_ok = True elif is_owner: access_ok = file_stats.st_mode & stat.S_IWUSR elif is_group: access_ok = file_stats.st_mode & stat.S_IWGRP else: access_ok = file_stats.st_mode & stat.S_IWOTH # TODO: Don't let me if it isn't mine try: if access_ok: os.chown(path, uid, gid) logging.debug("Successful chown for {}".format(client)) else: response_obj.set_err_no(errno.EACCES) response_obj.set_data({"message": "Access denied"}) logging.warning("Error during chown request: Access denied") except Exception as e: logging.error("Error during chown request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_utimens(self, httpfs_request_args): """ Called when HttpFsRequest.OP_UTIMENS is received from the client :param httpfs_request_args: The client request arg dict """ response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) times = httpfs_request_args["times"] if isinstance(times, list): times = tuple(times) uid = httpfs_request_args["uid"] gid = httpfs_request_args["gid"] file_stats = os.stat(path) is_owner = file_stats.st_uid == uid is_group = file_stats.st_gid == gid if uid == 0: access_ok = True elif is_owner: access_ok = file_stats.st_mode & stat.S_IWUSR elif is_group: access_ok = file_stats.st_mode & stat.S_IWGRP else: access_ok = file_stats.st_mode & stat.S_IWOTH try: if access_ok: os.utime(path, times) else: logging.warning("Error during write request: Access denied") response_obj.set_err_no(errno.EACCES) response_obj.set_data({"message": "Access denied"}) except Exception as e: logging.error("Error during utimens request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_unlink(self, httpfs_request_args): """ Called when HttpFsRequest.OP_UNLINK is received from the client :param httpfs_request_args: The client request arg dict """ response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) uid = httpfs_request_args["uid"] gid = httpfs_request_args["gid"] try: file_stats = os.stat(path) is_owner = file_stats.st_uid == uid is_group = file_stats.st_gid == gid if uid == 0: access_ok = True elif is_owner: access_ok = file_stats.st_mode & stat.S_IWUSR elif is_group: access_ok = file_stats.st_mode & stat.S_IWGRP else: access_ok = file_stats.st_mode & stat.S_IWOTH if access_ok: os.unlink(path) else: logging.warning("Error during unlink request: Access denied") response_obj.set_err_no(errno.EACCES) response_obj.set_data({"message": "Access denied"}) except OSError as e: logging.error("Error during unlink request: {}".format(e)) response_obj.set_err_no(e.errno) response_obj.set_data({"message": str(e)}) except Exception as e: logging.error("Error during unlink request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(200, response_obj.as_dict())
def on_rename(self, httpfs_request_args): """ Called when HttpFsRequest.OP_RENAME is requested :param httpfs_request_args: The client request args dict """ old_path = self.get_abs_path(httpfs_request_args["old_path"]) new_path = self.get_abs_path(httpfs_request_args["new_path"]) uid = httpfs_request_args["uid"] gid = httpfs_request_args["gid"] file_stats = os.stat(old_path) is_owner = file_stats.st_uid == uid is_group = file_stats.st_gid == gid response_obj = HttpFsResponse() if uid == 0: access_ok = True elif is_owner: access_ok = file_stats.st_mode & stat.S_IWUSR elif is_group: access_ok = file_stats.st_mode & stat.S_IWGRP else: access_ok = file_stats.st_mode & stat.S_IWOTH try: if access_ok: os.rename(old_path, new_path) else: logging.warning("Error during rename request: Access denied") response_obj.set_err_no(errno.EACCES) response_obj.set_data({"message": "Access denied"}) except Exception as e: logging.error("Error during rename request: {}".format(e)) response_obj.set_err_no(errno.EIO) response_obj.set_data({"message": str(e)}) self.send_json_response(http.HTTPStatus.OK, response_obj.as_dict())
def on_getattr(self, httpfs_request_args): """ Called when HttpFsRequest.OP_GET_ATTR is requested :param httpfs_request_args: The client request args dict """ response_obj = HttpFsResponse() path = self.get_abs_path(httpfs_request_args["path"]) try: os_attrs = os.lstat(path) attrs = dict() for k in _HttpFsRequestHandler.GETATTR_KEYS: attrs[k] = getattr(os_attrs, k) response_obj.set_data(attrs) except FileNotFoundError: logging.warning("{} not found".format(path)) response_obj.set_err_no(errno.ENOENT) self.send_json_response(200, response_obj.as_dict())
def fake_send_request(request_type, **kwargs): assert request_type == HttpFsRequest.OP_TRUNCATE assert kwargs["path"] == fake_path assert kwargs["length"] == fake_len return HttpFsResponse()
def fake_send_request(request_type, **kwargs): assert request_type == HttpFsRequest.OP_SYMLINK assert kwargs["target"] == fake_target assert kwargs["source"] == fake_source return HttpFsResponse()
def fake_send_request(request_type, **kwargs): assert request_type == HttpFsRequest.OP_STAT_FS assert kwargs["path"] == fake_path return HttpFsResponse()