aboutsummaryrefslogtreecommitdiff
path: root/src/api/admin/router_v2.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/admin/router_v2.rs')
-rw-r--r--src/api/admin/router_v2.rs249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/api/admin/router_v2.rs b/src/api/admin/router_v2.rs
new file mode 100644
index 00000000..29250f39
--- /dev/null
+++ b/src/api/admin/router_v2.rs
@@ -0,0 +1,249 @@
+use std::borrow::Cow;
+
+use hyper::body::Incoming as IncomingBody;
+use hyper::{Method, Request};
+use paste::paste;
+
+use crate::admin::api::*;
+use crate::admin::error::*;
+use crate::admin::router_v1;
+use crate::admin::Authorization;
+use crate::helpers::*;
+use crate::router_macros::*;
+
+impl AdminApiRequest {
+ /// Determine which S3 endpoint a request is for using the request, and a bucket which was
+ /// possibly extracted from the Host header.
+ /// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets
+ pub async fn from_request(req: Request<IncomingBody>) -> Result<Self, Error> {
+ let uri = req.uri().clone();
+ let path = uri.path();
+ let query = uri.query();
+
+ let method = req.method().clone();
+
+ let mut query = QueryParameters::from_query(query.unwrap_or_default())?;
+
+ let res = router_match!(@gen_path_parser_v2 (&method, path, "/v2/", query, req) [
+ @special OPTIONS _ => Options (),
+ @special GET "/check" => CheckDomain (query::domain),
+ @special GET "/health" => Health (),
+ @special GET "/metrics" => Metrics (),
+ // Cluster endpoints
+ GET GetClusterStatus (),
+ GET GetClusterHealth (),
+ POST ConnectClusterNodes (body),
+ // Layout endpoints
+ GET GetClusterLayout (),
+ POST UpdateClusterLayout (body),
+ POST ApplyClusterLayout (body),
+ POST RevertClusterLayout (),
+ // API key endpoints
+ GET GetKeyInfo (query_opt::id, query_opt::search, parse_default(false)::show_secret_key),
+ POST UpdateKey (body_field, query::id),
+ POST CreateKey (body),
+ POST ImportKey (body),
+ POST DeleteKey (query::id),
+ GET ListKeys (),
+ // Bucket endpoints
+ GET GetBucketInfo (query_opt::id, query_opt::global_alias),
+ GET ListBuckets (),
+ POST CreateBucket (body),
+ POST DeleteBucket (query::id),
+ POST UpdateBucket (body_field, query::id),
+ // Bucket-key permissions
+ POST AllowBucketKey (body),
+ POST DenyBucketKey (body),
+ // Bucket aliases
+ POST AddBucketAlias (body),
+ POST RemoveBucketAlias (body),
+ ]);
+
+ if let Some(message) = query.nonempty_message() {
+ debug!("Unused query parameter: {}", message)
+ }
+
+ Ok(res)
+ }
+
+ /// Some endpoints work exactly the same in their v2/ version as they did in their v1/ version.
+ /// For these endpoints, we can convert a v1/ call to its equivalent as if it was made using
+ /// its v2/ URL.
+ pub async fn from_v1(
+ v1_endpoint: router_v1::Endpoint,
+ req: Request<IncomingBody>,
+ ) -> Result<Self, Error> {
+ use router_v1::Endpoint;
+
+ match v1_endpoint {
+ Endpoint::GetClusterStatus => {
+ Ok(AdminApiRequest::GetClusterStatus(GetClusterStatusRequest))
+ }
+ Endpoint::GetClusterHealth => {
+ Ok(AdminApiRequest::GetClusterHealth(GetClusterHealthRequest))
+ }
+ Endpoint::ConnectClusterNodes => {
+ let req = parse_json_body::<ConnectClusterNodesRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::ConnectClusterNodes(req))
+ }
+
+ // Layout
+ Endpoint::GetClusterLayout => {
+ Ok(AdminApiRequest::GetClusterLayout(GetClusterLayoutRequest))
+ }
+ Endpoint::UpdateClusterLayout => {
+ let updates = parse_json_body::<UpdateClusterLayoutRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::UpdateClusterLayout(updates))
+ }
+ Endpoint::ApplyClusterLayout => {
+ let param = parse_json_body::<ApplyClusterLayoutRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::ApplyClusterLayout(param))
+ }
+ Endpoint::RevertClusterLayout => Ok(AdminApiRequest::RevertClusterLayout(
+ RevertClusterLayoutRequest,
+ )),
+
+ // Keys
+ Endpoint::ListKeys => Ok(AdminApiRequest::ListKeys(ListKeysRequest)),
+ Endpoint::GetKeyInfo {
+ id,
+ search,
+ show_secret_key,
+ } => {
+ let show_secret_key = show_secret_key.map(|x| x == "true").unwrap_or(false);
+ Ok(AdminApiRequest::GetKeyInfo(GetKeyInfoRequest {
+ id,
+ search,
+ show_secret_key,
+ }))
+ }
+ Endpoint::CreateKey => {
+ let req = parse_json_body::<CreateKeyRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::CreateKey(req))
+ }
+ Endpoint::ImportKey => {
+ let req = parse_json_body::<ImportKeyRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::ImportKey(req))
+ }
+ Endpoint::UpdateKey { id } => {
+ let body = parse_json_body::<UpdateKeyRequestBody, _, Error>(req).await?;
+ Ok(AdminApiRequest::UpdateKey(UpdateKeyRequest { id, body }))
+ }
+
+ // DeleteKey semantics changed:
+ // - in v1/ : HTTP DELETE => HTTP 204 No Content
+ // - in v2/ : HTTP POST => HTTP 200 Ok
+ // Endpoint::DeleteKey { id } => Ok(AdminApiRequest::DeleteKey(DeleteKeyRequest { id })),
+
+ // Buckets
+ Endpoint::ListBuckets => Ok(AdminApiRequest::ListBuckets(ListBucketsRequest)),
+ Endpoint::GetBucketInfo { id, global_alias } => {
+ Ok(AdminApiRequest::GetBucketInfo(GetBucketInfoRequest {
+ id,
+ global_alias,
+ }))
+ }
+ Endpoint::CreateBucket => {
+ let req = parse_json_body::<CreateBucketRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::CreateBucket(req))
+ }
+
+ // DeleteBucket semantics changed::
+ // - in v1/ : HTTP DELETE => HTTP 204 No Content
+ // - in v2/ : HTTP POST => HTTP 200 Ok
+ // Endpoint::DeleteBucket { id } => {
+ // Ok(AdminApiRequest::DeleteBucket(DeleteBucketRequest { id }))
+ // }
+ Endpoint::UpdateBucket { id } => {
+ let body = parse_json_body::<UpdateBucketRequestBody, _, Error>(req).await?;
+ Ok(AdminApiRequest::UpdateBucket(UpdateBucketRequest {
+ id,
+ body,
+ }))
+ }
+
+ // Bucket-key permissions
+ Endpoint::BucketAllowKey => {
+ let req = parse_json_body::<BucketKeyPermChangeRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::AllowBucketKey(AllowBucketKeyRequest(req)))
+ }
+ Endpoint::BucketDenyKey => {
+ let req = parse_json_body::<BucketKeyPermChangeRequest, _, Error>(req).await?;
+ Ok(AdminApiRequest::DenyBucketKey(DenyBucketKeyRequest(req)))
+ }
+ // Bucket aliasing
+ Endpoint::GlobalAliasBucket { id, alias } => {
+ Ok(AdminApiRequest::AddBucketAlias(AddBucketAliasRequest {
+ bucket_id: id,
+ alias: BucketAliasEnum::Global {
+ global_alias: alias,
+ },
+ }))
+ }
+ Endpoint::GlobalUnaliasBucket { id, alias } => Ok(AdminApiRequest::RemoveBucketAlias(
+ RemoveBucketAliasRequest {
+ bucket_id: id,
+ alias: BucketAliasEnum::Global {
+ global_alias: alias,
+ },
+ },
+ )),
+ Endpoint::LocalAliasBucket {
+ id,
+ access_key_id,
+ alias,
+ } => Ok(AdminApiRequest::AddBucketAlias(AddBucketAliasRequest {
+ bucket_id: id,
+ alias: BucketAliasEnum::Local {
+ local_alias: alias,
+ access_key_id,
+ },
+ })),
+ Endpoint::LocalUnaliasBucket {
+ id,
+ access_key_id,
+ alias,
+ } => Ok(AdminApiRequest::RemoveBucketAlias(
+ RemoveBucketAliasRequest {
+ bucket_id: id,
+ alias: BucketAliasEnum::Local {
+ local_alias: alias,
+ access_key_id,
+ },
+ },
+ )),
+
+ // For endpoints that have different body content syntax, issue
+ // deprecation warning
+ _ => Err(Error::bad_request(format!(
+ "v1/ endpoint is no longer supported: {}",
+ v1_endpoint.name()
+ ))),
+ }
+ }
+
+ /// Get the kind of authorization which is required to perform the operation.
+ pub fn authorization_type(&self) -> Authorization {
+ match self {
+ Self::Options(_) => Authorization::None,
+ Self::Health(_) => Authorization::None,
+ Self::CheckDomain(_) => Authorization::None,
+ Self::Metrics(_) => Authorization::MetricsToken,
+ _ => Authorization::AdminToken,
+ }
+ }
+}
+
+generateQueryParameters! {
+ keywords: [],
+ fields: [
+ "domain" => domain,
+ "format" => format,
+ "id" => id,
+ "search" => search,
+ "globalAlias" => global_alias,
+ "alias" => alias,
+ "accessKeyId" => access_key_id,
+ "showSecretKey" => show_secret_key
+ ]
+}