forked from Deuxfleurs/garage
refactor s3_router and api_server to make unused Endpoint parameters more obvious
This commit is contained in:
parent
7c049f1c94
commit
178e35f868
2 changed files with 145 additions and 255 deletions
|
@ -107,25 +107,25 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|root_domain| host_to_bucket(&host, root_domain));
|
.and_then(|root_domain| host_to_bucket(&host, root_domain));
|
||||||
|
|
||||||
let endpoint = Endpoint::from_request(&req, bucket.map(ToOwned::to_owned))?;
|
let (endpoint, bucket) = Endpoint::from_request(&req, bucket.map(ToOwned::to_owned))?;
|
||||||
debug!("Endpoint: {:?}", endpoint);
|
debug!("Endpoint: {:?}", endpoint);
|
||||||
|
|
||||||
// Special code path for CreateBucket API endpoint
|
let bucket_name = match bucket {
|
||||||
if let Endpoint::CreateBucket { bucket } = endpoint {
|
|
||||||
return handle_create_bucket(&garage, req, content_sha256, api_key, bucket).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bucket_name = match endpoint.get_bucket() {
|
|
||||||
None => return handle_request_without_bucket(garage, req, api_key, endpoint).await,
|
None => return handle_request_without_bucket(garage, req, api_key, endpoint).await,
|
||||||
Some(bucket) => bucket.to_string(),
|
Some(bucket) => bucket.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Special code path for CreateBucket API endpoint
|
||||||
|
if let Endpoint::CreateBucket {} = endpoint {
|
||||||
|
return handle_create_bucket(&garage, req, content_sha256, api_key, bucket_name).await;
|
||||||
|
}
|
||||||
|
|
||||||
let bucket_id = resolve_bucket(&garage, &bucket_name, &api_key).await?;
|
let bucket_id = resolve_bucket(&garage, &bucket_name, &api_key).await?;
|
||||||
|
|
||||||
let allowed = match endpoint.authorization_type() {
|
let allowed = match endpoint.authorization_type() {
|
||||||
Authorization::Read(_) => api_key.allow_read(&bucket_id),
|
Authorization::Read => api_key.allow_read(&bucket_id),
|
||||||
Authorization::Write(_) => api_key.allow_write(&bucket_id),
|
Authorization::Write => api_key.allow_write(&bucket_id),
|
||||||
Authorization::Owner(_) => api_key.allow_owner(&bucket_id),
|
Authorization::Owner => api_key.allow_owner(&bucket_id),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,7 +142,6 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
key,
|
key,
|
||||||
part_number,
|
part_number,
|
||||||
upload_id,
|
upload_id,
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
handle_put_part(
|
handle_put_part(
|
||||||
garage,
|
garage,
|
||||||
|
@ -155,14 +154,11 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Endpoint::CopyObject { key, .. } => {
|
Endpoint::CopyObject { key } => handle_copy(garage, &api_key, &req, bucket_id, &key).await,
|
||||||
handle_copy(garage, &api_key, &req, bucket_id, &key).await
|
|
||||||
}
|
|
||||||
Endpoint::UploadPartCopy {
|
Endpoint::UploadPartCopy {
|
||||||
key,
|
key,
|
||||||
part_number,
|
part_number,
|
||||||
upload_id,
|
upload_id,
|
||||||
..
|
|
||||||
} => {
|
} => {
|
||||||
handle_upload_part_copy(
|
handle_upload_part_copy(
|
||||||
garage,
|
garage,
|
||||||
|
@ -175,25 +171,21 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Endpoint::PutObject { key, .. } => {
|
Endpoint::PutObject { key } => {
|
||||||
handle_put(garage, req, bucket_id, &key, &api_key, content_sha256).await
|
handle_put(garage, req, bucket_id, &key, &api_key, content_sha256).await
|
||||||
}
|
}
|
||||||
Endpoint::AbortMultipartUpload { key, upload_id, .. } => {
|
Endpoint::AbortMultipartUpload { key, upload_id } => {
|
||||||
handle_abort_multipart_upload(garage, bucket_id, &key, &upload_id).await
|
handle_abort_multipart_upload(garage, bucket_id, &key, &upload_id).await
|
||||||
}
|
}
|
||||||
Endpoint::DeleteObject { key, .. } => handle_delete(garage, bucket_id, &key).await,
|
Endpoint::DeleteObject { key, .. } => handle_delete(garage, bucket_id, &key).await,
|
||||||
Endpoint::CreateMultipartUpload { bucket, key } => {
|
Endpoint::CreateMultipartUpload { key } => {
|
||||||
handle_create_multipart_upload(garage, &req, &bucket, bucket_id, &key).await
|
handle_create_multipart_upload(garage, &req, &bucket_name, bucket_id, &key).await
|
||||||
}
|
}
|
||||||
Endpoint::CompleteMultipartUpload {
|
Endpoint::CompleteMultipartUpload { key, upload_id } => {
|
||||||
bucket,
|
|
||||||
key,
|
|
||||||
upload_id,
|
|
||||||
} => {
|
|
||||||
handle_complete_multipart_upload(
|
handle_complete_multipart_upload(
|
||||||
garage,
|
garage,
|
||||||
req,
|
req,
|
||||||
&bucket,
|
&bucket_name,
|
||||||
bucket_id,
|
bucket_id,
|
||||||
&key,
|
&key,
|
||||||
&upload_id,
|
&upload_id,
|
||||||
|
@ -201,19 +193,18 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Endpoint::CreateBucket { .. } => unreachable!(),
|
Endpoint::CreateBucket {} => unreachable!(),
|
||||||
Endpoint::HeadBucket { .. } => {
|
Endpoint::HeadBucket {} => {
|
||||||
let empty_body: Body = Body::from(vec![]);
|
let empty_body: Body = Body::from(vec![]);
|
||||||
let response = Response::builder().body(empty_body).unwrap();
|
let response = Response::builder().body(empty_body).unwrap();
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
Endpoint::DeleteBucket { .. } => {
|
Endpoint::DeleteBucket {} => {
|
||||||
handle_delete_bucket(&garage, bucket_id, bucket_name, api_key).await
|
handle_delete_bucket(&garage, bucket_id, bucket_name, api_key).await
|
||||||
}
|
}
|
||||||
Endpoint::GetBucketLocation { .. } => handle_get_bucket_location(garage),
|
Endpoint::GetBucketLocation {} => handle_get_bucket_location(garage),
|
||||||
Endpoint::GetBucketVersioning { .. } => handle_get_bucket_versioning(),
|
Endpoint::GetBucketVersioning {} => handle_get_bucket_versioning(),
|
||||||
Endpoint::ListObjects {
|
Endpoint::ListObjects {
|
||||||
bucket,
|
|
||||||
delimiter,
|
delimiter,
|
||||||
encoding_type,
|
encoding_type,
|
||||||
marker,
|
marker,
|
||||||
|
@ -224,7 +215,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
garage,
|
garage,
|
||||||
&ListObjectsQuery {
|
&ListObjectsQuery {
|
||||||
common: ListQueryCommon {
|
common: ListQueryCommon {
|
||||||
bucket_name: bucket,
|
bucket_name,
|
||||||
bucket_id,
|
bucket_id,
|
||||||
delimiter: delimiter.map(|d| d.to_string()),
|
delimiter: delimiter.map(|d| d.to_string()),
|
||||||
page_size: max_keys.map(|p| min(1000, max(1, p))).unwrap_or(1000),
|
page_size: max_keys.map(|p| min(1000, max(1, p))).unwrap_or(1000),
|
||||||
|
@ -240,7 +231,6 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Endpoint::ListObjectsV2 {
|
Endpoint::ListObjectsV2 {
|
||||||
bucket,
|
|
||||||
delimiter,
|
delimiter,
|
||||||
encoding_type,
|
encoding_type,
|
||||||
max_keys,
|
max_keys,
|
||||||
|
@ -255,7 +245,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
garage,
|
garage,
|
||||||
&ListObjectsQuery {
|
&ListObjectsQuery {
|
||||||
common: ListQueryCommon {
|
common: ListQueryCommon {
|
||||||
bucket_name: bucket,
|
bucket_name,
|
||||||
bucket_id,
|
bucket_id,
|
||||||
delimiter: delimiter.map(|d| d.to_string()),
|
delimiter: delimiter.map(|d| d.to_string()),
|
||||||
page_size: max_keys.map(|p| min(1000, max(1, p))).unwrap_or(1000),
|
page_size: max_keys.map(|p| min(1000, max(1, p))).unwrap_or(1000),
|
||||||
|
@ -277,7 +267,6 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Endpoint::ListMultipartUploads {
|
Endpoint::ListMultipartUploads {
|
||||||
bucket,
|
|
||||||
delimiter,
|
delimiter,
|
||||||
encoding_type,
|
encoding_type,
|
||||||
key_marker,
|
key_marker,
|
||||||
|
@ -289,7 +278,7 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
garage,
|
garage,
|
||||||
&ListMultipartUploadsQuery {
|
&ListMultipartUploadsQuery {
|
||||||
common: ListQueryCommon {
|
common: ListQueryCommon {
|
||||||
bucket_name: bucket,
|
bucket_name,
|
||||||
bucket_id,
|
bucket_id,
|
||||||
delimiter: delimiter.map(|d| d.to_string()),
|
delimiter: delimiter.map(|d| d.to_string()),
|
||||||
page_size: max_uploads.map(|p| min(1000, max(1, p))).unwrap_or(1000),
|
page_size: max_uploads.map(|p| min(1000, max(1, p))).unwrap_or(1000),
|
||||||
|
@ -302,14 +291,14 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Endpoint::DeleteObjects { .. } => {
|
Endpoint::DeleteObjects {} => {
|
||||||
handle_delete_objects(garage, bucket_id, req, content_sha256).await
|
handle_delete_objects(garage, bucket_id, req, content_sha256).await
|
||||||
}
|
}
|
||||||
Endpoint::GetBucketWebsite { .. } => handle_get_website(garage, bucket_id).await,
|
Endpoint::GetBucketWebsite {} => handle_get_website(garage, bucket_id).await,
|
||||||
Endpoint::PutBucketWebsite { .. } => {
|
Endpoint::PutBucketWebsite {} => {
|
||||||
handle_put_website(garage, bucket_id, req, content_sha256).await
|
handle_put_website(garage, bucket_id, req, content_sha256).await
|
||||||
}
|
}
|
||||||
Endpoint::DeleteBucketWebsite { .. } => handle_delete_website(garage, bucket_id).await,
|
Endpoint::DeleteBucketWebsite {} => handle_delete_website(garage, bucket_id).await,
|
||||||
endpoint => Err(Error::NotImplemented(endpoint.name().to_owned())),
|
endpoint => Err(Error::NotImplemented(endpoint.name().to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,17 @@ use hyper::{HeaderMap, Method, Request};
|
||||||
/// This macro is used to generate very repetitive match {} blocks in this module
|
/// This macro is used to generate very repetitive match {} blocks in this module
|
||||||
/// It is _not_ made to be used anywhere else
|
/// It is _not_ made to be used anywhere else
|
||||||
macro_rules! s3_match {
|
macro_rules! s3_match {
|
||||||
|
(@match $enum:expr , [ $($endpoint:ident,)* ]) => {{
|
||||||
|
// usage: s3_match {@match my_enum, [ VariantWithField1, VariantWithField2 ..] }
|
||||||
|
// returns true if the variant was one of the listed variants, false otherwise.
|
||||||
|
use Endpoint::*;
|
||||||
|
match $enum {
|
||||||
|
$(
|
||||||
|
$endpoint { .. } => true,
|
||||||
|
)*
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}};
|
||||||
(@extract $enum:expr , $param:ident, [ $($endpoint:ident,)* ]) => {{
|
(@extract $enum:expr , $param:ident, [ $($endpoint:ident,)* ]) => {{
|
||||||
// usage: s3_match {@extract my_enum, field_name, [ VariantWithField1, VariantWithField2 ..] }
|
// usage: s3_match {@extract my_enum, field_name, [ VariantWithField1, VariantWithField2 ..] }
|
||||||
// returns Some(field_value), or None if the variant was not one of the listed variants.
|
// returns Some(field_value), or None if the variant was not one of the listed variants.
|
||||||
|
@ -19,10 +30,10 @@ macro_rules! s3_match {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
(@gen_parser ($keyword:expr, $key:expr, $bucket:expr, $query:expr, $header:expr),
|
(@gen_parser ($keyword:expr, $key:expr, $query:expr, $header:expr),
|
||||||
key: [$($kw_k:ident $(if $required_k:ident)? $(header $header_k:expr)? => $api_k:ident $(($($conv_k:ident :: $param_k:ident),*))?,)*],
|
key: [$($kw_k:ident $(if $required_k:ident)? $(header $header_k:expr)? => $api_k:ident $(($($conv_k:ident :: $param_k:ident),*))?,)*],
|
||||||
no_key: [$($kw_nk:ident $(if $required_nk:ident)? $(if_header $header_nk:expr)? => $api_nk:ident $(($($conv_nk:ident :: $param_nk:ident),*))?,)*]) => {{
|
no_key: [$($kw_nk:ident $(if $required_nk:ident)? $(if_header $header_nk:expr)? => $api_nk:ident $(($($conv_nk:ident :: $param_nk:ident),*))?,)*]) => {{
|
||||||
// usage: s3_match {@gen_parser (keyword, key, bucket, query, header),
|
// usage: s3_match {@gen_parser (keyword, key, query, header),
|
||||||
// key: [
|
// key: [
|
||||||
// SOME_KEYWORD => VariantWithKey,
|
// SOME_KEYWORD => VariantWithKey,
|
||||||
// ...
|
// ...
|
||||||
|
@ -38,7 +49,6 @@ macro_rules! s3_match {
|
||||||
match ($keyword, !$key.is_empty()){
|
match ($keyword, !$key.is_empty()){
|
||||||
$(
|
$(
|
||||||
($kw_k, true) if true $(&& $query.$required_k.is_some())? $(&& $header.contains_key($header_k))? => Ok($api_k {
|
($kw_k, true) if true $(&& $query.$required_k.is_some())? $(&& $header.contains_key($header_k))? => Ok($api_k {
|
||||||
bucket: $bucket,
|
|
||||||
key: $key,
|
key: $key,
|
||||||
$($(
|
$($(
|
||||||
$param_k: s3_match!(@@parse_param $query, $conv_k, $param_k),
|
$param_k: s3_match!(@@parse_param $query, $conv_k, $param_k),
|
||||||
|
@ -47,7 +57,6 @@ macro_rules! s3_match {
|
||||||
)*
|
)*
|
||||||
$(
|
$(
|
||||||
($kw_nk, false) $(if $query.$required_nk.is_some())? $(if $header.contains($header_nk))? => Ok($api_nk {
|
($kw_nk, false) $(if $query.$required_nk.is_some())? $(if $header.contains($header_nk))? => Ok($api_nk {
|
||||||
bucket: $bucket,
|
|
||||||
$($(
|
$($(
|
||||||
$param_nk: s3_match!(@@parse_param $query, $conv_nk, $param_nk),
|
$param_nk: s3_match!(@@parse_param $query, $conv_nk, $param_nk),
|
||||||
)*)?
|
)*)?
|
||||||
|
@ -87,7 +96,6 @@ macro_rules! s3_match {
|
||||||
$(
|
$(
|
||||||
$(#[$outer:meta])*
|
$(#[$outer:meta])*
|
||||||
$variant:ident $({
|
$variant:ident $({
|
||||||
bucket: String,
|
|
||||||
$($name:ident: $ty:ty,)*
|
$($name:ident: $ty:ty,)*
|
||||||
})?,
|
})?,
|
||||||
)*
|
)*
|
||||||
|
@ -97,7 +105,6 @@ macro_rules! s3_match {
|
||||||
$(
|
$(
|
||||||
$(#[$outer])*
|
$(#[$outer])*
|
||||||
$variant $({
|
$variant $({
|
||||||
bucket: String,
|
|
||||||
$($name: $ty, )*
|
$($name: $ty, )*
|
||||||
})?,
|
})?,
|
||||||
)*
|
)*
|
||||||
|
@ -108,15 +115,6 @@ macro_rules! s3_match {
|
||||||
$(Endpoint::$variant $({ $($name: _,)* .. })? => stringify!($variant),)*
|
$(Endpoint::$variant $({ $($name: _,)* .. })? => stringify!($variant),)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bucket the request target. Returns None for requests not related to a bucket.
|
|
||||||
pub fn get_bucket(&self) -> Option<&str> {
|
|
||||||
match self {
|
|
||||||
$(
|
|
||||||
Endpoint::$variant $({ bucket, $($name: _,)* .. })? => s3_match!{@if ($(bucket $($name)*)?) then (Some(bucket)) else (None)},
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(@if ($($cond:tt)+) then ($($then:tt)*) else ($($else:tt)*)) => {
|
(@if ($($cond:tt)+) then ($($then:tt)*) else ($($else:tt)*)) => {
|
||||||
|
@ -138,215 +136,158 @@ s3_match! {@func
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Endpoint {
|
pub enum Endpoint {
|
||||||
AbortMultipartUpload {
|
AbortMultipartUpload {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
upload_id: String,
|
upload_id: String,
|
||||||
},
|
},
|
||||||
CompleteMultipartUpload {
|
CompleteMultipartUpload {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
upload_id: String,
|
upload_id: String,
|
||||||
},
|
},
|
||||||
CopyObject {
|
CopyObject {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
},
|
},
|
||||||
CreateBucket {
|
CreateBucket {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
CreateMultipartUpload {
|
CreateMultipartUpload {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
},
|
},
|
||||||
DeleteBucket {
|
DeleteBucket {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketAnalyticsConfiguration {
|
DeleteBucketAnalyticsConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
DeleteBucketCors {
|
DeleteBucketCors {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketEncryption {
|
DeleteBucketEncryption {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketIntelligentTieringConfiguration {
|
DeleteBucketIntelligentTieringConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
DeleteBucketInventoryConfiguration {
|
DeleteBucketInventoryConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
DeleteBucketLifecycle {
|
DeleteBucketLifecycle {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketMetricsConfiguration {
|
DeleteBucketMetricsConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
DeleteBucketOwnershipControls {
|
DeleteBucketOwnershipControls {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketPolicy {
|
DeleteBucketPolicy {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketReplication {
|
DeleteBucketReplication {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketTagging {
|
DeleteBucketTagging {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteBucketWebsite {
|
DeleteBucketWebsite {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteObject {
|
DeleteObject {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
DeleteObjects {
|
DeleteObjects {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
DeleteObjectTagging {
|
DeleteObjectTagging {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
DeletePublicAccessBlock {
|
DeletePublicAccessBlock {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketAccelerateConfiguration {
|
GetBucketAccelerateConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketAcl {
|
GetBucketAcl {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketAnalyticsConfiguration {
|
GetBucketAnalyticsConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
GetBucketCors {
|
GetBucketCors {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketEncryption {
|
GetBucketEncryption {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketIntelligentTieringConfiguration {
|
GetBucketIntelligentTieringConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
GetBucketInventoryConfiguration {
|
GetBucketInventoryConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
GetBucketLifecycleConfiguration {
|
GetBucketLifecycleConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketLocation {
|
GetBucketLocation {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketLogging {
|
GetBucketLogging {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketMetricsConfiguration {
|
GetBucketMetricsConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
GetBucketNotificationConfiguration {
|
GetBucketNotificationConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketOwnershipControls {
|
GetBucketOwnershipControls {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketPolicy {
|
GetBucketPolicy {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketPolicyStatus {
|
GetBucketPolicyStatus {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketReplication {
|
GetBucketReplication {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketRequestPayment {
|
GetBucketRequestPayment {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketTagging {
|
GetBucketTagging {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketVersioning {
|
GetBucketVersioning {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetBucketWebsite {
|
GetBucketWebsite {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
/// There are actually many more query parameters, used to add headers to the answer. They were
|
/// There are actually many more query parameters, used to add headers to the answer. They were
|
||||||
/// not added here as they are best handled in a dedicated route.
|
/// not added here as they are best handled in a dedicated route.
|
||||||
GetObject {
|
GetObject {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
part_number: Option<u64>,
|
part_number: Option<u64>,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
GetObjectAcl {
|
GetObjectAcl {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
GetObjectLegalHold {
|
GetObjectLegalHold {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
GetObjectLockConfiguration {
|
GetObjectLockConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
GetObjectRetention {
|
GetObjectRetention {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
GetObjectTagging {
|
GetObjectTagging {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
GetObjectTorrent {
|
GetObjectTorrent {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
},
|
},
|
||||||
GetPublicAccessBlock {
|
GetPublicAccessBlock {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
HeadBucket {
|
HeadBucket {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
HeadObject {
|
HeadObject {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
part_number: Option<u64>,
|
part_number: Option<u64>,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
ListBucketAnalyticsConfigurations {
|
ListBucketAnalyticsConfigurations {
|
||||||
bucket: String,
|
|
||||||
continuation_token: Option<String>,
|
continuation_token: Option<String>,
|
||||||
},
|
},
|
||||||
ListBucketIntelligentTieringConfigurations {
|
ListBucketIntelligentTieringConfigurations {
|
||||||
bucket: String,
|
|
||||||
continuation_token: Option<String>,
|
continuation_token: Option<String>,
|
||||||
},
|
},
|
||||||
ListBucketInventoryConfigurations {
|
ListBucketInventoryConfigurations {
|
||||||
bucket: String,
|
|
||||||
continuation_token: Option<String>,
|
continuation_token: Option<String>,
|
||||||
},
|
},
|
||||||
ListBucketMetricsConfigurations {
|
ListBucketMetricsConfigurations {
|
||||||
bucket: String,
|
|
||||||
continuation_token: Option<String>,
|
continuation_token: Option<String>,
|
||||||
},
|
},
|
||||||
ListBuckets,
|
ListBuckets,
|
||||||
ListMultipartUploads {
|
ListMultipartUploads {
|
||||||
bucket: String,
|
|
||||||
delimiter: Option<char>,
|
delimiter: Option<char>,
|
||||||
encoding_type: Option<String>,
|
encoding_type: Option<String>,
|
||||||
key_marker: Option<String>,
|
key_marker: Option<String>,
|
||||||
|
@ -355,7 +296,6 @@ pub enum Endpoint {
|
||||||
upload_id_marker: Option<String>,
|
upload_id_marker: Option<String>,
|
||||||
},
|
},
|
||||||
ListObjects {
|
ListObjects {
|
||||||
bucket: String,
|
|
||||||
delimiter: Option<char>,
|
delimiter: Option<char>,
|
||||||
encoding_type: Option<String>,
|
encoding_type: Option<String>,
|
||||||
marker: Option<String>,
|
marker: Option<String>,
|
||||||
|
@ -363,7 +303,6 @@ pub enum Endpoint {
|
||||||
prefix: Option<String>,
|
prefix: Option<String>,
|
||||||
},
|
},
|
||||||
ListObjectsV2 {
|
ListObjectsV2 {
|
||||||
bucket: String,
|
|
||||||
// This value should always be 2. It is not checked when constructing the struct
|
// This value should always be 2. It is not checked when constructing the struct
|
||||||
list_type: String,
|
list_type: String,
|
||||||
continuation_token: Option<String>,
|
continuation_token: Option<String>,
|
||||||
|
@ -375,7 +314,6 @@ pub enum Endpoint {
|
||||||
start_after: Option<String>,
|
start_after: Option<String>,
|
||||||
},
|
},
|
||||||
ListObjectVersions {
|
ListObjectVersions {
|
||||||
bucket: String,
|
|
||||||
delimiter: Option<char>,
|
delimiter: Option<char>,
|
||||||
encoding_type: Option<String>,
|
encoding_type: Option<String>,
|
||||||
key_marker: Option<String>,
|
key_marker: Option<String>,
|
||||||
|
@ -384,119 +322,89 @@ pub enum Endpoint {
|
||||||
version_id_marker: Option<String>,
|
version_id_marker: Option<String>,
|
||||||
},
|
},
|
||||||
ListParts {
|
ListParts {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
max_parts: Option<u64>,
|
max_parts: Option<u64>,
|
||||||
part_number_marker: Option<u64>,
|
part_number_marker: Option<u64>,
|
||||||
upload_id: String,
|
upload_id: String,
|
||||||
},
|
},
|
||||||
PutBucketAccelerateConfiguration {
|
PutBucketAccelerateConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketAcl {
|
PutBucketAcl {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketAnalyticsConfiguration {
|
PutBucketAnalyticsConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
PutBucketCors {
|
PutBucketCors {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketEncryption {
|
PutBucketEncryption {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketIntelligentTieringConfiguration {
|
PutBucketIntelligentTieringConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
PutBucketInventoryConfiguration {
|
PutBucketInventoryConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
PutBucketLifecycleConfiguration {
|
PutBucketLifecycleConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketLogging {
|
PutBucketLogging {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketMetricsConfiguration {
|
PutBucketMetricsConfiguration {
|
||||||
bucket: String,
|
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
PutBucketNotificationConfiguration {
|
PutBucketNotificationConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketOwnershipControls {
|
PutBucketOwnershipControls {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketPolicy {
|
PutBucketPolicy {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketReplication {
|
PutBucketReplication {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketRequestPayment {
|
PutBucketRequestPayment {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketTagging {
|
PutBucketTagging {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketVersioning {
|
PutBucketVersioning {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutBucketWebsite {
|
PutBucketWebsite {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutObject {
|
PutObject {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
},
|
},
|
||||||
PutObjectAcl {
|
PutObjectAcl {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
PutObjectLegalHold {
|
PutObjectLegalHold {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
PutObjectLockConfiguration {
|
PutObjectLockConfiguration {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
PutObjectRetention {
|
PutObjectRetention {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
PutObjectTagging {
|
PutObjectTagging {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
PutPublicAccessBlock {
|
PutPublicAccessBlock {
|
||||||
bucket: String,
|
|
||||||
},
|
},
|
||||||
RestoreObject {
|
RestoreObject {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
},
|
},
|
||||||
SelectObjectContent {
|
SelectObjectContent {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
// This value should always be 2. It is not checked when constructing the struct
|
// This value should always be 2. It is not checked when constructing the struct
|
||||||
select_type: String,
|
select_type: String,
|
||||||
},
|
},
|
||||||
UploadPart {
|
UploadPart {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
part_number: u64,
|
part_number: u64,
|
||||||
upload_id: String,
|
upload_id: String,
|
||||||
},
|
},
|
||||||
UploadPartCopy {
|
UploadPartCopy {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
part_number: u64,
|
part_number: u64,
|
||||||
upload_id: String,
|
upload_id: String,
|
||||||
|
@ -506,12 +414,16 @@ pub enum Endpoint {
|
||||||
impl Endpoint {
|
impl Endpoint {
|
||||||
/// Determine which S3 endpoint a request is for using the request, and a bucket which was
|
/// Determine which S3 endpoint a request is for using the request, and a bucket which was
|
||||||
/// possibly extracted from the Host header.
|
/// possibly extracted from the Host header.
|
||||||
pub fn from_request<T>(req: &Request<T>, bucket: Option<String>) -> Result<Self, Error> {
|
/// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets
|
||||||
|
pub fn from_request<T>(
|
||||||
|
req: &Request<T>,
|
||||||
|
bucket: Option<String>,
|
||||||
|
) -> Result<(Self, Option<String>), Error> {
|
||||||
let uri = req.uri();
|
let uri = req.uri();
|
||||||
let path = uri.path().trim_start_matches('/');
|
let path = uri.path().trim_start_matches('/');
|
||||||
let query = uri.query();
|
let query = uri.query();
|
||||||
if bucket.is_none() && path.is_empty() {
|
if bucket.is_none() && path.is_empty() {
|
||||||
return Ok(Self::ListBuckets);
|
return Ok((Self::ListBuckets, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (bucket, key) = if let Some(bucket) = bucket {
|
let (bucket, key) = if let Some(bucket) = bucket {
|
||||||
|
@ -529,29 +441,25 @@ impl Endpoint {
|
||||||
let mut query = QueryParameters::from_query(query.unwrap_or_default())?;
|
let mut query = QueryParameters::from_query(query.unwrap_or_default())?;
|
||||||
|
|
||||||
let res = match *req.method() {
|
let res = match *req.method() {
|
||||||
Method::GET => Self::from_get(bucket, key, &mut query)?,
|
Method::GET => Self::from_get(key, &mut query)?,
|
||||||
Method::HEAD => Self::from_head(bucket, key, &mut query)?,
|
Method::HEAD => Self::from_head(key, &mut query)?,
|
||||||
Method::POST => Self::from_post(bucket, key, &mut query)?,
|
Method::POST => Self::from_post(key, &mut query)?,
|
||||||
Method::PUT => Self::from_put(bucket, key, &mut query, req.headers())?,
|
Method::PUT => Self::from_put(key, &mut query, req.headers())?,
|
||||||
Method::DELETE => Self::from_delete(bucket, key, &mut query)?,
|
Method::DELETE => Self::from_delete(key, &mut query)?,
|
||||||
_ => return Err(Error::BadRequest("Unknown method".to_owned())),
|
_ => return Err(Error::BadRequest("Unknown method".to_owned())),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(message) = query.nonempty_message() {
|
if let Some(message) = query.nonempty_message() {
|
||||||
debug!("Unused query parameter: {}", message)
|
debug!("Unused query parameter: {}", message)
|
||||||
}
|
}
|
||||||
Ok(res)
|
Ok((res, Some(bucket)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine which endpoint a request is for, knowing it is a GET.
|
/// Determine which endpoint a request is for, knowing it is a GET.
|
||||||
fn from_get(
|
fn from_get(key: String, query: &mut QueryParameters<'_>) -> Result<Self, Error> {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
|
||||||
query: &mut QueryParameters<'_>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
s3_match! {
|
s3_match! {
|
||||||
@gen_parser
|
@gen_parser
|
||||||
(query.keyword.take().unwrap_or_default().as_ref(), key, bucket, query, None),
|
(query.keyword.take().unwrap_or_default().as_ref(), key, query, None),
|
||||||
key: [
|
key: [
|
||||||
EMPTY if upload_id => ListParts (query::upload_id, opt_parse::max_parts, opt_parse::part_number_marker),
|
EMPTY if upload_id => ListParts (query::upload_id, opt_parse::max_parts, opt_parse::part_number_marker),
|
||||||
EMPTY => GetObject (query_opt::version_id, opt_parse::part_number),
|
EMPTY => GetObject (query_opt::version_id, opt_parse::part_number),
|
||||||
|
@ -605,14 +513,10 @@ impl Endpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine which endpoint a request is for, knowing it is a HEAD.
|
/// Determine which endpoint a request is for, knowing it is a HEAD.
|
||||||
fn from_head(
|
fn from_head(key: String, query: &mut QueryParameters<'_>) -> Result<Self, Error> {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
|
||||||
query: &mut QueryParameters<'_>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
s3_match! {
|
s3_match! {
|
||||||
@gen_parser
|
@gen_parser
|
||||||
(query.keyword.take().unwrap_or_default().as_ref(), key, bucket, query, None),
|
(query.keyword.take().unwrap_or_default().as_ref(), key, query, None),
|
||||||
key: [
|
key: [
|
||||||
EMPTY => HeadObject(opt_parse::part_number, query_opt::version_id),
|
EMPTY => HeadObject(opt_parse::part_number, query_opt::version_id),
|
||||||
],
|
],
|
||||||
|
@ -623,14 +527,10 @@ impl Endpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine which endpoint a request is for, knowing it is a POST.
|
/// Determine which endpoint a request is for, knowing it is a POST.
|
||||||
fn from_post(
|
fn from_post(key: String, query: &mut QueryParameters<'_>) -> Result<Self, Error> {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
|
||||||
query: &mut QueryParameters<'_>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
s3_match! {
|
s3_match! {
|
||||||
@gen_parser
|
@gen_parser
|
||||||
(query.keyword.take().unwrap_or_default().as_ref(), key, bucket, query, None),
|
(query.keyword.take().unwrap_or_default().as_ref(), key, query, None),
|
||||||
key: [
|
key: [
|
||||||
EMPTY if upload_id => CompleteMultipartUpload (query::upload_id),
|
EMPTY if upload_id => CompleteMultipartUpload (query::upload_id),
|
||||||
RESTORE => RestoreObject (query_opt::version_id),
|
RESTORE => RestoreObject (query_opt::version_id),
|
||||||
|
@ -645,14 +545,13 @@ impl Endpoint {
|
||||||
|
|
||||||
/// Determine which endpoint a request is for, knowing it is a PUT.
|
/// Determine which endpoint a request is for, knowing it is a PUT.
|
||||||
fn from_put(
|
fn from_put(
|
||||||
bucket: String,
|
|
||||||
key: String,
|
key: String,
|
||||||
query: &mut QueryParameters<'_>,
|
query: &mut QueryParameters<'_>,
|
||||||
headers: &HeaderMap<HeaderValue>,
|
headers: &HeaderMap<HeaderValue>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
s3_match! {
|
s3_match! {
|
||||||
@gen_parser
|
@gen_parser
|
||||||
(query.keyword.take().unwrap_or_default().as_ref(), key, bucket, query, headers),
|
(query.keyword.take().unwrap_or_default().as_ref(), key, query, headers),
|
||||||
key: [
|
key: [
|
||||||
EMPTY if part_number header "x-amz-copy-source" => UploadPartCopy (parse::part_number, query::upload_id),
|
EMPTY if part_number header "x-amz-copy-source" => UploadPartCopy (parse::part_number, query::upload_id),
|
||||||
EMPTY header "x-amz-copy-source" => CopyObject,
|
EMPTY header "x-amz-copy-source" => CopyObject,
|
||||||
|
@ -691,14 +590,10 @@ impl Endpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine which endpoint a request is for, knowing it is a DELETE.
|
/// Determine which endpoint a request is for, knowing it is a DELETE.
|
||||||
fn from_delete(
|
fn from_delete(key: String, query: &mut QueryParameters<'_>) -> Result<Self, Error> {
|
||||||
bucket: String,
|
|
||||||
key: String,
|
|
||||||
query: &mut QueryParameters<'_>,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
s3_match! {
|
s3_match! {
|
||||||
@gen_parser
|
@gen_parser
|
||||||
(query.keyword.take().unwrap_or_default().as_ref(), key, bucket, query, None),
|
(query.keyword.take().unwrap_or_default().as_ref(), key, query, None),
|
||||||
key: [
|
key: [
|
||||||
EMPTY if upload_id => AbortMultipartUpload (query::upload_id),
|
EMPTY if upload_id => AbortMultipartUpload (query::upload_id),
|
||||||
EMPTY => DeleteObject (query_opt::version_id),
|
EMPTY => DeleteObject (query_opt::version_id),
|
||||||
|
@ -759,16 +654,13 @@ impl Endpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the kind of authorization which is required to perform the operation.
|
/// Get the kind of authorization which is required to perform the operation.
|
||||||
pub fn authorization_type(&self) -> Authorization<'_> {
|
pub fn authorization_type(&self) -> Authorization {
|
||||||
let bucket = if let Some(bucket) = self.get_bucket() {
|
if let Endpoint::ListBuckets = self {
|
||||||
bucket
|
|
||||||
} else {
|
|
||||||
return Authorization::None;
|
return Authorization::None;
|
||||||
};
|
};
|
||||||
let readonly = s3_match! {
|
let readonly = s3_match! {
|
||||||
@extract
|
@match
|
||||||
self,
|
self,
|
||||||
bucket,
|
|
||||||
[
|
[
|
||||||
GetBucketAccelerateConfiguration,
|
GetBucketAccelerateConfiguration,
|
||||||
GetBucketAcl,
|
GetBucketAcl,
|
||||||
|
@ -810,41 +702,38 @@ impl Endpoint {
|
||||||
ListParts,
|
ListParts,
|
||||||
SelectObjectContent,
|
SelectObjectContent,
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
.is_some();
|
|
||||||
let owner = s3_match! {
|
let owner = s3_match! {
|
||||||
@extract
|
@match
|
||||||
self,
|
self,
|
||||||
bucket,
|
|
||||||
[
|
[
|
||||||
DeleteBucket,
|
DeleteBucket,
|
||||||
GetBucketWebsite,
|
GetBucketWebsite,
|
||||||
PutBucketWebsite,
|
PutBucketWebsite,
|
||||||
DeleteBucketWebsite,
|
DeleteBucketWebsite,
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
.is_some();
|
|
||||||
if readonly {
|
if readonly {
|
||||||
Authorization::Read(bucket)
|
Authorization::Read
|
||||||
} else if owner {
|
} else if owner {
|
||||||
Authorization::Owner(bucket)
|
Authorization::Owner
|
||||||
} else {
|
} else {
|
||||||
Authorization::Write(bucket)
|
Authorization::Write
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What kind of authorization is required to perform a given action
|
/// What kind of authorization is required to perform a given action
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Authorization<'a> {
|
pub enum Authorization {
|
||||||
/// No authorization is required
|
/// No authorization is required
|
||||||
None,
|
None,
|
||||||
/// Having Read permission on bucket .0 is required
|
/// Having Read permission on bucket
|
||||||
Read(&'a str),
|
Read,
|
||||||
/// Having Write permission on bucket .0 is required
|
/// Having Write permission on bucket
|
||||||
Write(&'a str),
|
Write,
|
||||||
/// Having Owner permission on bucket .0 is required
|
/// Having Owner permission on bucket
|
||||||
Owner(&'a str),
|
Owner,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This macro is used to generate part of the code in this module. It must be called only one, and
|
/// This macro is used to generate part of the code in this module. It must be called only one, and
|
||||||
|
@ -985,7 +874,7 @@ mod tests {
|
||||||
uri: &str,
|
uri: &str,
|
||||||
bucket: Option<String>,
|
bucket: Option<String>,
|
||||||
header: Option<(&str, &str)>,
|
header: Option<(&str, &str)>,
|
||||||
) -> Endpoint {
|
) -> (Endpoint, Option<String>) {
|
||||||
let mut req = Request::builder().method(method).uri(uri);
|
let mut req = Request::builder().method(method).uri(uri);
|
||||||
if let Some((k, v)) = header {
|
if let Some((k, v)) = header {
|
||||||
req = req.header(k, v)
|
req = req.header(k, v)
|
||||||
|
@ -1000,13 +889,13 @@ mod tests {
|
||||||
$(
|
$(
|
||||||
assert!(
|
assert!(
|
||||||
matches!(
|
matches!(
|
||||||
parse(test_cases!{@actual_method $method}, $uri, Some("my_bucket".to_owned()), None),
|
parse(test_cases!{@actual_method $method}, $uri, Some("my_bucket".to_owned()), None).0,
|
||||||
Endpoint::$variant { .. }
|
Endpoint::$variant { .. }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(
|
matches!(
|
||||||
parse(test_cases!{@actual_method $method}, concat!("/my_bucket", $uri), None, None),
|
parse(test_cases!{@actual_method $method}, concat!("/my_bucket", $uri), None, None).0,
|
||||||
Endpoint::$variant { .. }
|
Endpoint::$variant { .. }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1025,78 +914,82 @@ mod tests {
|
||||||
(@actual_method OWNER_DELETE) => {{ "DELETE" }};
|
(@actual_method OWNER_DELETE) => {{ "DELETE" }};
|
||||||
|
|
||||||
(@auth HEAD $uri:expr) => {{
|
(@auth HEAD $uri:expr) => {{
|
||||||
assert_eq!(parse("HEAD", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("HEAD", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Read("my_bucket"))
|
Authorization::Read)
|
||||||
}};
|
}};
|
||||||
(@auth GET $uri:expr) => {{
|
(@auth GET $uri:expr) => {{
|
||||||
assert_eq!(parse("GET", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("GET", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Read("my_bucket"))
|
Authorization::Read)
|
||||||
}};
|
}};
|
||||||
(@auth OWNER_GET $uri:expr) => {{
|
(@auth OWNER_GET $uri:expr) => {{
|
||||||
assert_eq!(parse("GET", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("GET", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Owner("my_bucket"))
|
Authorization::Owner)
|
||||||
}};
|
}};
|
||||||
(@auth PUT $uri:expr) => {{
|
(@auth PUT $uri:expr) => {{
|
||||||
assert_eq!(parse("PUT", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("PUT", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Write("my_bucket"))
|
Authorization::Write)
|
||||||
}};
|
}};
|
||||||
(@auth OWNER_PUT $uri:expr) => {{
|
(@auth OWNER_PUT $uri:expr) => {{
|
||||||
assert_eq!(parse("PUT", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("PUT", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Owner("my_bucket"))
|
Authorization::Owner)
|
||||||
}};
|
}};
|
||||||
(@auth POST $uri:expr) => {{
|
(@auth POST $uri:expr) => {{
|
||||||
assert_eq!(parse("POST", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("POST", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Write("my_bucket"))
|
Authorization::Write)
|
||||||
}};
|
}};
|
||||||
(@auth DELETE $uri:expr) => {{
|
(@auth DELETE $uri:expr) => {{
|
||||||
assert_eq!(parse("DELETE", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("DELETE", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Write("my_bucket"))
|
Authorization::Write)
|
||||||
}};
|
}};
|
||||||
(@auth OWNER_DELETE $uri:expr) => {{
|
(@auth OWNER_DELETE $uri:expr) => {{
|
||||||
assert_eq!(parse("DELETE", concat!("/my_bucket", $uri), None, None).authorization_type(),
|
assert_eq!(parse("DELETE", concat!("/my_bucket", $uri), None, None).0.authorization_type(),
|
||||||
Authorization::Owner("my_bucket"))
|
Authorization::Owner)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bucket_extraction() {
|
fn test_bucket_extraction() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("GET", "/my/key", Some("my_bucket".to_owned()), None).get_bucket(),
|
parse("GET", "/my/key", Some("my_bucket".to_owned()), None).1,
|
||||||
parse("GET", "/my_bucket/my/key", None, None).get_bucket()
|
parse("GET", "/my_bucket/my/key", None, None).1
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("GET", "/my_bucket/my/key", None, None)
|
parse("GET", "/my_bucket/my/key", None, None).1.unwrap(),
|
||||||
.get_bucket()
|
|
||||||
.unwrap(),
|
|
||||||
"my_bucket"
|
"my_bucket"
|
||||||
);
|
);
|
||||||
assert!(parse("GET", "/", None, None).get_bucket().is_none());
|
assert!(parse("GET", "/", None, None).1.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key() {
|
fn test_key() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("GET", "/my/key", Some("my_bucket".to_owned()), None).get_key(),
|
parse("GET", "/my/key", Some("my_bucket".to_owned()), None)
|
||||||
parse("GET", "/my_bucket/my/key", None, None).get_key()
|
.0
|
||||||
|
.get_key(),
|
||||||
|
parse("GET", "/my_bucket/my/key", None, None).0.get_key()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("GET", "/my_bucket/my/key", None, None)
|
parse("GET", "/my_bucket/my/key", None, None)
|
||||||
|
.0
|
||||||
.get_key()
|
.get_key()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"my/key"
|
"my/key"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("GET", "/my_bucket/my/key?acl", None, None)
|
parse("GET", "/my_bucket/my/key?acl", None, None)
|
||||||
|
.0
|
||||||
.get_key()
|
.get_key()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"my/key"
|
"my/key"
|
||||||
);
|
);
|
||||||
assert!(parse("GET", "/my_bucket/?list-type=2", None, None)
|
assert!(parse("GET", "/my_bucket/?list-type=2", None, None)
|
||||||
|
.0
|
||||||
.get_key()
|
.get_key()
|
||||||
.is_none());
|
.is_none());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse("GET", "/my_bucket/%26%2B%3F%25%C3%A9/something", None, None)
|
parse("GET", "/my_bucket/%26%2B%3F%25%C3%A9/something", None, None)
|
||||||
|
.0
|
||||||
.get_key()
|
.get_key()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
"&+?%é/something"
|
"&+?%é/something"
|
||||||
|
@ -1268,11 +1161,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
// no bucket, won't work with the rest of the test suite
|
// no bucket, won't work with the rest of the test suite
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse("GET", "/", None, None),
|
parse("GET", "/", None, None).0,
|
||||||
Endpoint::ListBuckets { .. }
|
Endpoint::ListBuckets { .. }
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse("GET", "/", None, None).authorization_type(),
|
parse("GET", "/", None, None).0.authorization_type(),
|
||||||
Authorization::None
|
Authorization::None
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1283,16 +1176,8 @@ mod tests {
|
||||||
"/Key+",
|
"/Key+",
|
||||||
Some("my_bucket".to_owned()),
|
Some("my_bucket".to_owned()),
|
||||||
Some(("x-amz-copy-source", "some/key"))
|
Some(("x-amz-copy-source", "some/key"))
|
||||||
),
|
)
|
||||||
Endpoint::CopyObject { .. }
|
.0,
|
||||||
));
|
|
||||||
assert!(matches!(
|
|
||||||
parse(
|
|
||||||
"PUT",
|
|
||||||
"/my_bucket/Key+",
|
|
||||||
None,
|
|
||||||
Some(("x-amz-copy-source", "some/key"))
|
|
||||||
),
|
|
||||||
Endpoint::CopyObject { .. }
|
Endpoint::CopyObject { .. }
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
|
@ -1302,8 +1187,19 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
Some(("x-amz-copy-source", "some/key"))
|
Some(("x-amz-copy-source", "some/key"))
|
||||||
)
|
)
|
||||||
|
.0,
|
||||||
|
Endpoint::CopyObject { .. }
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
parse(
|
||||||
|
"PUT",
|
||||||
|
"/my_bucket/Key+",
|
||||||
|
None,
|
||||||
|
Some(("x-amz-copy-source", "some/key"))
|
||||||
|
)
|
||||||
|
.0
|
||||||
.authorization_type(),
|
.authorization_type(),
|
||||||
Authorization::Write("my_bucket")
|
Authorization::Write
|
||||||
));
|
));
|
||||||
|
|
||||||
// require a header
|
// require a header
|
||||||
|
@ -1313,16 +1209,8 @@ mod tests {
|
||||||
"/Key+?partNumber=2&uploadId=UploadId",
|
"/Key+?partNumber=2&uploadId=UploadId",
|
||||||
Some("my_bucket".to_owned()),
|
Some("my_bucket".to_owned()),
|
||||||
Some(("x-amz-copy-source", "some/key"))
|
Some(("x-amz-copy-source", "some/key"))
|
||||||
),
|
)
|
||||||
Endpoint::UploadPartCopy { .. }
|
.0,
|
||||||
));
|
|
||||||
assert!(matches!(
|
|
||||||
parse(
|
|
||||||
"PUT",
|
|
||||||
"/my_bucket/Key+?partNumber=2&uploadId=UploadId",
|
|
||||||
None,
|
|
||||||
Some(("x-amz-copy-source", "some/key"))
|
|
||||||
),
|
|
||||||
Endpoint::UploadPartCopy { .. }
|
Endpoint::UploadPartCopy { .. }
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
|
@ -1332,8 +1220,19 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
Some(("x-amz-copy-source", "some/key"))
|
Some(("x-amz-copy-source", "some/key"))
|
||||||
)
|
)
|
||||||
|
.0,
|
||||||
|
Endpoint::UploadPartCopy { .. }
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
parse(
|
||||||
|
"PUT",
|
||||||
|
"/my_bucket/Key+?partNumber=2&uploadId=UploadId",
|
||||||
|
None,
|
||||||
|
Some(("x-amz-copy-source", "some/key"))
|
||||||
|
)
|
||||||
|
.0
|
||||||
.authorization_type(),
|
.authorization_type(),
|
||||||
Authorization::Write("my_bucket")
|
Authorization::Write
|
||||||
));
|
));
|
||||||
|
|
||||||
// POST request, but with GET semantic for permissions purpose
|
// POST request, but with GET semantic for permissions purpose
|
||||||
|
@ -1343,17 +1242,19 @@ mod tests {
|
||||||
"/{Key+}?select&select-type=2",
|
"/{Key+}?select&select-type=2",
|
||||||
Some("my_bucket".to_owned()),
|
Some("my_bucket".to_owned()),
|
||||||
None
|
None
|
||||||
),
|
)
|
||||||
|
.0,
|
||||||
Endpoint::SelectObjectContent { .. }
|
Endpoint::SelectObjectContent { .. }
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse("POST", "/my_bucket/{Key+}?select&select-type=2", None, None),
|
parse("POST", "/my_bucket/{Key+}?select&select-type=2", None, None).0,
|
||||||
Endpoint::SelectObjectContent { .. }
|
Endpoint::SelectObjectContent { .. }
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse("POST", "/my_bucket/{Key+}?select&select-type=2", None, None)
|
parse("POST", "/my_bucket/{Key+}?select&select-type=2", None, None)
|
||||||
|
.0
|
||||||
.authorization_type(),
|
.authorization_type(),
|
||||||
Authorization::Read("my_bucket")
|
Authorization::Read
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue