aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2021-03-15 19:14:26 +0100
committerAlex Auvolat <alex@adnab.me>2021-03-15 19:14:26 +0100
commit5ee1d956b6c7dd847a304ef524253b2e067e1245 (patch)
tree0dcaf522847fae7e105950c3f25886f5f5afa91c
parent537f652fec479c7c5676bba14c23ea6634613122 (diff)
downloadgarage-5ee1d956b6c7dd847a304ef524253b2e067e1245.tar.gz
garage-5ee1d956b6c7dd847a304ef524253b2e067e1245.zip
Allow manipulation of keys by their shorthand in the CLI
-rw-r--r--src/garage/admin_rpc.rs48
-rw-r--r--src/garage/cli.rs16
-rw-r--r--src/model/key_table.rs15
3 files changed, 48 insertions, 31 deletions
diff --git a/src/garage/admin_rpc.rs b/src/garage/admin_rpc.rs
index 512e00dd..07c1b582 100644
--- a/src/garage/admin_rpc.rs
+++ b/src/garage/admin_rpc.rs
@@ -122,7 +122,7 @@ impl AdminRpcHandler {
for (key_id, _, _) in bucket.authorized_keys() {
if let Some(key) = self.garage.key_table.get(&EmptyKey, key_id).await? {
if !key.deleted.get() {
- self.update_key_bucket(key, &bucket.name, false, false)
+ self.update_key_bucket(&key, &bucket.name, false, false)
.await?;
}
} else {
@@ -134,31 +134,31 @@ impl AdminRpcHandler {
Ok(AdminRPC::Ok(format!("Bucket {} was deleted.", query.name)))
}
BucketOperation::Allow(query) => {
- let key = self.get_existing_key(&query.key_id).await?;
+ let key = self.get_existing_key(&query.key_pattern).await?;
let bucket = self.get_existing_bucket(&query.bucket).await?;
let allow_read = query.read || key.allow_read(&query.bucket);
let allow_write = query.write || key.allow_write(&query.bucket);
- self.update_key_bucket(key, &query.bucket, allow_read, allow_write)
+ self.update_key_bucket(&key, &query.bucket, allow_read, allow_write)
.await?;
- self.update_bucket_key(bucket, &query.key_id, allow_read, allow_write)
+ self.update_bucket_key(bucket, &key.key_id, allow_read, allow_write)
.await?;
Ok(AdminRPC::Ok(format!(
"New permissions for {} on {}: read {}, write {}.",
- &query.key_id, &query.bucket, allow_read, allow_write
+ &key.key_id, &query.bucket, allow_read, allow_write
)))
}
BucketOperation::Deny(query) => {
- let key = self.get_existing_key(&query.key_id).await?;
+ let key = self.get_existing_key(&query.key_pattern).await?;
let bucket = self.get_existing_bucket(&query.bucket).await?;
let allow_read = !query.read && key.allow_read(&query.bucket);
let allow_write = !query.write && key.allow_write(&query.bucket);
- self.update_key_bucket(key, &query.bucket, allow_read, allow_write)
+ self.update_key_bucket(&key, &query.bucket, allow_read, allow_write)
.await?;
- self.update_bucket_key(bucket, &query.key_id, allow_read, allow_write)
+ self.update_bucket_key(bucket, &key.key_id, allow_read, allow_write)
.await?;
Ok(AdminRPC::Ok(format!(
"New permissions for {} on {}: read {}, write {}.",
- &query.key_id, &query.bucket, allow_read, allow_write
+ &key.key_id, &query.bucket, allow_read, allow_write
)))
}
BucketOperation::Website(query) => {
@@ -193,7 +193,7 @@ impl AdminRpcHandler {
let key_ids = self
.garage
.key_table
- .get_range(&EmptyKey, None, Some(DeletedFilter::NotDeleted), 10000)
+ .get_range(&EmptyKey, None, Some(KeyFilter::Deleted(DeletedFilter::NotDeleted)), 10000)
.await?
.iter()
.map(|k| (k.key_id.to_string(), k.name.get().clone()))
@@ -201,7 +201,7 @@ impl AdminRpcHandler {
Ok(AdminRPC::KeyList(key_ids))
}
KeyOperation::Info(query) => {
- let key = self.get_existing_key(&query.key_id).await?;
+ let key = self.get_existing_key(&query.key_pattern).await?;
Ok(AdminRPC::KeyInfo(key))
}
KeyOperation::New(query) => {
@@ -210,13 +210,13 @@ impl AdminRpcHandler {
Ok(AdminRPC::KeyInfo(key))
}
KeyOperation::Rename(query) => {
- let mut key = self.get_existing_key(&query.key_id).await?;
+ let mut key = self.get_existing_key(&query.key_pattern).await?;
key.name.update(query.new_name);
self.garage.key_table.insert(&key).await?;
Ok(AdminRPC::KeyInfo(key))
}
KeyOperation::Delete(query) => {
- let key = self.get_existing_key(&query.key_id).await?;
+ let key = self.get_existing_key(&query.key_pattern).await?;
if !query.yes {
return Err(Error::BadRPC(format!(
"Add --yes flag to really perform this operation"
@@ -233,11 +233,11 @@ impl AdminRpcHandler {
return Err(Error::Message(format!("Bucket not found: {}", ab_name)));
}
}
- let del_key = Key::delete(key.key_id);
+ let del_key = Key::delete(key.key_id.to_string());
self.garage.key_table.insert(&del_key).await?;
Ok(AdminRPC::Ok(format!(
"Key {} was deleted successfully.",
- query.key_id
+ key.key_id
)))
}
}
@@ -256,14 +256,19 @@ impl AdminRpcHandler {
))))
}
- async fn get_existing_key(&self, id: &String) -> Result<Key, Error> {
- self.garage
+ async fn get_existing_key(&self, pattern: &str) -> Result<Key, Error> {
+ let candidates = self.garage
.key_table
- .get(&EmptyKey, id)
+ .get_range(&EmptyKey, None, Some(KeyFilter::Matches(pattern.to_string())), 10)
.await?
+ .into_iter()
.filter(|k| !k.deleted.get())
- .map(Ok)
- .unwrap_or(Err(Error::BadRPC(format!("Key {} does not exist", id))))
+ .collect::<Vec<_>>();
+ if candidates.len() != 1 {
+ Err(Error::Message(format!("{} matching keys", candidates.len())))
+ } else {
+ Ok(candidates.into_iter().next().unwrap())
+ }
}
/// Update **bucket table** to inform of the new linked key
@@ -296,11 +301,12 @@ impl AdminRpcHandler {
/// Update **key table** to inform of the new linked bucket
async fn update_key_bucket(
&self,
- mut key: Key,
+ key: &Key,
bucket: &String,
allow_read: bool,
allow_write: bool,
) -> Result<(), Error> {
+ let mut key = key.clone();
let old_map = key.authorized_buckets.take_and_clear();
key.authorized_buckets.merge(&old_map.update_mutator(
bucket.clone(),
diff --git a/src/garage/cli.rs b/src/garage/cli.rs
index b5c91ffc..e74f59a2 100644
--- a/src/garage/cli.rs
+++ b/src/garage/cli.rs
@@ -157,9 +157,9 @@ pub struct DeleteBucketOpt {
#[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct PermBucketOpt {
- /// Access key ID
+ /// Access key name or ID
#[structopt(long = "key")]
- pub key_id: String,
+ pub key_pattern: String,
/// Allow/deny read operations
#[structopt(long = "read")]
@@ -198,8 +198,8 @@ pub enum KeyOperation {
#[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct KeyOpt {
- /// ID of the key
- pub key_id: String,
+ /// ID or name of the key
+ pub key_pattern: String,
}
#[derive(Serialize, Deserialize, StructOpt, Debug)]
@@ -211,8 +211,8 @@ pub struct KeyNewOpt {
#[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct KeyRenameOpt {
- /// ID of the key
- pub key_id: String,
+ /// ID or name of the key
+ pub key_pattern: String,
/// New name of the key
pub new_name: String,
@@ -220,8 +220,8 @@ pub struct KeyRenameOpt {
#[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct KeyDeleteOpt {
- /// ID of the key
- pub key_id: String,
+ /// ID or name of the key
+ pub key_pattern: String,
/// Confirm deletion
#[structopt(long = "yes")]
diff --git a/src/model/key_table.rs b/src/model/key_table.rs
index b4ab65b6..ce5888ce 100644
--- a/src/model/key_table.rs
+++ b/src/model/key_table.rs
@@ -92,13 +92,24 @@ impl CRDT for Key {
pub struct KeyTable;
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum KeyFilter {
+ Deleted(DeletedFilter),
+ Matches(String),
+}
+
impl TableSchema for KeyTable {
type P = EmptyKey;
type S = String;
type E = Key;
- type Filter = DeletedFilter;
+ type Filter = KeyFilter;
fn matches_filter(entry: &Self::E, filter: &Self::Filter) -> bool {
- filter.apply(entry.deleted.get())
+ match filter {
+ KeyFilter::Deleted(df) => df.apply(entry.deleted.get()),
+ KeyFilter::Matches(pat) => {
+ entry.key_id.starts_with(pat) || entry.name.get().to_lowercase() == pat.to_lowercase()
+ }
+ }
}
}