diff --git a/doc/drafts/admin-api.md b/doc/drafts/admin-api.md index 5dc3f127..14c4ec39 100644 --- a/doc/drafts/admin-api.md +++ b/doc/drafts/admin-api.md @@ -501,3 +501,22 @@ Request body format: Flags in `permissions` which have the value `true` will be deactivated. Other flags will remain unchanged. + +## Operations on bucket aliases + +### GlobalAliasBucket `PUT /bucket/alias/global?id=&alias=` + +Empty body. Creates a global alias for a bucket. + +### GlobalUnaliasBucket `DELETE /bucket/alias/global?id=&alias=` + +Removes a global alias for a bucket. + +### LocalAliasBucket `PUT /bucket/alias/local?id=&accessKeyId=&alias=` + +Empty body. Creates a local alias for a bucket in the namespace of a specific access key. + +### LocalUnaliasBucket `DELETE /bucket/alias/local?id=&accessKeyId&alias=` + +Removes a local alias for a bucket in the namespace of a specific access key. + diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs index 098a54aa..a51d66e5 100644 --- a/src/api/admin/api_server.rs +++ b/src/api/admin/api_server.rs @@ -151,6 +151,23 @@ impl ApiHandler for AdminApiServer { Endpoint::BucketDenyKey => { handle_bucket_change_key_perm(&self.garage, req, false).await } + // Bucket aliasing + Endpoint::GlobalAliasBucket { id, alias } => { + handle_global_alias_bucket(&self.garage, id, alias).await + } + Endpoint::GlobalUnaliasBucket { id, alias } => { + handle_global_unalias_bucket(&self.garage, id, alias).await + } + Endpoint::LocalAliasBucket { + id, + access_key_id, + alias, + } => handle_local_alias_bucket(&self.garage, id, access_key_id, alias).await, + Endpoint::LocalUnaliasBucket { + id, + access_key_id, + alias, + } => handle_local_unalias_bucket(&self.garage, id, access_key_id, alias).await, } } } diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs index 2124f2c2..cc37089a 100644 --- a/src/api/admin/bucket.rs +++ b/src/api/admin/bucket.rs @@ -87,10 +87,7 @@ pub async fn handle_get_bucket_info( global_alias: Option, ) -> Result, Error> { let bucket_id = match (id, global_alias) { - (Some(id), None) => { - let id_hex = hex::decode(&id).ok_or_bad_request("Invalid bucket id")?; - Uuid::try_from(&id_hex).ok_or_bad_request("Invalid bucket id")? - } + (Some(id), None) => parse_bucket_id(&id)?, (None, Some(ga)) => garage .bucket_helper() .resolve_global_bucket_name(&ga) @@ -324,8 +321,7 @@ pub async fn handle_delete_bucket( ) -> Result, Error> { let helper = garage.bucket_helper(); - let id_hex = hex::decode(&id).ok_or_bad_request("Invalid bucket id")?; - let bucket_id = Uuid::try_from(&id_hex).ok_or_bad_request("Invalid bucket id")?; + let bucket_id = parse_bucket_id(&id)?; let mut bucket = helper.get_existing_bucket(bucket_id).await?; let state = bucket.state.as_option().unwrap(); @@ -385,8 +381,7 @@ pub async fn handle_bucket_change_key_perm( ) -> Result, Error> { let req = parse_json_body::(req).await?; - let id_hex = hex::decode(&req.bucket_id).ok_or_bad_request("Invalid bucket id")?; - let bucket_id = Uuid::try_from(&id_hex).ok_or_bad_request("Invalid bucket id")?; + let bucket_id = parse_bucket_id(&req.bucket_id)?; let bucket = garage .bucket_helper() @@ -430,3 +425,70 @@ struct BucketKeyPermChangeRequest { access_key_id: String, permissions: ApiBucketKeyPerm, } + +pub async fn handle_global_alias_bucket( + garage: &Arc, + bucket_id: String, + alias: String, +) -> Result, Error> { + let bucket_id = parse_bucket_id(&bucket_id)?; + + garage + .bucket_helper() + .set_global_bucket_alias(bucket_id, &alias) + .await?; + + 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)?; + + garage + .bucket_helper() + .unset_global_bucket_alias(bucket_id, &alias) + .await?; + + 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)?; + + garage + .bucket_helper() + .set_local_bucket_alias(bucket_id, &access_key_id, &alias) + .await?; + + 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)?; + + garage + .bucket_helper() + .unset_local_bucket_alias(bucket_id, &access_key_id, &alias) + .await?; + + bucket_info_results(garage, bucket_id).await +} + +fn parse_bucket_id(id: &str) -> Result { + let id_hex = hex::decode(&id).ok_or_bad_request("Invalid bucket id")?; + Ok(Uuid::try_from(&id_hex).ok_or_bad_request("Invalid bucket id")?) +} diff --git a/src/api/admin/router.rs b/src/api/admin/router.rs index 2a5098bf..6961becb 100644 --- a/src/api/admin/router.rs +++ b/src/api/admin/router.rs @@ -49,6 +49,25 @@ pub enum Endpoint { // Bucket-Key Permissions BucketAllowKey, BucketDenyKey, + // Bucket aliases + GlobalAliasBucket { + id: String, + alias: String, + }, + GlobalUnaliasBucket { + id: String, + alias: String, + }, + LocalAliasBucket { + id: String, + access_key_id: String, + alias: String, + }, + LocalUnaliasBucket { + id: String, + access_key_id: String, + alias: String, + }, }} impl Endpoint { @@ -87,6 +106,11 @@ impl Endpoint { // Bucket-key permissions POST "/bucket/allow" => BucketAllowKey, POST "/bucket/deny" => BucketDenyKey, + // Bucket aliases + PUT "/bucket/alias/global" => GlobalAliasBucket (query::id, query::alias), + DELETE "/bucket/alias/global" => GlobalUnaliasBucket (query::id, query::alias), + PUT "/bucket/alias/local" => LocalAliasBucket (query::id, query::access_key_id, query::alias), + DELETE "/bucket/alias/local" => LocalUnaliasBucket (query::id, query::access_key_id, query::alias), ]); if let Some(message) = query.nonempty_message() { @@ -107,5 +131,7 @@ impl Endpoint { generateQueryParameters! { "id" => id, "search" => search, - "globalAlias" => global_alias + "globalAlias" => global_alias, + "alias" => alias, + "accessKeyId" => access_key_id }