aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-05-12 11:19:41 +0200
committerAlex Auvolat <alex@adnab.me>2022-05-12 11:19:41 +0200
commitfc2f73ddb5ecaca250daa7b034fe59fb8c47f570 (patch)
treef4287c3a90c32e2c9ea69c14085273ec32544749
parentfe399a326506a9d8870cb7783a57495849793d2c (diff)
downloadgarage-fc2f73ddb5ecaca250daa7b034fe59fb8c47f570.tar.gz
garage-fc2f73ddb5ecaca250daa7b034fe59fb8c47f570.zip
BucketAllowKey and BucketDenyKey
-rw-r--r--doc/drafts/admin-api.md45
-rw-r--r--src/api/admin/api_server.rs3
-rw-r--r--src/api/admin/bucket.rs79
-rw-r--r--src/api/admin/key.rs11
-rw-r--r--src/api/admin/router.rs6
5 files changed, 137 insertions, 7 deletions
diff --git a/doc/drafts/admin-api.md b/doc/drafts/admin-api.md
index 048b77fb..5dc3f127 100644
--- a/doc/drafts/admin-api.md
+++ b/doc/drafts/admin-api.md
@@ -456,3 +456,48 @@ or no alias at all.
Deletes a storage bucket. A bucket cannot be deleted if it is not empty.
Warning: this will delete all aliases associated with the bucket!
+
+
+## Operations on permissions for keys on buckets
+
+### BucketAllowKey `POST /bucket/allow`
+
+Allows a key to do read/write/owner operations on a bucket.
+
+Request body format:
+
+```json
+{
+ "bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
+ "accessKeyId": "GK31c2f218a2e44f485b94239e",
+ "permissions": {
+ "read": true,
+ "write": true,
+ "owner": true
+ },
+}
+```
+
+Flags in `permissions` which have the value `true` will be activated.
+Other flags will remain unchanged.
+
+### BucketDenyKey `POST /bucket/deny`
+
+Denies a key from doing read/write/owner operations on a bucket.
+
+Request body format:
+
+```json
+{
+ "bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
+ "accessKeyId": "GK31c2f218a2e44f485b94239e",
+ "permissions": {
+ "read": false,
+ "write": false,
+ "owner": true
+ },
+}
+```
+
+Flags in `permissions` which have the value `true` will be deactivated.
+Other flags will remain unchanged.
diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs
index 4366cbd6..6bdef56c 100644
--- a/src/api/admin/api_server.rs
+++ b/src/api/admin/api_server.rs
@@ -147,6 +147,9 @@ impl ApiHandler for AdminApiServer {
}
Endpoint::CreateBucket => handle_create_bucket(&self.garage, req).await,
Endpoint::DeleteBucket { id } => handle_delete_bucket(&self.garage, id).await,
+ // Bucket-key permissions
+ Endpoint::BucketAllowKey => handle_bucket_allow_key(&self.garage, req).await,
+ Endpoint::BucketDenyKey => handle_bucket_deny_key(&self.garage, req).await,
_ => Err(Error::NotImplemented(format!(
"Admin endpoint {} not implemented yet",
endpoint.name()
diff --git a/src/api/admin/bucket.rs b/src/api/admin/bucket.rs
index 8e6cc067..16e9c174 100644
--- a/src/api/admin/bucket.rs
+++ b/src/api/admin/bucket.rs
@@ -16,7 +16,7 @@ use garage_model::garage::Garage;
use garage_model::permission::*;
use garage_model::s3::object_table::ObjectFilter;
-use crate::admin::key::KeyBucketPermResult;
+use crate::admin::key::ApiBucketKeyPerm;
use crate::error::*;
use crate::helpers::*;
@@ -174,7 +174,7 @@ async fn bucket_info_results(
permissions: p
.authorized_buckets
.get(&bucket.id)
- .map(|p| KeyBucketPermResult {
+ .map(|p| ApiBucketKeyPerm {
read: p.allow_read,
write: p.allow_write,
owner: p.allow_owner,
@@ -212,7 +212,7 @@ struct GetBucketInfoKey {
access_key_id: String,
#[serde(rename = "name")]
name: String,
- permissions: KeyBucketPermResult,
+ permissions: ApiBucketKeyPerm,
#[serde(rename = "bucketLocalAliases")]
bucket_local_aliases: Vec<String>,
}
@@ -368,3 +368,76 @@ pub async fn handle_delete_bucket(
.status(StatusCode::NO_CONTENT)
.body(Body::empty())?)
}
+
+pub async fn handle_bucket_allow_key(
+ garage: &Arc<Garage>,
+ req: Request<Body>,
+) -> Result<Response<Body>, Error> {
+ handle_bucket_change_key_perm(garage, req, true).await
+}
+
+pub async fn handle_bucket_deny_key(
+ garage: &Arc<Garage>,
+ req: Request<Body>,
+) -> Result<Response<Body>, Error> {
+ handle_bucket_change_key_perm(garage, req, false).await
+}
+
+pub async fn handle_bucket_change_key_perm(
+ garage: &Arc<Garage>,
+ req: Request<Body>,
+ new_perm_flag: bool,
+) -> Result<Response<Body>, Error> {
+ let req = parse_json_body::<BucketKeyPermChangeRequest>(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 = garage
+ .bucket_helper()
+ .get_existing_bucket(bucket_id)
+ .await?;
+ let state = bucket.state.as_option().unwrap();
+
+ let key = garage
+ .key_helper()
+ .get_existing_key(&req.access_key_id)
+ .await?;
+
+ let mut perm = state
+ .authorized_keys
+ .get(&key.key_id)
+ .cloned()
+ .unwrap_or(BucketKeyPerm::NO_PERMISSIONS);
+
+ if req.permissions.read {
+ perm.allow_read = new_perm_flag;
+ }
+ if req.permissions.write {
+ perm.allow_write = new_perm_flag;
+ }
+ if req.permissions.owner {
+ perm.allow_owner = new_perm_flag;
+ }
+
+ garage
+ .bucket_helper()
+ .set_bucket_key_permissions(bucket.id, &key.key_id, perm)
+ .await?;
+
+ let bucket = garage
+ .bucket_table
+ .get(&EmptyKey, &bucket.id)
+ .await?
+ .ok_or_internal_error("Bucket should now exist but doesn't")?;
+ bucket_info_results(garage, bucket).await
+}
+
+#[derive(Deserialize)]
+struct BucketKeyPermChangeRequest {
+ #[serde(rename = "bucketId")]
+ bucket_id: String,
+ #[serde(rename = "accessKeyId")]
+ access_key_id: String,
+ permissions: ApiBucketKeyPerm,
+}
diff --git a/src/api/admin/key.rs b/src/api/admin/key.rs
index 1252d2c8..19ad5160 100644
--- a/src/api/admin/key.rs
+++ b/src/api/admin/key.rs
@@ -198,7 +198,7 @@ async fn key_info_results(garage: &Arc<Garage>, key: Key) -> Result<Response<Bod
permissions: key_state
.authorized_buckets
.get(&bucket.id)
- .map(|p| KeyBucketPermResult {
+ .map(|p| ApiBucketKeyPerm {
read: p.allow_read,
write: p.allow_write,
owner: p.allow_owner,
@@ -239,12 +239,15 @@ struct KeyInfoBucketResult {
global_aliases: Vec<String>,
#[serde(rename = "localAliases")]
local_aliases: Vec<String>,
- permissions: KeyBucketPermResult,
+ permissions: ApiBucketKeyPerm,
}
-#[derive(Serialize, Default)]
-pub(crate) struct KeyBucketPermResult {
+#[derive(Serialize, Deserialize, Default)]
+pub(crate) struct ApiBucketKeyPerm {
+ #[serde(default)]
pub(crate) read: bool,
+ #[serde(default)]
pub(crate) write: bool,
+ #[serde(default)]
pub(crate) owner: bool,
}
diff --git a/src/api/admin/router.rs b/src/api/admin/router.rs
index a6e1c848..6f787fe9 100644
--- a/src/api/admin/router.rs
+++ b/src/api/admin/router.rs
@@ -46,6 +46,9 @@ pub enum Endpoint {
DeleteBucket {
id: String,
},
+ // Bucket-Key Permissions
+ BucketAllowKey,
+ BucketDenyKey,
}}
impl Endpoint {
@@ -81,6 +84,9 @@ impl Endpoint {
GET "/bucket" => ListBuckets,
POST "/bucket" => CreateBucket,
DELETE "/bucket" if id => DeleteBucket (query::id),
+ // Bucket-key permissions
+ POST "/bucket/allow" => BucketAllowKey,
+ POST "/bucket/deny" => BucketDenyKey,
]);
if let Some(message) = query.nonempty_message() {