From c1eb1610bab4d0d689dae9389f3fc10c0ab0efdc Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Mon, 27 Jan 2025 23:13:01 +0100 Subject: admin api: create structs for all requests/responess in src/api/admin/api.rs --- src/api/admin/bucket.rs | 210 ++++++++++++++---------------------------------- 1 file changed, 59 insertions(+), 151 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index ac3cba00..593848f0 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::sync::Arc; use hyper::{body::Incoming as IncomingBody, Request, Response, StatusCode}; -use serde::{Deserialize, Serialize}; use garage_util::crdt::*; use garage_util::data::*; @@ -17,9 +16,14 @@ use garage_model::permission::*; use garage_model::s3::mpu_table; use garage_model::s3::object_table::*; +use crate::admin::api::ApiBucketKeyPerm; +use crate::admin::api::{ + ApiBucketQuotas, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, + GetBucketInfoKey, GetBucketInfoResponse, GetBucketInfoWebsiteResponse, ListBucketsResponseItem, + UpdateBucketRequest, +}; use crate::admin::api_server::ResBody; use crate::admin::error::*; -use crate::admin::key::ApiBucketKeyPerm; use crate::common_error::CommonError; use crate::helpers::*; @@ -39,7 +43,7 @@ pub async fn handle_list_buckets(garage: &Arc) -> Result) -> Result, - local_aliases: Vec, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct BucketLocalAlias { - access_key_id: String, - alias: String, -} - -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct ApiBucketQuotas { - max_size: Option, - max_objects: Option, -} - pub async fn handle_get_bucket_info( garage: &Arc, id: Option, @@ -175,98 +157,63 @@ async fn bucket_info_results( let state = bucket.state.as_option().unwrap(); let quotas = state.quotas.get(); - let res = - GetBucketInfoResult { - id: hex::encode(bucket.id), - global_aliases: state - .aliases - .items() - .iter() - .filter(|(_, _, a)| *a) - .map(|(n, _, _)| n.to_string()) - .collect::>(), - website_access: state.website_config.get().is_some(), - website_config: state.website_config.get().clone().map(|wsc| { - GetBucketInfoWebsiteResult { - index_document: wsc.index_document, - error_document: wsc.error_document, + let res = GetBucketInfoResponse { + id: hex::encode(bucket.id), + global_aliases: state + .aliases + .items() + .iter() + .filter(|(_, _, a)| *a) + .map(|(n, _, _)| n.to_string()) + .collect::>(), + website_access: state.website_config.get().is_some(), + website_config: state.website_config.get().clone().map(|wsc| { + GetBucketInfoWebsiteResponse { + index_document: wsc.index_document, + error_document: wsc.error_document, + } + }), + keys: relevant_keys + .into_values() + .map(|key| { + let p = key.state.as_option().unwrap(); + GetBucketInfoKey { + access_key_id: key.key_id, + name: p.name.get().to_string(), + permissions: p + .authorized_buckets + .get(&bucket.id) + .map(|p| ApiBucketKeyPerm { + read: p.allow_read, + write: p.allow_write, + owner: p.allow_owner, + }) + .unwrap_or_default(), + bucket_local_aliases: p + .local_aliases + .items() + .iter() + .filter(|(_, _, b)| *b == Some(bucket.id)) + .map(|(n, _, _)| n.to_string()) + .collect::>(), } - }), - keys: relevant_keys - .into_values() - .map(|key| { - let p = key.state.as_option().unwrap(); - GetBucketInfoKey { - access_key_id: key.key_id, - name: p.name.get().to_string(), - permissions: p - .authorized_buckets - .get(&bucket.id) - .map(|p| ApiBucketKeyPerm { - read: p.allow_read, - write: p.allow_write, - owner: p.allow_owner, - }) - .unwrap_or_default(), - bucket_local_aliases: p - .local_aliases - .items() - .iter() - .filter(|(_, _, b)| *b == Some(bucket.id)) - .map(|(n, _, _)| n.to_string()) - .collect::>(), - } - }) - .collect::>(), - objects: *counters.get(OBJECTS).unwrap_or(&0), - bytes: *counters.get(BYTES).unwrap_or(&0), - unfinished_uploads: *counters.get(UNFINISHED_UPLOADS).unwrap_or(&0), - unfinished_multipart_uploads: *mpu_counters.get(mpu_table::UPLOADS).unwrap_or(&0), - unfinished_multipart_upload_parts: *mpu_counters.get(mpu_table::PARTS).unwrap_or(&0), - unfinished_multipart_upload_bytes: *mpu_counters.get(mpu_table::BYTES).unwrap_or(&0), - quotas: ApiBucketQuotas { - max_size: quotas.max_size, - max_objects: quotas.max_objects, - }, - }; + }) + .collect::>(), + objects: *counters.get(OBJECTS).unwrap_or(&0), + bytes: *counters.get(BYTES).unwrap_or(&0), + unfinished_uploads: *counters.get(UNFINISHED_UPLOADS).unwrap_or(&0), + unfinished_multipart_uploads: *mpu_counters.get(mpu_table::UPLOADS).unwrap_or(&0), + unfinished_multipart_upload_parts: *mpu_counters.get(mpu_table::PARTS).unwrap_or(&0), + unfinished_multipart_upload_bytes: *mpu_counters.get(mpu_table::BYTES).unwrap_or(&0), + quotas: ApiBucketQuotas { + max_size: quotas.max_size, + max_objects: quotas.max_objects, + }, + }; Ok(json_ok_response(&res)?) } -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct GetBucketInfoResult { - id: String, - global_aliases: Vec, - website_access: bool, - #[serde(default)] - website_config: Option, - keys: Vec, - objects: i64, - bytes: i64, - unfinished_uploads: i64, - unfinished_multipart_uploads: i64, - unfinished_multipart_upload_parts: i64, - unfinished_multipart_upload_bytes: i64, - quotas: ApiBucketQuotas, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct GetBucketInfoWebsiteResult { - index_document: String, - error_document: Option, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -struct GetBucketInfoKey { - access_key_id: String, - name: String, - permissions: ApiBucketKeyPerm, - bucket_local_aliases: Vec, -} - pub async fn handle_create_bucket( garage: &Arc, req: Request, @@ -336,22 +283,6 @@ pub async fn handle_create_bucket( bucket_info_results(garage, bucket.id).await } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct CreateBucketRequest { - global_alias: Option, - local_alias: Option, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct CreateBucketLocalAlias { - access_key_id: String, - alias: String, - #[serde(default)] - allow: ApiBucketKeyPerm, -} - pub async fn handle_delete_bucket( garage: &Arc, id: String, @@ -446,21 +377,6 @@ pub async fn handle_update_bucket( bucket_info_results(garage, bucket_id).await } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct UpdateBucketRequest { - website_access: Option, - quotas: Option, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct UpdateBucketWebsiteAccess { - enabled: bool, - index_document: Option, - error_document: Option, -} - // ---- BUCKET/KEY PERMISSIONS ---- pub async fn handle_bucket_change_key_perm( @@ -502,14 +418,6 @@ pub async fn handle_bucket_change_key_perm( bucket_info_results(garage, bucket.id).await } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct BucketKeyPermChangeRequest { - bucket_id: String, - access_key_id: String, - permissions: ApiBucketKeyPerm, -} - // ---- BUCKET ALIASES ---- pub async fn handle_global_alias_bucket( -- cgit v1.2.3 From 831f2b0207f128d67f061e6f7084337b1cbfefa4 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 28 Jan 2025 00:22:14 +0100 Subject: admin api: make all handlers impls of a single trait --- src/api/admin/bucket.rs | 522 ++++++++++++++++++++++++++---------------------- 1 file changed, 284 insertions(+), 238 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 593848f0..d62bfa54 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::sync::Arc; -use hyper::{body::Incoming as IncomingBody, Request, Response, StatusCode}; +use async_trait::async_trait; use garage_util::crdt::*; use garage_util::data::*; @@ -18,83 +18,93 @@ use garage_model::s3::object_table::*; use crate::admin::api::ApiBucketKeyPerm; use crate::admin::api::{ - ApiBucketQuotas, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, - GetBucketInfoKey, GetBucketInfoResponse, GetBucketInfoWebsiteResponse, ListBucketsResponseItem, - UpdateBucketRequest, + ApiBucketQuotas, BucketAllowKeyRequest, BucketAllowKeyResponse, BucketDenyKeyRequest, + BucketDenyKeyResponse, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, + CreateBucketResponse, DeleteBucketRequest, DeleteBucketResponse, GetBucketInfoKey, + GetBucketInfoRequest, GetBucketInfoResponse, GetBucketInfoWebsiteResponse, + GlobalAliasBucketRequest, GlobalAliasBucketResponse, GlobalUnaliasBucketRequest, + GlobalUnaliasBucketResponse, ListBucketsRequest, ListBucketsResponse, ListBucketsResponseItem, + LocalAliasBucketRequest, LocalAliasBucketResponse, LocalUnaliasBucketRequest, + LocalUnaliasBucketResponse, UpdateBucketRequest, UpdateBucketResponse, }; -use crate::admin::api_server::ResBody; use crate::admin::error::*; +use crate::admin::EndpointHandler; use crate::common_error::CommonError; -use crate::helpers::*; - -pub async fn handle_list_buckets(garage: &Arc) -> Result, Error> { - let buckets = garage - .bucket_table - .get_range( - &EmptyKey, - None, - Some(DeletedFilter::NotDeleted), - 10000, - EnumerationOrder::Forward, - ) - .await?; - let res = buckets - .into_iter() - .map(|b| { - let state = b.state.as_option().unwrap(); - ListBucketsResponseItem { - id: hex::encode(b.id), - global_aliases: state - .aliases - .items() - .iter() - .filter(|(_, _, a)| *a) - .map(|(n, _, _)| n.to_string()) - .collect::>(), - local_aliases: state - .local_aliases - .items() - .iter() - .filter(|(_, _, a)| *a) - .map(|((k, n), _, _)| BucketLocalAlias { - access_key_id: k.to_string(), - alias: n.to_string(), - }) - .collect::>(), - } - }) - .collect::>(); +#[async_trait] +impl EndpointHandler for ListBucketsRequest { + type Response = ListBucketsResponse; + + async fn handle(self, garage: &Arc) -> Result { + let buckets = garage + .bucket_table + .get_range( + &EmptyKey, + None, + Some(DeletedFilter::NotDeleted), + 10000, + EnumerationOrder::Forward, + ) + .await?; + + let res = buckets + .into_iter() + .map(|b| { + let state = b.state.as_option().unwrap(); + ListBucketsResponseItem { + id: hex::encode(b.id), + global_aliases: state + .aliases + .items() + .iter() + .filter(|(_, _, a)| *a) + .map(|(n, _, _)| n.to_string()) + .collect::>(), + local_aliases: state + .local_aliases + .items() + .iter() + .filter(|(_, _, a)| *a) + .map(|((k, n), _, _)| BucketLocalAlias { + access_key_id: k.to_string(), + alias: n.to_string(), + }) + .collect::>(), + } + }) + .collect::>(); - Ok(json_ok_response(&res)?) + Ok(ListBucketsResponse(res)) + } } -pub async fn handle_get_bucket_info( - garage: &Arc, - id: Option, - global_alias: Option, -) -> Result, Error> { - let bucket_id = match (id, global_alias) { - (Some(id), None) => parse_bucket_id(&id)?, - (None, Some(ga)) => garage - .bucket_helper() - .resolve_global_bucket_name(&ga) - .await? - .ok_or_else(|| HelperError::NoSuchBucket(ga.to_string()))?, - _ => { - return Err(Error::bad_request( - "Either id or globalAlias must be provided (but not both)", - )); - } - }; +#[async_trait] +impl EndpointHandler for GetBucketInfoRequest { + type Response = GetBucketInfoResponse; + + async fn handle(self, garage: &Arc) -> Result { + let bucket_id = match (self.id, self.global_alias) { + (Some(id), None) => parse_bucket_id(&id)?, + (None, Some(ga)) => garage + .bucket_helper() + .resolve_global_bucket_name(&ga) + .await? + .ok_or_else(|| HelperError::NoSuchBucket(ga.to_string()))?, + _ => { + return Err(Error::bad_request( + "Either id or globalAlias must be provided (but not both)", + )); + } + }; - bucket_info_results(garage, bucket_id).await + bucket_info_results(garage, bucket_id).await + } } async fn bucket_info_results( garage: &Arc, bucket_id: Uuid, -) -> Result, Error> { +) -> Result { let bucket = garage .bucket_helper() .get_existing_bucket(bucket_id) @@ -211,181 +221,203 @@ async fn bucket_info_results( }, }; - Ok(json_ok_response(&res)?) + Ok(res) } -pub async fn handle_create_bucket( - garage: &Arc, - req: Request, -) -> Result, Error> { - let req = parse_json_body::(req).await?; +#[async_trait] +impl EndpointHandler for CreateBucketRequest { + type Response = CreateBucketResponse; - let helper = garage.locked_helper().await; + async fn handle(self, garage: &Arc) -> Result { + let helper = garage.locked_helper().await; - if let Some(ga) = &req.global_alias { - if !is_valid_bucket_name(ga) { - return Err(Error::bad_request(format!( - "{}: {}", - ga, INVALID_BUCKET_NAME_MESSAGE - ))); - } + if let Some(ga) = &self.global_alias { + if !is_valid_bucket_name(ga) { + return Err(Error::bad_request(format!( + "{}: {}", + ga, INVALID_BUCKET_NAME_MESSAGE + ))); + } - if let Some(alias) = garage.bucket_alias_table.get(&EmptyKey, ga).await? { - if alias.state.get().is_some() { - return Err(CommonError::BucketAlreadyExists.into()); + if let Some(alias) = garage.bucket_alias_table.get(&EmptyKey, ga).await? { + if alias.state.get().is_some() { + return Err(CommonError::BucketAlreadyExists.into()); + } } } - } - if let Some(la) = &req.local_alias { - if !is_valid_bucket_name(&la.alias) { - return Err(Error::bad_request(format!( - "{}: {}", - la.alias, INVALID_BUCKET_NAME_MESSAGE - ))); - } + if let Some(la) = &self.local_alias { + if !is_valid_bucket_name(&la.alias) { + return Err(Error::bad_request(format!( + "{}: {}", + la.alias, INVALID_BUCKET_NAME_MESSAGE + ))); + } - let key = helper.key().get_existing_key(&la.access_key_id).await?; - let state = key.state.as_option().unwrap(); - if matches!(state.local_aliases.get(&la.alias), Some(_)) { - return Err(Error::bad_request("Local alias already exists")); + let key = helper.key().get_existing_key(&la.access_key_id).await?; + let state = key.state.as_option().unwrap(); + if matches!(state.local_aliases.get(&la.alias), Some(_)) { + return Err(Error::bad_request("Local alias already exists")); + } } - } - - let bucket = Bucket::new(); - garage.bucket_table.insert(&bucket).await?; - if let Some(ga) = &req.global_alias { - helper.set_global_bucket_alias(bucket.id, ga).await?; - } + let bucket = Bucket::new(); + garage.bucket_table.insert(&bucket).await?; - if let Some(la) = &req.local_alias { - helper - .set_local_bucket_alias(bucket.id, &la.access_key_id, &la.alias) - .await?; + if let Some(ga) = &self.global_alias { + helper.set_global_bucket_alias(bucket.id, ga).await?; + } - if la.allow.read || la.allow.write || la.allow.owner { + if let Some(la) = &self.local_alias { helper - .set_bucket_key_permissions( - bucket.id, - &la.access_key_id, - BucketKeyPerm { - timestamp: now_msec(), - allow_read: la.allow.read, - allow_write: la.allow.write, - allow_owner: la.allow.owner, - }, - ) + .set_local_bucket_alias(bucket.id, &la.access_key_id, &la.alias) .await?; + + if la.allow.read || la.allow.write || la.allow.owner { + helper + .set_bucket_key_permissions( + bucket.id, + &la.access_key_id, + BucketKeyPerm { + timestamp: now_msec(), + allow_read: la.allow.read, + allow_write: la.allow.write, + allow_owner: la.allow.owner, + }, + ) + .await?; + } } - } - bucket_info_results(garage, bucket.id).await + Ok(CreateBucketResponse( + bucket_info_results(garage, bucket.id).await?, + )) + } } -pub async fn handle_delete_bucket( - garage: &Arc, - id: String, -) -> Result, Error> { - let helper = garage.locked_helper().await; +#[async_trait] +impl EndpointHandler for DeleteBucketRequest { + type Response = DeleteBucketResponse; - let bucket_id = parse_bucket_id(&id)?; + async fn handle(self, garage: &Arc) -> Result { + let helper = garage.locked_helper().await; - let mut bucket = helper.bucket().get_existing_bucket(bucket_id).await?; - let state = bucket.state.as_option().unwrap(); + let bucket_id = parse_bucket_id(&self.id)?; - // Check bucket is empty - if !helper.bucket().is_bucket_empty(bucket_id).await? { - return Err(CommonError::BucketNotEmpty.into()); - } + let mut bucket = helper.bucket().get_existing_bucket(bucket_id).await?; + let state = bucket.state.as_option().unwrap(); - // --- done checking, now commit --- - // 1. delete authorization from keys that had access - for (key_id, perm) in bucket.authorized_keys() { - if perm.is_any() { - helper - .set_bucket_key_permissions(bucket.id, key_id, BucketKeyPerm::NO_PERMISSIONS) - .await?; + // Check bucket is empty + if !helper.bucket().is_bucket_empty(bucket_id).await? { + return Err(CommonError::BucketNotEmpty.into()); } - } - // 2. delete all local aliases - for ((key_id, alias), _, active) in state.local_aliases.items().iter() { - if *active { - helper - .unset_local_bucket_alias(bucket.id, key_id, alias) - .await?; + + // --- done checking, now commit --- + // 1. delete authorization from keys that had access + for (key_id, perm) in bucket.authorized_keys() { + if perm.is_any() { + helper + .set_bucket_key_permissions(bucket.id, key_id, BucketKeyPerm::NO_PERMISSIONS) + .await?; + } } - } - // 3. delete all global aliases - for (alias, _, active) in state.aliases.items().iter() { - if *active { - helper.purge_global_bucket_alias(bucket.id, alias).await?; + // 2. delete all local aliases + for ((key_id, alias), _, active) in state.local_aliases.items().iter() { + if *active { + helper + .unset_local_bucket_alias(bucket.id, key_id, alias) + .await?; + } + } + // 3. delete all global aliases + for (alias, _, active) in state.aliases.items().iter() { + if *active { + helper.purge_global_bucket_alias(bucket.id, alias).await?; + } } - } - // 4. delete bucket - bucket.state = Deletable::delete(); - garage.bucket_table.insert(&bucket).await?; + // 4. delete bucket + bucket.state = Deletable::delete(); + garage.bucket_table.insert(&bucket).await?; - Ok(Response::builder() - .status(StatusCode::NO_CONTENT) - .body(empty_body())?) + Ok(DeleteBucketResponse) + } } -pub async fn handle_update_bucket( - garage: &Arc, - id: String, - req: Request, -) -> Result, Error> { - let req = parse_json_body::(req).await?; - let bucket_id = parse_bucket_id(&id)?; +#[async_trait] +impl EndpointHandler for UpdateBucketRequest { + type Response = UpdateBucketResponse; - let mut bucket = garage - .bucket_helper() - .get_existing_bucket(bucket_id) - .await?; + async fn handle(self, garage: &Arc) -> Result { + let bucket_id = parse_bucket_id(&self.id)?; - let state = bucket.state.as_option_mut().unwrap(); - - if let Some(wa) = req.website_access { - if wa.enabled { - state.website_config.update(Some(WebsiteConfig { - index_document: wa.index_document.ok_or_bad_request( - "Please specify indexDocument when enabling website access.", - )?, - error_document: wa.error_document, - })); - } else { - if wa.index_document.is_some() || wa.error_document.is_some() { - return Err(Error::bad_request( - "Cannot specify indexDocument or errorDocument when disabling website access.", - )); + let mut bucket = garage + .bucket_helper() + .get_existing_bucket(bucket_id) + .await?; + + let state = bucket.state.as_option_mut().unwrap(); + + if let Some(wa) = self.params.website_access { + if wa.enabled { + state.website_config.update(Some(WebsiteConfig { + index_document: wa.index_document.ok_or_bad_request( + "Please specify indexDocument when enabling website access.", + )?, + error_document: wa.error_document, + })); + } else { + if wa.index_document.is_some() || wa.error_document.is_some() { + return Err(Error::bad_request( + "Cannot specify indexDocument or errorDocument when disabling website access.", + )); + } + state.website_config.update(None); } - state.website_config.update(None); } - } - if let Some(q) = req.quotas { - state.quotas.update(BucketQuotas { - max_size: q.max_size, - max_objects: q.max_objects, - }); - } + if let Some(q) = self.params.quotas { + state.quotas.update(BucketQuotas { + max_size: q.max_size, + max_objects: q.max_objects, + }); + } - garage.bucket_table.insert(&bucket).await?; + garage.bucket_table.insert(&bucket).await?; - bucket_info_results(garage, bucket_id).await + Ok(UpdateBucketResponse( + bucket_info_results(garage, bucket_id).await?, + )) + } } // ---- BUCKET/KEY PERMISSIONS ---- +#[async_trait] +impl EndpointHandler for BucketAllowKeyRequest { + type Response = BucketAllowKeyResponse; + + async fn handle(self, garage: &Arc) -> Result { + let res = handle_bucket_change_key_perm(garage, self.0, true).await?; + Ok(BucketAllowKeyResponse(res)) + } +} + +#[async_trait] +impl EndpointHandler for BucketDenyKeyRequest { + type Response = BucketDenyKeyResponse; + + async fn handle(self, garage: &Arc) -> Result { + let res = handle_bucket_change_key_perm(garage, self.0, false).await?; + Ok(BucketDenyKeyResponse(res)) + } +} + pub async fn handle_bucket_change_key_perm( garage: &Arc, - req: Request, + req: BucketKeyPermChangeRequest, new_perm_flag: bool, -) -> Result, Error> { - let req = parse_json_body::(req).await?; - +) -> Result { let helper = garage.locked_helper().await; let bucket_id = parse_bucket_id(&req.bucket_id)?; @@ -420,66 +452,80 @@ pub async fn handle_bucket_change_key_perm( // ---- BUCKET ALIASES ---- -pub async fn handle_global_alias_bucket( - garage: &Arc, - bucket_id: String, - alias: String, -) -> Result, Error> { - let bucket_id = parse_bucket_id(&bucket_id)?; +#[async_trait] +impl EndpointHandler for GlobalAliasBucketRequest { + type Response = GlobalAliasBucketResponse; - let helper = garage.locked_helper().await; + async fn handle(self, garage: &Arc) -> Result { + let bucket_id = parse_bucket_id(&self.id)?; - helper.set_global_bucket_alias(bucket_id, &alias).await?; + let helper = garage.locked_helper().await; - bucket_info_results(garage, bucket_id).await + helper + .set_global_bucket_alias(bucket_id, &self.alias) + .await?; + + Ok(GlobalAliasBucketResponse( + bucket_info_results(garage, bucket_id).await?, + )) + } } -pub async fn handle_global_unalias_bucket( - garage: &Arc, - bucket_id: String, - alias: String, -) -> Result, Error> { - let bucket_id = parse_bucket_id(&bucket_id)?; +#[async_trait] +impl EndpointHandler for GlobalUnaliasBucketRequest { + type Response = GlobalUnaliasBucketResponse; - let helper = garage.locked_helper().await; + async fn handle(self, garage: &Arc) -> Result { + let bucket_id = parse_bucket_id(&self.id)?; + + let helper = garage.locked_helper().await; - helper.unset_global_bucket_alias(bucket_id, &alias).await?; + helper + .unset_global_bucket_alias(bucket_id, &self.alias) + .await?; - bucket_info_results(garage, bucket_id).await + Ok(GlobalUnaliasBucketResponse( + bucket_info_results(garage, bucket_id).await?, + )) + } } -pub async fn handle_local_alias_bucket( - garage: &Arc, - bucket_id: String, - access_key_id: String, - alias: String, -) -> Result, Error> { - let bucket_id = parse_bucket_id(&bucket_id)?; +#[async_trait] +impl EndpointHandler for LocalAliasBucketRequest { + type Response = LocalAliasBucketResponse; - let helper = garage.locked_helper().await; + async fn handle(self, garage: &Arc) -> Result { + let bucket_id = parse_bucket_id(&self.id)?; - helper - .set_local_bucket_alias(bucket_id, &access_key_id, &alias) - .await?; + let helper = garage.locked_helper().await; - bucket_info_results(garage, bucket_id).await + helper + .set_local_bucket_alias(bucket_id, &self.access_key_id, &self.alias) + .await?; + + Ok(LocalAliasBucketResponse( + bucket_info_results(garage, bucket_id).await?, + )) + } } -pub async fn handle_local_unalias_bucket( - garage: &Arc, - bucket_id: String, - access_key_id: String, - alias: String, -) -> Result, Error> { - let bucket_id = parse_bucket_id(&bucket_id)?; +#[async_trait] +impl EndpointHandler for LocalUnaliasBucketRequest { + type Response = LocalUnaliasBucketResponse; - let helper = garage.locked_helper().await; + async fn handle(self, garage: &Arc) -> Result { + let bucket_id = parse_bucket_id(&self.id)?; - helper - .unset_local_bucket_alias(bucket_id, &access_key_id, &alias) - .await?; + let helper = garage.locked_helper().await; - bucket_info_results(garage, bucket_id).await + helper + .unset_local_bucket_alias(bucket_id, &self.access_key_id, &self.alias) + .await?; + + Ok(LocalUnaliasBucketResponse( + bucket_info_results(garage, bucket_id).await?, + )) + } } // ---- HELPER ---- -- cgit v1.2.3 From c99bfe69ea19497895d32669fd15c689b86035d8 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 28 Jan 2025 15:12:03 +0100 Subject: admin api: new router_v2 with unified path syntax --- src/api/admin/bucket.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index d62bfa54..f9accba5 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -358,7 +358,7 @@ impl EndpointHandler for UpdateBucketRequest { let state = bucket.state.as_option_mut().unwrap(); - if let Some(wa) = self.params.website_access { + if let Some(wa) = self.body.website_access { if wa.enabled { state.website_config.update(Some(WebsiteConfig { index_document: wa.index_document.ok_or_bad_request( @@ -376,7 +376,7 @@ impl EndpointHandler for UpdateBucketRequest { } } - if let Some(q) = self.params.quotas { + if let Some(q) = self.body.quotas { state.quotas.update(BucketQuotas { max_size: q.max_size, max_objects: q.max_objects, -- cgit v1.2.3 From ed58f8b0fe3c44eac7416b3aaa444d1b568f8918 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 28 Jan 2025 16:18:48 +0100 Subject: admin api: update semantics of some endpoints, and update doc --- src/api/admin/bucket.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index f9accba5..8e19b93e 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -457,7 +457,7 @@ impl EndpointHandler for GlobalAliasBucketRequest { type Response = GlobalAliasBucketResponse; async fn handle(self, garage: &Arc) -> Result { - let bucket_id = parse_bucket_id(&self.id)?; + let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; @@ -476,7 +476,7 @@ impl EndpointHandler for GlobalUnaliasBucketRequest { type Response = GlobalUnaliasBucketResponse; async fn handle(self, garage: &Arc) -> Result { - let bucket_id = parse_bucket_id(&self.id)?; + let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; @@ -495,7 +495,7 @@ impl EndpointHandler for LocalAliasBucketRequest { type Response = LocalAliasBucketResponse; async fn handle(self, garage: &Arc) -> Result { - let bucket_id = parse_bucket_id(&self.id)?; + let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; @@ -514,7 +514,7 @@ impl EndpointHandler for LocalUnaliasBucketRequest { type Response = LocalUnaliasBucketResponse; async fn handle(self, garage: &Arc) -> Result { - let bucket_id = parse_bucket_id(&self.id)?; + let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; -- cgit v1.2.3 From ba810b2e8157855df36b5f8dc9d5fced40efbafd Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 28 Jan 2025 18:51:15 +0100 Subject: admin api: rename bucket aliasing operations --- src/api/admin/bucket.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 8e19b93e..09952bff 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -22,10 +22,10 @@ use crate::admin::api::{ BucketDenyKeyResponse, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, CreateBucketResponse, DeleteBucketRequest, DeleteBucketResponse, GetBucketInfoKey, GetBucketInfoRequest, GetBucketInfoResponse, GetBucketInfoWebsiteResponse, - GlobalAliasBucketRequest, GlobalAliasBucketResponse, GlobalUnaliasBucketRequest, - GlobalUnaliasBucketResponse, ListBucketsRequest, ListBucketsResponse, ListBucketsResponseItem, - LocalAliasBucketRequest, LocalAliasBucketResponse, LocalUnaliasBucketRequest, - LocalUnaliasBucketResponse, UpdateBucketRequest, UpdateBucketResponse, + AddGlobalBucketAliasRequest, AddGlobalBucketAliasResponse, RemoveGlobalBucketAliasRequest, + RemoveGlobalBucketAliasResponse, ListBucketsRequest, ListBucketsResponse, ListBucketsResponseItem, + AddLocalBucketAliasRequest, AddLocalBucketAliasResponse, RemoveLocalBucketAliasRequest, + RemoveLocalBucketAliasResponse, UpdateBucketRequest, UpdateBucketResponse, }; use crate::admin::error::*; use crate::admin::EndpointHandler; @@ -453,10 +453,10 @@ pub async fn handle_bucket_change_key_perm( // ---- BUCKET ALIASES ---- #[async_trait] -impl EndpointHandler for GlobalAliasBucketRequest { - type Response = GlobalAliasBucketResponse; +impl EndpointHandler for AddGlobalBucketAliasRequest { + type Response = AddGlobalBucketAliasResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; @@ -465,17 +465,17 @@ impl EndpointHandler for GlobalAliasBucketRequest { .set_global_bucket_alias(bucket_id, &self.alias) .await?; - Ok(GlobalAliasBucketResponse( + Ok(AddGlobalBucketAliasResponse( bucket_info_results(garage, bucket_id).await?, )) } } #[async_trait] -impl EndpointHandler for GlobalUnaliasBucketRequest { - type Response = GlobalUnaliasBucketResponse; +impl EndpointHandler for RemoveGlobalBucketAliasRequest { + type Response = RemoveGlobalBucketAliasResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; @@ -484,17 +484,17 @@ impl EndpointHandler for GlobalUnaliasBucketRequest { .unset_global_bucket_alias(bucket_id, &self.alias) .await?; - Ok(GlobalUnaliasBucketResponse( + Ok(RemoveGlobalBucketAliasResponse( bucket_info_results(garage, bucket_id).await?, )) } } #[async_trait] -impl EndpointHandler for LocalAliasBucketRequest { - type Response = LocalAliasBucketResponse; +impl EndpointHandler for AddLocalBucketAliasRequest { + type Response = AddLocalBucketAliasResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; @@ -503,17 +503,17 @@ impl EndpointHandler for LocalAliasBucketRequest { .set_local_bucket_alias(bucket_id, &self.access_key_id, &self.alias) .await?; - Ok(LocalAliasBucketResponse( + Ok(AddLocalBucketAliasResponse( bucket_info_results(garage, bucket_id).await?, )) } } #[async_trait] -impl EndpointHandler for LocalUnaliasBucketRequest { - type Response = LocalUnaliasBucketResponse; +impl EndpointHandler for RemoveLocalBucketAliasRequest { + type Response = RemoveLocalBucketAliasResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; @@ -522,7 +522,7 @@ impl EndpointHandler for LocalUnaliasBucketRequest { .unset_local_bucket_alias(bucket_id, &self.access_key_id, &self.alias) .await?; - Ok(LocalUnaliasBucketResponse( + Ok(RemoveLocalBucketAliasResponse( bucket_info_results(garage, bucket_id).await?, )) } -- cgit v1.2.3 From 5fefbd94e9f8cded0d911f7cdae3d0382762607c Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 28 Jan 2025 18:53:44 +0100 Subject: admin api: rename allow/deny api calls in api v2 --- src/api/admin/bucket.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 09952bff..885c1749 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -18,8 +18,8 @@ use garage_model::s3::object_table::*; use crate::admin::api::ApiBucketKeyPerm; use crate::admin::api::{ - ApiBucketQuotas, BucketAllowKeyRequest, BucketAllowKeyResponse, BucketDenyKeyRequest, - BucketDenyKeyResponse, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, + ApiBucketQuotas, AllowBucketKeyRequest, AllowBucketKeyResponse, DenyBucketKeyRequest, + DenyBucketKeyResponse, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, CreateBucketResponse, DeleteBucketRequest, DeleteBucketResponse, GetBucketInfoKey, GetBucketInfoRequest, GetBucketInfoResponse, GetBucketInfoWebsiteResponse, AddGlobalBucketAliasRequest, AddGlobalBucketAliasResponse, RemoveGlobalBucketAliasRequest, @@ -394,22 +394,22 @@ impl EndpointHandler for UpdateBucketRequest { // ---- BUCKET/KEY PERMISSIONS ---- #[async_trait] -impl EndpointHandler for BucketAllowKeyRequest { - type Response = BucketAllowKeyResponse; +impl EndpointHandler for AllowBucketKeyRequest { + type Response = AllowBucketKeyResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let res = handle_bucket_change_key_perm(garage, self.0, true).await?; - Ok(BucketAllowKeyResponse(res)) + Ok(AllowBucketKeyResponse(res)) } } #[async_trait] -impl EndpointHandler for BucketDenyKeyRequest { - type Response = BucketDenyKeyResponse; +impl EndpointHandler for DenyBucketKeyRequest { + type Response = DenyBucketKeyResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let res = handle_bucket_change_key_perm(garage, self.0, false).await?; - Ok(BucketDenyKeyResponse(res)) + Ok(DenyBucketKeyResponse(res)) } } -- cgit v1.2.3 From 12ea4cda5fe033fc2b9f1fec51ddc3d8b860a85f Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Tue, 28 Jan 2025 19:03:39 +0100 Subject: admin api: merge calls to manage global/local aliases --- src/api/admin/bucket.rs | 98 +++++++++++++++++++------------------------------ 1 file changed, 38 insertions(+), 60 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 885c1749..ee7a5e12 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -18,14 +18,12 @@ use garage_model::s3::object_table::*; use crate::admin::api::ApiBucketKeyPerm; use crate::admin::api::{ - ApiBucketQuotas, AllowBucketKeyRequest, AllowBucketKeyResponse, DenyBucketKeyRequest, - DenyBucketKeyResponse, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, - CreateBucketResponse, DeleteBucketRequest, DeleteBucketResponse, GetBucketInfoKey, - GetBucketInfoRequest, GetBucketInfoResponse, GetBucketInfoWebsiteResponse, - AddGlobalBucketAliasRequest, AddGlobalBucketAliasResponse, RemoveGlobalBucketAliasRequest, - RemoveGlobalBucketAliasResponse, ListBucketsRequest, ListBucketsResponse, ListBucketsResponseItem, - AddLocalBucketAliasRequest, AddLocalBucketAliasResponse, RemoveLocalBucketAliasRequest, - RemoveLocalBucketAliasResponse, UpdateBucketRequest, UpdateBucketResponse, + AddBucketAliasRequest, AddBucketAliasResponse, AllowBucketKeyRequest, AllowBucketKeyResponse, + ApiBucketQuotas, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, + CreateBucketResponse, DeleteBucketRequest, DeleteBucketResponse, DenyBucketKeyRequest, + DenyBucketKeyResponse, GetBucketInfoKey, GetBucketInfoRequest, GetBucketInfoResponse, + GetBucketInfoWebsiteResponse, ListBucketsRequest, ListBucketsResponse, ListBucketsResponseItem, + RemoveBucketAliasRequest, RemoveBucketAliasResponse, UpdateBucketRequest, UpdateBucketResponse, }; use crate::admin::error::*; use crate::admin::EndpointHandler; @@ -453,76 +451,56 @@ pub async fn handle_bucket_change_key_perm( // ---- BUCKET ALIASES ---- #[async_trait] -impl EndpointHandler for AddGlobalBucketAliasRequest { - type Response = AddGlobalBucketAliasResponse; +impl EndpointHandler for AddBucketAliasRequest { + type Response = AddBucketAliasResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; - helper - .set_global_bucket_alias(bucket_id, &self.alias) - .await?; - - Ok(AddGlobalBucketAliasResponse( - bucket_info_results(garage, bucket_id).await?, - )) - } -} - -#[async_trait] -impl EndpointHandler for RemoveGlobalBucketAliasRequest { - type Response = RemoveGlobalBucketAliasResponse; - - async fn handle(self, garage: &Arc) -> Result { - let bucket_id = parse_bucket_id(&self.bucket_id)?; - - let helper = garage.locked_helper().await; - - helper - .unset_global_bucket_alias(bucket_id, &self.alias) - .await?; - - Ok(RemoveGlobalBucketAliasResponse( - bucket_info_results(garage, bucket_id).await?, - )) - } -} - -#[async_trait] -impl EndpointHandler for AddLocalBucketAliasRequest { - type Response = AddLocalBucketAliasResponse; - - async fn handle(self, garage: &Arc) -> Result { - let bucket_id = parse_bucket_id(&self.bucket_id)?; - - let helper = garage.locked_helper().await; - - helper - .set_local_bucket_alias(bucket_id, &self.access_key_id, &self.alias) - .await?; + match self.access_key_id { + None => { + helper + .set_global_bucket_alias(bucket_id, &self.alias) + .await?; + } + Some(ak) => { + helper + .set_local_bucket_alias(bucket_id, &ak, &self.alias) + .await?; + } + } - Ok(AddLocalBucketAliasResponse( + Ok(AddBucketAliasResponse( bucket_info_results(garage, bucket_id).await?, )) } } #[async_trait] -impl EndpointHandler for RemoveLocalBucketAliasRequest { - type Response = RemoveLocalBucketAliasResponse; +impl EndpointHandler for RemoveBucketAliasRequest { + type Response = RemoveBucketAliasResponse; - async fn handle(self, garage: &Arc) -> Result { + async fn handle(self, garage: &Arc) -> Result { let bucket_id = parse_bucket_id(&self.bucket_id)?; let helper = garage.locked_helper().await; - helper - .unset_local_bucket_alias(bucket_id, &self.access_key_id, &self.alias) - .await?; + match self.access_key_id { + None => { + helper + .unset_global_bucket_alias(bucket_id, &self.alias) + .await?; + } + Some(ak) => { + helper + .unset_local_bucket_alias(bucket_id, &ak, &self.alias) + .await?; + } + } - Ok(RemoveLocalBucketAliasResponse( + Ok(RemoveBucketAliasResponse( bucket_info_results(garage, bucket_id).await?, )) } -- cgit v1.2.3 From 420bbc162dffd1246544168cf2e935efc60c5c98 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 29 Jan 2025 11:06:45 +0100 Subject: admin api: clearer syntax for AddBucketAlias and RemoveBucketAlias --- src/api/admin/bucket.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'src/api/admin/bucket.rs') diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index ee7a5e12..0cc420ec 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -16,15 +16,7 @@ use garage_model::permission::*; use garage_model::s3::mpu_table; use garage_model::s3::object_table::*; -use crate::admin::api::ApiBucketKeyPerm; -use crate::admin::api::{ - AddBucketAliasRequest, AddBucketAliasResponse, AllowBucketKeyRequest, AllowBucketKeyResponse, - ApiBucketQuotas, BucketKeyPermChangeRequest, BucketLocalAlias, CreateBucketRequest, - CreateBucketResponse, DeleteBucketRequest, DeleteBucketResponse, DenyBucketKeyRequest, - DenyBucketKeyResponse, GetBucketInfoKey, GetBucketInfoRequest, GetBucketInfoResponse, - GetBucketInfoWebsiteResponse, ListBucketsRequest, ListBucketsResponse, ListBucketsResponseItem, - RemoveBucketAliasRequest, RemoveBucketAliasResponse, UpdateBucketRequest, UpdateBucketResponse, -}; +use crate::admin::api::*; use crate::admin::error::*; use crate::admin::EndpointHandler; use crate::common_error::CommonError; @@ -459,15 +451,18 @@ impl EndpointHandler for AddBucketAliasRequest { let helper = garage.locked_helper().await; - match self.access_key_id { - None => { + match self.alias { + BucketAliasEnum::Global { global_alias } => { helper - .set_global_bucket_alias(bucket_id, &self.alias) + .set_global_bucket_alias(bucket_id, &global_alias) .await?; } - Some(ak) => { + BucketAliasEnum::Local { + local_alias, + access_key_id, + } => { helper - .set_local_bucket_alias(bucket_id, &ak, &self.alias) + .set_local_bucket_alias(bucket_id, &access_key_id, &local_alias) .await?; } } @@ -487,15 +482,18 @@ impl EndpointHandler for RemoveBucketAliasRequest { let helper = garage.locked_helper().await; - match self.access_key_id { - None => { + match self.alias { + BucketAliasEnum::Global { global_alias } => { helper - .unset_global_bucket_alias(bucket_id, &self.alias) + .unset_global_bucket_alias(bucket_id, &global_alias) .await?; } - Some(ak) => { + BucketAliasEnum::Local { + local_alias, + access_key_id, + } => { helper - .unset_local_bucket_alias(bucket_id, &ak, &self.alias) + .unset_local_bucket_alias(bucket_id, &access_key_id, &local_alias) .await?; } } -- cgit v1.2.3