Allow manipulation of keys by their shorthand in the CLI
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Alex 2021-03-15 19:14:26 +01:00
parent 537f652fec
commit 5ee1d956b6
3 changed files with 48 additions and 31 deletions

View file

@ -122,7 +122,7 @@ impl AdminRpcHandler {
for (key_id, _, _) in bucket.authorized_keys() { for (key_id, _, _) in bucket.authorized_keys() {
if let Some(key) = self.garage.key_table.get(&EmptyKey, key_id).await? { if let Some(key) = self.garage.key_table.get(&EmptyKey, key_id).await? {
if !key.deleted.get() { if !key.deleted.get() {
self.update_key_bucket(key, &bucket.name, false, false) self.update_key_bucket(&key, &bucket.name, false, false)
.await?; .await?;
} }
} else { } else {
@ -134,31 +134,31 @@ impl AdminRpcHandler {
Ok(AdminRPC::Ok(format!("Bucket {} was deleted.", query.name))) Ok(AdminRPC::Ok(format!("Bucket {} was deleted.", query.name)))
} }
BucketOperation::Allow(query) => { 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 bucket = self.get_existing_bucket(&query.bucket).await?;
let allow_read = query.read || key.allow_read(&query.bucket); let allow_read = query.read || key.allow_read(&query.bucket);
let allow_write = query.write || key.allow_write(&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?; .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?; .await?;
Ok(AdminRPC::Ok(format!( Ok(AdminRPC::Ok(format!(
"New permissions for {} on {}: read {}, write {}.", "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) => { 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 bucket = self.get_existing_bucket(&query.bucket).await?;
let allow_read = !query.read && key.allow_read(&query.bucket); let allow_read = !query.read && key.allow_read(&query.bucket);
let allow_write = !query.write && key.allow_write(&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?; .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?; .await?;
Ok(AdminRPC::Ok(format!( Ok(AdminRPC::Ok(format!(
"New permissions for {} on {}: read {}, write {}.", "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) => { BucketOperation::Website(query) => {
@ -193,7 +193,7 @@ impl AdminRpcHandler {
let key_ids = self let key_ids = self
.garage .garage
.key_table .key_table
.get_range(&EmptyKey, None, Some(DeletedFilter::NotDeleted), 10000) .get_range(&EmptyKey, None, Some(KeyFilter::Deleted(DeletedFilter::NotDeleted)), 10000)
.await? .await?
.iter() .iter()
.map(|k| (k.key_id.to_string(), k.name.get().clone())) .map(|k| (k.key_id.to_string(), k.name.get().clone()))
@ -201,7 +201,7 @@ impl AdminRpcHandler {
Ok(AdminRPC::KeyList(key_ids)) Ok(AdminRPC::KeyList(key_ids))
} }
KeyOperation::Info(query) => { 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)) Ok(AdminRPC::KeyInfo(key))
} }
KeyOperation::New(query) => { KeyOperation::New(query) => {
@ -210,13 +210,13 @@ impl AdminRpcHandler {
Ok(AdminRPC::KeyInfo(key)) Ok(AdminRPC::KeyInfo(key))
} }
KeyOperation::Rename(query) => { 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); key.name.update(query.new_name);
self.garage.key_table.insert(&key).await?; self.garage.key_table.insert(&key).await?;
Ok(AdminRPC::KeyInfo(key)) Ok(AdminRPC::KeyInfo(key))
} }
KeyOperation::Delete(query) => { 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 { if !query.yes {
return Err(Error::BadRPC(format!( return Err(Error::BadRPC(format!(
"Add --yes flag to really perform this operation" "Add --yes flag to really perform this operation"
@ -233,11 +233,11 @@ impl AdminRpcHandler {
return Err(Error::Message(format!("Bucket not found: {}", ab_name))); 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?; self.garage.key_table.insert(&del_key).await?;
Ok(AdminRPC::Ok(format!( Ok(AdminRPC::Ok(format!(
"Key {} was deleted successfully.", "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> { async fn get_existing_key(&self, pattern: &str) -> Result<Key, Error> {
self.garage let candidates = self.garage
.key_table .key_table
.get(&EmptyKey, id) .get_range(&EmptyKey, None, Some(KeyFilter::Matches(pattern.to_string())), 10)
.await? .await?
.into_iter()
.filter(|k| !k.deleted.get()) .filter(|k| !k.deleted.get())
.map(Ok) .collect::<Vec<_>>();
.unwrap_or(Err(Error::BadRPC(format!("Key {} does not exist", id)))) 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 /// 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 /// Update **key table** to inform of the new linked bucket
async fn update_key_bucket( async fn update_key_bucket(
&self, &self,
mut key: Key, key: &Key,
bucket: &String, bucket: &String,
allow_read: bool, allow_read: bool,
allow_write: bool, allow_write: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut key = key.clone();
let old_map = key.authorized_buckets.take_and_clear(); let old_map = key.authorized_buckets.take_and_clear();
key.authorized_buckets.merge(&old_map.update_mutator( key.authorized_buckets.merge(&old_map.update_mutator(
bucket.clone(), bucket.clone(),

View file

@ -157,9 +157,9 @@ pub struct DeleteBucketOpt {
#[derive(Serialize, Deserialize, StructOpt, Debug)] #[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct PermBucketOpt { pub struct PermBucketOpt {
/// Access key ID /// Access key name or ID
#[structopt(long = "key")] #[structopt(long = "key")]
pub key_id: String, pub key_pattern: String,
/// Allow/deny read operations /// Allow/deny read operations
#[structopt(long = "read")] #[structopt(long = "read")]
@ -198,8 +198,8 @@ pub enum KeyOperation {
#[derive(Serialize, Deserialize, StructOpt, Debug)] #[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct KeyOpt { pub struct KeyOpt {
/// ID of the key /// ID or name of the key
pub key_id: String, pub key_pattern: String,
} }
#[derive(Serialize, Deserialize, StructOpt, Debug)] #[derive(Serialize, Deserialize, StructOpt, Debug)]
@ -211,8 +211,8 @@ pub struct KeyNewOpt {
#[derive(Serialize, Deserialize, StructOpt, Debug)] #[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct KeyRenameOpt { pub struct KeyRenameOpt {
/// ID of the key /// ID or name of the key
pub key_id: String, pub key_pattern: String,
/// New name of the key /// New name of the key
pub new_name: String, pub new_name: String,
@ -220,8 +220,8 @@ pub struct KeyRenameOpt {
#[derive(Serialize, Deserialize, StructOpt, Debug)] #[derive(Serialize, Deserialize, StructOpt, Debug)]
pub struct KeyDeleteOpt { pub struct KeyDeleteOpt {
/// ID of the key /// ID or name of the key
pub key_id: String, pub key_pattern: String,
/// Confirm deletion /// Confirm deletion
#[structopt(long = "yes")] #[structopt(long = "yes")]

View file

@ -92,13 +92,24 @@ impl CRDT for Key {
pub struct KeyTable; pub struct KeyTable;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum KeyFilter {
Deleted(DeletedFilter),
Matches(String),
}
impl TableSchema for KeyTable { impl TableSchema for KeyTable {
type P = EmptyKey; type P = EmptyKey;
type S = String; type S = String;
type E = Key; type E = Key;
type Filter = DeletedFilter; type Filter = KeyFilter;
fn matches_filter(entry: &Self::E, filter: &Self::Filter) -> bool { 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()
}
}
} }
} }