forked from Deuxfleurs/garage
Allow manipulation of keys by their shorthand in the CLI
This commit is contained in:
parent
537f652fec
commit
5ee1d956b6
3 changed files with 48 additions and 31 deletions
|
@ -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(),
|
||||||
|
|
|
@ -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")]
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue