From 89bdd04ff8ebeb1a5cfbbede6be327ea82708e1b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 25 May 2025 11:03:49 +1200 Subject: [PATCH] Broken --- README.md | 13 ++--- src/server.cpp | 127 ++++++++++++++++++++++++++++---------------- test.sh | 8 ++- test.sh.downloaded1 | 1 + test.sh.downloaded2 | 1 + 5 files changed, 95 insertions(+), 55 deletions(-) create mode 100644 test.sh.downloaded1 create mode 100644 test.sh.downloaded2 diff --git a/README.md b/README.md index f96023c..efd7c1a 100644 --- a/README.md +++ b/README.md @@ -42,15 +42,10 @@ Write actions: - to upload a file (via http put) ``` curl -X PUT \ - -H "Content-Type: application/json" \ - -d '{ - "label": "example", - "filename": "example.txt", - "description": "Example file", - "tags": ["test", "example"], - "custom_field": "custom value" - }' \ - "http://localhost:8123/upload?token=YOUR_TOKEN" + -H "Authorization: Bearer YOUR_TOKEN" \ + -F "file=@/path/to/your/file.txt" \ + -F 'metadata={"labeltag":"example:latest","description":"Example file","tags":["test","example"],"custom_field":"custom value"}' \ + "http://localhost:8123/upload" ``` - the object_file is uploaded, hashed, added to the registry (if that hash doesn't already exist), and {label:tag,hash} entries are added to the directory index. - matching tags on older versions are removed. diff --git a/src/server.cpp b/src/server.cpp index 7707ae3..189e5c2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -51,6 +51,33 @@ bool Server::init_db() { bool Server::validate_write_request(const httplib::Request &req, httplib::Response &res, const std::vector &required_params, std::map ¶ms) { + // Get token from Authorization header + std::string token; + if (req.has_header("Authorization")) { + const auto& auth_header = req.get_header_value("Authorization"); + // Check if it's a Bearer token + if (auth_header.substr(0, 7) == "Bearer ") { + token = auth_header.substr(7); + } + } + + if (token.empty()) { + res.status = 401; + nlohmann::json response = {{"result", "error"}, {"error", "Missing or invalid Authorization header"}}; + res.set_content(response.dump(), "application/json"); + return false; + } + + // Check if token is valid + bool write_token_valid = std::find(config_.write_tokens.begin(), config_.write_tokens.end(), token) != config_.write_tokens.end(); + if (!write_token_valid) { + res.status = 403; + nlohmann::json response = {{"result", "error"}, {"error", "Invalid write token"}}; + res.set_content(response.dump(), "application/json"); + return false; + } + + // Get other parameters from query params for (const auto& param : req.params) { params[param.first] = param.second; } @@ -65,15 +92,6 @@ bool Server::validate_write_request(const httplib::Request &req, httplib::Respon } } - // check token is valid - bool write_token_valid = std::find(config_.write_tokens.begin(), config_.write_tokens.end(), params["token"]) != config_.write_tokens.end(); - if (!write_token_valid) { - res.status = 403; - nlohmann::json response = {{"result", "error"}, {"error", "Invalid write token"}}; - res.set_content(response.dump(), "application/json"); - return false; - } - return true; } @@ -281,7 +299,7 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r // Check all request parameters first before processing any data std::map params; - if (!validate_write_request(req, res, {"token", "labeltag", "filename"}, params)) { + if (!validate_write_request(req, res, {}, params)) { // No required params now since token is in header return; } @@ -293,7 +311,42 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r return; } - auto [label, tag] = parse_label_tag(params["labeltag"]); + // Parse the multipart form data + if (!req.has_file("file")) { + res.status = 400; + nlohmann::json response = {{"result", "error"}, {"error", "No file provided in upload"}}; + res.set_content(response.dump(), "application/json"); + return; + } + + // Get the file data + const auto& file = req.get_file_value("file"); + + // Parse metadata if provided + nlohmann::json metadata; + if (req.has_file("metadata")) { + try { + const auto& metadata_file = req.get_file_value("metadata"); + metadata = nlohmann::json::parse(metadata_file.content); + } catch (const nlohmann::json::parse_error& e) { + res.status = 400; + nlohmann::json response = {{"result", "error"}, {"error", "Invalid JSON metadata: " + std::string(e.what())}}; + res.set_content(response.dump(), "application/json"); + return; + } + } + + // Validate required metadata fields + if (!metadata.contains("labeltag")) { + res.status = 400; + nlohmann::json response = {{"result", "error"}, {"error", "Missing required metadata field: labeltag"}}; + res.set_content(response.dump(), "application/json"); + return; + } + + // Extract labeltag and validate format + std::string labeltag = metadata["labeltag"]; + auto [label, tag] = parse_label_tag(labeltag); if (label.empty() || tag.empty()) { res.status = 400; nlohmann::json response = {{"result", "error"}, {"error", "Invalid label:tag format"}}; @@ -301,6 +354,11 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r return; } + // Add filename to metadata if not provided + if (!metadata.contains("filename")) { + metadata["filename"] = file.filename; + } + // Now that all parameters are validated, process the upload // Generate a random number for the temporary filename @@ -319,29 +377,14 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r return; } - // Write request body to temporary file in chunks to handle large files better - // This improves memory usage even though the entire body is still loaded - // by httplib - a proper streaming solution would require changes to httplib - const size_t CHUNK_SIZE = 1024 * 1024; // 1MB chunks - size_t remaining = req.body.size(); - size_t offset = 0; - - while (remaining > 0) { - size_t write_size = std::min(CHUNK_SIZE, remaining); - if (!temp_file.write(req.body.data() + offset, write_size)) { - res.status = 500; - nlohmann::json response = {{"result", "error"}, {"error", "Failed to write to temporary file"}}; - res.set_content(response.dump(), "application/json"); - temp_file.close(); - std::filesystem::remove(temp_path); - return; - } - - offset += write_size; - remaining -= write_size; - - // Periodically flush to disk - temp_file.flush(); + // Write file content to temporary file + if (!temp_file.write(file.content.c_str(), file.content.size())) { + res.status = 500; + nlohmann::json response = {{"result", "error"}, {"error", "Failed to write to temporary file"}}; + res.set_content(response.dump(), "application/json"); + temp_file.close(); + std::filesystem::remove(temp_path); + return; } temp_file.close(); @@ -358,20 +401,14 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r return; } - nlohmann::json metadata; - - // Check for filename query parameter - std::string filename = ""; - if (req.has_param("filename")) - metadata["original_filename"] = params["filename"]; + // Add file metadata + add_file_metadata(temp_path.string(), metadata); // Check if filename ends with ".tgz" using the utility function - if (utils::ends_with(params["filename"], ".tgz")) { + if (utils::ends_with(metadata["filename"], ".tgz")) { metadata["tgz_content_hash"] = get_hash_from_tgz(temp_path.string()); } - add_file_metadata(temp_path.string(), metadata); - // Move file to final location std::filesystem::path final_path = config_.object_store_path / std::to_string(hash); if (!std::filesystem::exists(final_path)) { @@ -389,9 +426,9 @@ void Server::handle_put_object(const httplib::Request& req, httplib::Response& r // Update database index dbEntry entry; - entry.label_tag = params["labeltag"]; + entry.label_tag = labeltag; entry.hash = std::to_string(hash); - entry.metadata = metadata; // Store the potentially updated metadata + entry.metadata = metadata; // Store the complete metadata if (!db_->update_or_insert(entry)) { res.status = 500; diff --git a/test.sh b/test.sh index c328612..940b876 100755 --- a/test.sh +++ b/test.sh @@ -53,7 +53,13 @@ BASE_TAG="autotest" # upload this script as an object echo "uploading ${SCRIPT_DIR}/${SCRIPT_NAME} to ${BASE_TAG}:test1" -OBJECT_HASH=$(curl -s "${BASE_URL}/upload?token=${WRITE_TOKEN}&labeltag=${BASE_TAG}:test1&filename=${SCRIPT_NAME}" -T ${SCRIPT_DIR}/${SCRIPT_NAME} | jq -r '.hash') +OBJECT_HASH=$(curl -X PUT \ + -H "Authorization: Bearer ${WRITE_TOKEN}" \ + -F "file=@${SCRIPT_DIR}/${SCRIPT_NAME}" \ + -F 'metadata={"labeltag":"${BASE_TAG}:test1","description":"Example file","tags":["test","example"],"custom_field":"custom value"}' \ + "http://localhost:8123/upload" | jq -r '.hash') + +#OBJECT_HASH=$(curl -s "${BASE_URL}/upload?token=${WRITE_TOKEN}&labeltag=${BASE_TAG}:test1&filename=${SCRIPT_NAME}" -T ${SCRIPT_DIR}/${SCRIPT_NAME} | jq -r '.hash') echo "received hash ${OBJECT_HASH}" # check the hash matches. diff --git a/test.sh.downloaded1 b/test.sh.downloaded1 new file mode 100644 index 0000000..f45d37b --- /dev/null +++ b/test.sh.downloaded1 @@ -0,0 +1 @@ +{"error":"Invalid hash: null","result":"error"} \ No newline at end of file diff --git a/test.sh.downloaded2 b/test.sh.downloaded2 new file mode 100644 index 0000000..3462ca2 --- /dev/null +++ b/test.sh.downloaded2 @@ -0,0 +1 @@ +{"error":"Invalid hash: autotest:test1","result":"error"} \ No newline at end of file