diff options
author | KokaKiwi <kokakiwi+git@kokakiwi.net> | 2022-02-18 17:05:19 +0100 |
---|---|---|
committer | Gitea <gitea@fake.local> | 2022-03-23 10:22:36 +0100 |
commit | 98545a16dd9e469271fe480cc6f8d9991cd25444 (patch) | |
tree | b78bf697fc5507e31f177ea2c04990bac7879d45 /src/api/api_server.rs | |
parent | 822128e3c87b054eeab3696338dc688be3838e6f (diff) | |
download | garage-98545a16dd9e469271fe480cc6f8d9991cd25444.tar.gz garage-98545a16dd9e469271fe480cc6f8d9991cd25444.zip |
garage_api: Handle streaming payload early in request handling
Diffstat (limited to 'src/api/api_server.rs')
-rw-r--r-- | src/api/api_server.rs | 57 |
1 files changed, 54 insertions, 3 deletions
diff --git a/src/api/api_server.rs b/src/api/api_server.rs index a6bf5a44..e7b86d9e 100644 --- a/src/api/api_server.rs +++ b/src/api/api_server.rs @@ -1,7 +1,9 @@ use std::net::SocketAddr; use std::sync::Arc; +use chrono::{DateTime, NaiveDateTime, Utc}; use futures::future::Future; +use futures::prelude::*; use hyper::header; use hyper::server::conn::AddrStream; use hyper::service::{make_service_fn, service_fn}; @@ -24,7 +26,10 @@ use garage_model::key_table::Key; use garage_table::util::*; use crate::error::*; +use crate::signature::compute_scope; use crate::signature::payload::check_payload_signature; +use crate::signature::streaming::SignedPayloadStream; +use crate::signature::LONG_DATETIME; use crate::helpers::*; use crate::s3_bucket::*; @@ -158,7 +163,7 @@ async fn handler_stage2( let authority = req .headers() .get(header::HOST) - .ok_or_else(|| Error::BadRequest("HOST header required".to_owned()))? + .ok_or_bad_request("Host header required")? .to_str()?; let host = authority_to_host(authority)?; @@ -221,11 +226,57 @@ async fn handler_stage3( return handle_options_s3api(garage, &req, bucket_name).await; } - let (api_key, content_sha256) = check_payload_signature(&garage, &req).await?; + let (api_key, mut content_sha256) = check_payload_signature(&garage, &req).await?; let api_key = api_key.ok_or_else(|| { Error::Forbidden("Garage does not support anonymous access yet".to_string()) })?; + let req = match req.headers().get("x-amz-content-sha256") { + Some(header) if header == "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" => { + let signature = content_sha256 + .take() + .ok_or_bad_request("No signature provided")?; + + let secret_key = &api_key + .state + .as_option() + .ok_or_internal_error("Deleted key state")? + .secret_key; + + let date = req + .headers() + .get("x-amz-date") + .ok_or_bad_request("Missing X-Amz-Date field")? + .to_str()?; + let date: NaiveDateTime = NaiveDateTime::parse_from_str(date, LONG_DATETIME) + .ok_or_bad_request("Invalid date")?; + let date: DateTime<Utc> = DateTime::from_utc(date, Utc); + + let scope = compute_scope(&date, &garage.config.s3_api.s3_region); + let signing_hmac = crate::signature::signing_hmac( + &date, + secret_key, + &garage.config.s3_api.s3_region, + "s3", + ) + .ok_or_internal_error("Unable to build signing HMAC")?; + + req.map(move |body| { + Body::wrap_stream( + SignedPayloadStream::new( + body.map_err(Error::from), + signing_hmac, + date, + &scope, + signature, + ) + .map_err(Error::from), + ) + }) + } + _ => req, + }; + let bucket_name = match bucket_name { None => return handle_request_without_bucket(garage, req, api_key, endpoint).await, Some(bucket) => bucket.to_string(), @@ -307,7 +358,7 @@ async fn handler_stage3( .await } Endpoint::PutObject { key } => { - handle_put(garage, req, bucket_id, &key, &api_key, content_sha256).await + handle_put(garage, req, bucket_id, &key, content_sha256).await } Endpoint::AbortMultipartUpload { key, upload_id } => { handle_abort_multipart_upload(garage, bucket_id, &key, &upload_id).await |