aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-03-01 11:15:16 +0100
committerAlex Auvolat <alex@adnab.me>2022-03-01 11:15:16 +0100
commit8a5bbc3b0b1c6ab252d0c98950456a9d4cc2e9fe (patch)
treefc58f965981debc989108c39379dfca3c4da5e93
parent97f245f218836c699088ab51d810f33ae947c903 (diff)
downloadgarage-better-cors.tar.gz
garage-better-cors.zip
More permissive OPTIONS on S3 APIv0.6.1better-cors
-rw-r--r--src/api/api_server.rs2
-rw-r--r--src/api/s3_cors.rs64
-rw-r--r--src/web/web_server.rs4
3 files changed, 50 insertions, 20 deletions
diff --git a/src/api/api_server.rs b/src/api/api_server.rs
index 5ac78bf4..322ea298 100644
--- a/src/api/api_server.rs
+++ b/src/api/api_server.rs
@@ -116,7 +116,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
return handle_post_object(garage, req, bucket_name.unwrap()).await;
}
if let Endpoint::Options = endpoint {
- return handle_options(garage, &req, bucket_name).await;
+ return handle_options_s3api(garage, &req, bucket_name).await;
}
let (api_key, content_sha256) = check_payload_signature(&garage, &req).await?;
diff --git a/src/api/s3_cors.rs b/src/api/s3_cors.rs
index 7dc48d8e..e3deaeda 100644
--- a/src/api/s3_cors.rs
+++ b/src/api/s3_cors.rs
@@ -100,33 +100,63 @@ pub async fn handle_put_cors(
.body(Body::empty())?)
}
-pub async fn handle_options(
+pub async fn handle_options_s3api(
garage: Arc<Garage>,
req: &Request<Body>,
bucket_name: Option<String>,
) -> Result<Response<Body>, Error> {
- let bucket = if let Some(bn) = bucket_name {
+ // FIXME: CORS rules of buckets with local aliases are
+ // not taken into account.
+
+ // If the bucket name is a global bucket name,
+ // we try to apply the CORS rules of that bucket.
+ // If a user has a local bucket name that has
+ // the same name, its CORS rules won't be applied
+ // and will be shadowed by the rules of the globally
+ // existing bucket (but this is inevitable because
+ // OPTIONS calls are not auhtenticated).
+ if let Some(bn) = bucket_name {
let helper = garage.bucket_helper();
- let bucket_id = helper
- .resolve_global_bucket_name(&bn)
- .await?
- .ok_or(Error::NoSuchBucket)?;
- garage
- .bucket_table
- .get(&EmptyKey, &bucket_id)
- .await?
- .filter(|b| !b.state.is_deleted())
- .ok_or(Error::NoSuchBucket)?
+ let bucket_id = helper.resolve_global_bucket_name(&bn).await?;
+ if let Some(id) = bucket_id {
+ let bucket = garage
+ .bucket_table
+ .get(&EmptyKey, &id)
+ .await?
+ .filter(|b| !b.state.is_deleted())
+ .ok_or(Error::NoSuchBucket)?;
+ handle_options_for_bucket(req, &bucket)
+ } else {
+ // If there is a bucket name in the request, but that name
+ // does not correspond to a global alias for a bucket,
+ // then it's either a non-existing bucket or a local bucket.
+ // We have no way of knowing, because the request is not
+ // authenticated and thus we can't resolve local aliases.
+ // We take the permissive approach of allowing everything,
+ // because we don't want to prevent web apps that use
+ // local bucket names from making API calls.
+ Ok(Response::builder()
+ .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
+ .header(ACCESS_CONTROL_ALLOW_METHODS, "*")
+ .status(StatusCode::OK)
+ .body(Body::empty())?)
+ }
} else {
- // The only supported API call that doesn't use a bucket name is ListBuckets,
- // which we want to allow in all cases
- return Ok(Response::builder()
+ // If there is no bucket name in the request,
+ // we are doing a ListBuckets call, which we want to allow
+ // for all origins.
+ Ok(Response::builder()
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.header(ACCESS_CONTROL_ALLOW_METHODS, "GET")
.status(StatusCode::OK)
- .body(Body::empty())?);
- };
+ .body(Body::empty())?)
+ }
+}
+pub fn handle_options_for_bucket(
+ req: &Request<Body>,
+ bucket: &Bucket,
+) -> Result<Response<Body>, Error> {
let origin = req
.headers()
.get("Origin")
diff --git a/src/web/web_server.rs b/src/web/web_server.rs
index 15935cba..80d2feb9 100644
--- a/src/web/web_server.rs
+++ b/src/web/web_server.rs
@@ -13,7 +13,7 @@ use crate::error::*;
use garage_api::error::{Error as ApiError, OkOrBadRequest, OkOrInternalError};
use garage_api::helpers::{authority_to_host, host_to_bucket};
-use garage_api::s3_cors::{add_cors_headers, find_matching_cors_rule, handle_options};
+use garage_api::s3_cors::{add_cors_headers, find_matching_cors_rule, handle_options_for_bucket};
use garage_api::s3_get::{handle_get, handle_head};
use garage_model::garage::Garage;
@@ -133,7 +133,7 @@ async fn serve_file(garage: Arc<Garage>, req: &Request<Body>) -> Result<Response
);
let ret_doc = match *req.method() {
- Method::OPTIONS => handle_options(garage.clone(), req, Some(bucket_name.to_string())).await,
+ Method::OPTIONS => handle_options_for_bucket(req, &bucket),
Method::HEAD => handle_head(garage.clone(), req, bucket_id, &key, None).await,
Method::GET => handle_get(garage.clone(), req, bucket_id, &key, None).await,
_ => Err(ApiError::BadRequest("HTTP method not supported".into())),