forked from Deuxfleurs/garage
Key management admin commands
This commit is contained in:
parent
4ef84a0558
commit
51fb3799a1
5 changed files with 293 additions and 54 deletions
210
src/admin_rpc.rs
210
src/admin_rpc.rs
|
@ -12,6 +12,7 @@ use crate::rpc::rpc_client::*;
|
||||||
use crate::rpc::rpc_server::*;
|
use crate::rpc::rpc_server::*;
|
||||||
|
|
||||||
use crate::store::bucket_table::*;
|
use crate::store::bucket_table::*;
|
||||||
|
use crate::store::key_table::*;
|
||||||
use crate::store::repair::Repair;
|
use crate::store::repair::Repair;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
@ -29,6 +30,8 @@ pub enum AdminRPC {
|
||||||
Ok(String),
|
Ok(String),
|
||||||
BucketList(Vec<String>),
|
BucketList(Vec<String>),
|
||||||
BucketInfo(Bucket),
|
BucketInfo(Bucket),
|
||||||
|
KeyList(Vec<(String, String)>),
|
||||||
|
KeyInfo(Key),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RpcMessage for AdminRPC {}
|
impl RpcMessage for AdminRPC {}
|
||||||
|
@ -72,19 +75,8 @@ impl AdminRpcHandler {
|
||||||
Ok(AdminRPC::BucketList(bucket_names))
|
Ok(AdminRPC::BucketList(bucket_names))
|
||||||
}
|
}
|
||||||
BucketOperation::Info(query) => {
|
BucketOperation::Info(query) => {
|
||||||
let bucket = self
|
let bucket = self.get_existing_bucket(&query.name).await?;
|
||||||
.garage
|
Ok(AdminRPC::BucketInfo(bucket))
|
||||||
.bucket_table
|
|
||||||
.get(&EmptyKey, &query.name)
|
|
||||||
.await?
|
|
||||||
.filter(|b| !b.deleted);
|
|
||||||
match bucket {
|
|
||||||
Some(b) => Ok(AdminRPC::BucketInfo(b)),
|
|
||||||
None => Err(Error::BadRequest(format!(
|
|
||||||
"Bucket {} not found",
|
|
||||||
query.name
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BucketOperation::Create(query) => {
|
BucketOperation::Create(query) => {
|
||||||
let bucket = self.garage.bucket_table.get(&EmptyKey, &query.name).await?;
|
let bucket = self.garage.bucket_table.get(&EmptyKey, &query.name).await?;
|
||||||
|
@ -105,21 +97,7 @@ impl AdminRpcHandler {
|
||||||
Ok(AdminRPC::Ok(format!("Bucket {} was created.", query.name)))
|
Ok(AdminRPC::Ok(format!("Bucket {} was created.", query.name)))
|
||||||
}
|
}
|
||||||
BucketOperation::Delete(query) => {
|
BucketOperation::Delete(query) => {
|
||||||
let bucket = match self
|
let bucket = self.get_existing_bucket(&query.name).await?;
|
||||||
.garage
|
|
||||||
.bucket_table
|
|
||||||
.get(&EmptyKey, &query.name)
|
|
||||||
.await?
|
|
||||||
.filter(|b| !b.deleted)
|
|
||||||
{
|
|
||||||
None => {
|
|
||||||
return Err(Error::BadRequest(format!(
|
|
||||||
"Bucket {} does not exist",
|
|
||||||
query.name
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
Some(b) => b,
|
|
||||||
};
|
|
||||||
let objects = self
|
let objects = self
|
||||||
.garage
|
.garage
|
||||||
.object_table
|
.object_table
|
||||||
|
@ -136,6 +114,17 @@ impl AdminRpcHandler {
|
||||||
"Add --yes flag to really perform this operation"
|
"Add --yes flag to really perform this operation"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
// --- done checking, now commit ---
|
||||||
|
for ak in bucket.authorized_keys() {
|
||||||
|
if let Some(key) = self.garage.key_table.get(&EmptyKey, &ak.key_id).await? {
|
||||||
|
if !key.deleted {
|
||||||
|
self.update_key_bucket(key, &bucket.name, false, false)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::Message(format!("Key not found: {}", ak.key_id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
self.garage
|
self.garage
|
||||||
.bucket_table
|
.bucket_table
|
||||||
.insert(&Bucket::new(
|
.insert(&Bucket::new(
|
||||||
|
@ -147,15 +136,172 @@ impl AdminRpcHandler {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(AdminRPC::Ok(format!("Bucket {} was deleted.", query.name)))
|
Ok(AdminRPC::Ok(format!("Bucket {} was deleted.", query.name)))
|
||||||
}
|
}
|
||||||
_ => {
|
BucketOperation::Allow(query) => {
|
||||||
// TODO
|
let key = self.get_existing_key(&query.key_id).await?;
|
||||||
Err(Error::Message(format!("Not implemented")))
|
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)
|
||||||
|
.await?;
|
||||||
|
self.update_bucket_key(bucket, &query.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
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
BucketOperation::Deny(query) => {
|
||||||
|
let key = self.get_existing_key(&query.key_id).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)
|
||||||
|
.await?;
|
||||||
|
self.update_bucket_key(bucket, &query.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
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_key_cmd(&self, cmd: KeyOperation) -> Result<AdminRPC, Error> {
|
async fn handle_key_cmd(&self, cmd: KeyOperation) -> Result<AdminRPC, Error> {
|
||||||
Err(Error::Message(format!("Not implemented")))
|
match cmd {
|
||||||
|
KeyOperation::List => {
|
||||||
|
let key_ids = self
|
||||||
|
.garage
|
||||||
|
.key_table
|
||||||
|
.get_range(&EmptyKey, None, Some(()), 10000)
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.map(|k| (k.key_id.to_string(), k.name.to_string()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok(AdminRPC::KeyList(key_ids))
|
||||||
|
}
|
||||||
|
KeyOperation::Info(query) => {
|
||||||
|
let key = self.get_existing_key(&query.key_id).await?;
|
||||||
|
Ok(AdminRPC::KeyInfo(key))
|
||||||
|
}
|
||||||
|
KeyOperation::New(query) => {
|
||||||
|
let key = Key::new(query.name, vec![]);
|
||||||
|
self.garage.key_table.insert(&key).await?;
|
||||||
|
Ok(AdminRPC::KeyInfo(key))
|
||||||
|
}
|
||||||
|
KeyOperation::Rename(query) => {
|
||||||
|
let mut key = self.get_existing_key(&query.key_id).await?;
|
||||||
|
key.name_timestamp = std::cmp::max(key.name_timestamp + 1, now_msec());
|
||||||
|
key.name = 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?;
|
||||||
|
if !query.yes {
|
||||||
|
return Err(Error::BadRequest(format!(
|
||||||
|
"Add --yes flag to really perform this operation"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
// --- done checking, now commit ---
|
||||||
|
for ab in key.authorized_buckets().iter() {
|
||||||
|
if let Some(bucket) =
|
||||||
|
self.garage.bucket_table.get(&EmptyKey, &ab.bucket).await?
|
||||||
|
{
|
||||||
|
if !bucket.deleted {
|
||||||
|
self.update_bucket_key(bucket, &key.key_id, false, false)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(Error::Message(format!("Bucket not found: {}", ab.bucket)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let del_key = Key::delete(key.key_id);
|
||||||
|
self.garage.key_table.insert(&del_key).await?;
|
||||||
|
Ok(AdminRPC::Ok(format!(
|
||||||
|
"Key {} was deleted successfully.",
|
||||||
|
query.key_id
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_existing_bucket(&self, bucket: &String) -> Result<Bucket, Error> {
|
||||||
|
self.garage
|
||||||
|
.bucket_table
|
||||||
|
.get(&EmptyKey, bucket)
|
||||||
|
.await?
|
||||||
|
.filter(|b| !b.deleted)
|
||||||
|
.map(Ok)
|
||||||
|
.unwrap_or(Err(Error::BadRequest(format!(
|
||||||
|
"Bucket {} does not exist",
|
||||||
|
bucket
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_existing_key(&self, id: &String) -> Result<Key, Error> {
|
||||||
|
self.garage
|
||||||
|
.key_table
|
||||||
|
.get(&EmptyKey, id)
|
||||||
|
.await?
|
||||||
|
.filter(|k| !k.deleted)
|
||||||
|
.map(Ok)
|
||||||
|
.unwrap_or(Err(Error::BadRequest(format!("Key {} does not exist", id))))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_bucket_key(
|
||||||
|
&self,
|
||||||
|
mut bucket: Bucket,
|
||||||
|
key_id: &String,
|
||||||
|
allow_read: bool,
|
||||||
|
allow_write: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let timestamp = match bucket
|
||||||
|
.authorized_keys()
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.key_id == *key_id)
|
||||||
|
{
|
||||||
|
None => now_msec(),
|
||||||
|
Some(ab) => std::cmp::max(ab.timestamp + 1, now_msec()),
|
||||||
|
};
|
||||||
|
bucket.clear_keys();
|
||||||
|
bucket
|
||||||
|
.add_key(AllowedKey {
|
||||||
|
key_id: key_id.clone(),
|
||||||
|
timestamp,
|
||||||
|
allow_read,
|
||||||
|
allow_write,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
self.garage.bucket_table.insert(&bucket).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_key_bucket(
|
||||||
|
&self,
|
||||||
|
mut key: Key,
|
||||||
|
bucket: &String,
|
||||||
|
allow_read: bool,
|
||||||
|
allow_write: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let timestamp = match key
|
||||||
|
.authorized_buckets()
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.bucket == *bucket)
|
||||||
|
{
|
||||||
|
None => now_msec(),
|
||||||
|
Some(ab) => std::cmp::max(ab.timestamp + 1, now_msec()),
|
||||||
|
};
|
||||||
|
key.clear_buckets();
|
||||||
|
key.add_bucket(AllowedBucket {
|
||||||
|
bucket: bucket.clone(),
|
||||||
|
timestamp,
|
||||||
|
allow_read,
|
||||||
|
allow_write,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
self.garage.key_table.insert(&key).await?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_launch_repair(self: &Arc<Self>, opt: RepairOpt) -> Result<AdminRPC, Error> {
|
async fn handle_launch_repair(self: &Arc<Self>, opt: RepairOpt) -> Result<AdminRPC, Error> {
|
||||||
|
|
51
src/main.rs
51
src/main.rs
|
@ -172,7 +172,7 @@ pub struct DeleteBucketOpt {
|
||||||
pub struct PermBucketOpt {
|
pub struct PermBucketOpt {
|
||||||
/// Access key ID
|
/// Access key ID
|
||||||
#[structopt(long = "key")]
|
#[structopt(long = "key")]
|
||||||
pub key: String,
|
pub key_id: String,
|
||||||
|
|
||||||
/// Allow/deny read operations
|
/// Allow/deny read operations
|
||||||
#[structopt(long = "read")]
|
#[structopt(long = "read")]
|
||||||
|
@ -192,19 +192,53 @@ pub enum KeyOperation {
|
||||||
#[structopt(name = "list")]
|
#[structopt(name = "list")]
|
||||||
List,
|
List,
|
||||||
|
|
||||||
|
/// Get key info
|
||||||
|
#[structopt(name = "info")]
|
||||||
|
Info(KeyOpt),
|
||||||
|
|
||||||
/// Create new key
|
/// Create new key
|
||||||
#[structopt(name = "new")]
|
#[structopt(name = "new")]
|
||||||
New,
|
New(KeyNewOpt),
|
||||||
|
|
||||||
|
/// Rename key
|
||||||
|
#[structopt(name = "rename")]
|
||||||
|
Rename(KeyRenameOpt),
|
||||||
|
|
||||||
/// Delete key
|
/// Delete key
|
||||||
#[structopt(name = "delete")]
|
#[structopt(name = "delete")]
|
||||||
Delete(KeyDeleteOpt),
|
Delete(KeyDeleteOpt),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, StructOpt, Debug)]
|
||||||
|
pub struct KeyOpt {
|
||||||
|
/// ID of the key
|
||||||
|
key_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, StructOpt, Debug)]
|
||||||
|
pub struct KeyNewOpt {
|
||||||
|
/// Name of the key
|
||||||
|
#[structopt(long = "name", default_value = "Unnamed key")]
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, StructOpt, Debug)]
|
||||||
|
pub struct KeyRenameOpt {
|
||||||
|
/// ID of the key
|
||||||
|
key_id: String,
|
||||||
|
|
||||||
|
/// New name of the key
|
||||||
|
new_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, StructOpt, Debug)]
|
#[derive(Serialize, Deserialize, StructOpt, Debug)]
|
||||||
pub struct KeyDeleteOpt {
|
pub struct KeyDeleteOpt {
|
||||||
/// Name of the bucket to delete
|
/// ID of the key
|
||||||
bucket: String,
|
key_id: String,
|
||||||
|
|
||||||
|
/// Confirm deletion
|
||||||
|
#[structopt(long = "yes")]
|
||||||
|
yes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, StructOpt, Debug, Clone)]
|
#[derive(Serialize, Deserialize, StructOpt, Debug, Clone)]
|
||||||
|
@ -489,6 +523,15 @@ async fn cmd_admin(
|
||||||
AdminRPC::BucketInfo(bucket) => {
|
AdminRPC::BucketInfo(bucket) => {
|
||||||
println!("{:?}", bucket);
|
println!("{:?}", bucket);
|
||||||
}
|
}
|
||||||
|
AdminRPC::KeyList(kl) => {
|
||||||
|
println!("List of keys:");
|
||||||
|
for key in kl {
|
||||||
|
println!("{}\t{}", key.0, key.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AdminRPC::KeyInfo(key) => {
|
||||||
|
println!("{:?}", key);
|
||||||
|
}
|
||||||
r => {
|
r => {
|
||||||
error!("Unexpected response: {:?}", r);
|
error!("Unexpected response: {:?}", r);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ use crate::table::*;
|
||||||
use crate::store::block::*;
|
use crate::store::block::*;
|
||||||
use crate::store::block_ref_table::*;
|
use crate::store::block_ref_table::*;
|
||||||
use crate::store::bucket_table::*;
|
use crate::store::bucket_table::*;
|
||||||
|
use crate::store::key_table::*;
|
||||||
use crate::store::object_table::*;
|
use crate::store::object_table::*;
|
||||||
use crate::store::version_table::*;
|
use crate::store::version_table::*;
|
||||||
|
|
||||||
|
@ -35,6 +36,8 @@ pub struct Garage {
|
||||||
pub block_manager: Arc<BlockManager>,
|
pub block_manager: Arc<BlockManager>,
|
||||||
|
|
||||||
pub bucket_table: Arc<Table<BucketTable, TableFullReplication>>,
|
pub bucket_table: Arc<Table<BucketTable, TableFullReplication>>,
|
||||||
|
pub key_table: Arc<Table<KeyTable, TableFullReplication>>,
|
||||||
|
|
||||||
pub object_table: Arc<Table<ObjectTable, TableShardedReplication>>,
|
pub object_table: Arc<Table<ObjectTable, TableShardedReplication>>,
|
||||||
pub version_table: Arc<Table<VersionTable, TableShardedReplication>>,
|
pub version_table: Arc<Table<VersionTable, TableShardedReplication>>,
|
||||||
pub block_ref_table: Arc<Table<BlockRefTable, TableShardedReplication>>,
|
pub block_ref_table: Arc<Table<BlockRefTable, TableShardedReplication>>,
|
||||||
|
@ -138,6 +141,17 @@ impl Garage {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
info!("Initialize key_table_table...");
|
||||||
|
let key_table = Table::new(
|
||||||
|
KeyTable,
|
||||||
|
control_rep_param.clone(),
|
||||||
|
system.clone(),
|
||||||
|
&db,
|
||||||
|
"key".to_string(),
|
||||||
|
rpc_server,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
info!("Initialize Garage...");
|
info!("Initialize Garage...");
|
||||||
let garage = Arc::new(Self {
|
let garage = Arc::new(Self {
|
||||||
config,
|
config,
|
||||||
|
@ -146,6 +160,7 @@ impl Garage {
|
||||||
block_manager,
|
block_manager,
|
||||||
background,
|
background,
|
||||||
bucket_table,
|
bucket_table,
|
||||||
|
key_table,
|
||||||
object_table,
|
object_table,
|
||||||
version_table,
|
version_table,
|
||||||
block_ref_table,
|
block_ref_table,
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl Bucket {
|
||||||
pub fn add_key(&mut self, key: AllowedKey) -> Result<(), ()> {
|
pub fn add_key(&mut self, key: AllowedKey) -> Result<(), ()> {
|
||||||
match self
|
match self
|
||||||
.authorized_keys
|
.authorized_keys
|
||||||
.binary_search_by(|k| k.access_key_id.cmp(&key.access_key_id))
|
.binary_search_by(|k| k.key_id.cmp(&key.key_id))
|
||||||
{
|
{
|
||||||
Err(i) => {
|
Err(i) => {
|
||||||
self.authorized_keys.insert(i, key);
|
self.authorized_keys.insert(i, key);
|
||||||
|
@ -53,14 +53,17 @@ impl Bucket {
|
||||||
pub fn authorized_keys(&self) -> &[AllowedKey] {
|
pub fn authorized_keys(&self) -> &[AllowedKey] {
|
||||||
&self.authorized_keys[..]
|
&self.authorized_keys[..]
|
||||||
}
|
}
|
||||||
|
pub fn clear_keys(&mut self) {
|
||||||
|
self.authorized_keys.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct AllowedKey {
|
pub struct AllowedKey {
|
||||||
pub access_key_id: String,
|
pub key_id: String,
|
||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
pub allowed_read: bool,
|
pub allow_read: bool,
|
||||||
pub allowed_write: bool,
|
pub allow_write: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry<EmptyKey, String> for Bucket {
|
impl Entry<EmptyKey, String> for Bucket {
|
||||||
|
@ -83,7 +86,7 @@ impl Entry<EmptyKey, String> for Bucket {
|
||||||
for ak in other.authorized_keys.iter() {
|
for ak in other.authorized_keys.iter() {
|
||||||
match self
|
match self
|
||||||
.authorized_keys
|
.authorized_keys
|
||||||
.binary_search_by(|our_ak| our_ak.access_key_id.cmp(&ak.access_key_id))
|
.binary_search_by(|our_ak| our_ak.key_id.cmp(&ak.key_id))
|
||||||
{
|
{
|
||||||
Ok(i) => {
|
Ok(i) => {
|
||||||
let our_ak = &mut self.authorized_keys[i];
|
let our_ak = &mut self.authorized_keys[i];
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::data::*;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::table::*;
|
use crate::table::*;
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Key {
|
pub struct Key {
|
||||||
// Primary key
|
// Primary key
|
||||||
pub access_key_id: String,
|
pub key_id: String,
|
||||||
|
|
||||||
// Associated secret key (immutable)
|
// Associated secret key (immutable)
|
||||||
pub secret_access_key: String,
|
pub secret_key: String,
|
||||||
|
|
||||||
|
// Name
|
||||||
|
pub name: String,
|
||||||
|
pub name_timestamp: u64,
|
||||||
|
|
||||||
// Deletion
|
// Deletion
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
|
@ -20,12 +25,14 @@ pub struct Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Key {
|
impl Key {
|
||||||
pub fn new(buckets: Vec<AllowedBucket>) -> Self {
|
pub fn new(name: String, buckets: Vec<AllowedBucket>) -> Self {
|
||||||
let access_key_id = format!("GK{}", hex::encode(&rand::random::<[u8; 12]>()[..]));
|
let key_id = format!("GK{}", hex::encode(&rand::random::<[u8; 12]>()[..]));
|
||||||
let secret_access_key = hex::encode(&rand::random::<[u8; 32]>()[..]);
|
let secret_key = hex::encode(&rand::random::<[u8; 32]>()[..]);
|
||||||
let mut ret = Self {
|
let mut ret = Self {
|
||||||
access_key_id,
|
key_id,
|
||||||
secret_access_key,
|
secret_key,
|
||||||
|
name,
|
||||||
|
name_timestamp: now_msec(),
|
||||||
deleted: false,
|
deleted: false,
|
||||||
authorized_buckets: vec![],
|
authorized_buckets: vec![],
|
||||||
};
|
};
|
||||||
|
@ -35,10 +42,12 @@ impl Key {
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
pub fn delete(access_key_id: String, secret_access_key: String) -> Self {
|
pub fn delete(key_id: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
access_key_id,
|
key_id,
|
||||||
secret_access_key,
|
secret_key: "".into(),
|
||||||
|
name: "".into(),
|
||||||
|
name_timestamp: now_msec(),
|
||||||
deleted: true,
|
deleted: true,
|
||||||
authorized_buckets: vec![],
|
authorized_buckets: vec![],
|
||||||
}
|
}
|
||||||
|
@ -59,14 +68,31 @@ impl Key {
|
||||||
pub fn authorized_buckets(&self) -> &[AllowedBucket] {
|
pub fn authorized_buckets(&self) -> &[AllowedBucket] {
|
||||||
&self.authorized_buckets[..]
|
&self.authorized_buckets[..]
|
||||||
}
|
}
|
||||||
|
pub fn clear_buckets(&mut self) {
|
||||||
|
self.authorized_buckets.clear();
|
||||||
|
}
|
||||||
|
pub fn allow_read(&self, bucket: &str) -> bool {
|
||||||
|
self.authorized_buckets
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.bucket.as_str() == bucket)
|
||||||
|
.map(|x| x.allow_read)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
pub fn allow_write(&self, bucket: &str) -> bool {
|
||||||
|
self.authorized_buckets
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.bucket.as_str() == bucket)
|
||||||
|
.map(|x| x.allow_write)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct AllowedBucket {
|
pub struct AllowedBucket {
|
||||||
pub bucket: String,
|
pub bucket: String,
|
||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
pub allowed_read: bool,
|
pub allow_read: bool,
|
||||||
pub allowed_write: bool,
|
pub allow_write: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entry<EmptyKey, String> for Key {
|
impl Entry<EmptyKey, String> for Key {
|
||||||
|
@ -74,15 +100,21 @@ impl Entry<EmptyKey, String> for Key {
|
||||||
&EmptyKey
|
&EmptyKey
|
||||||
}
|
}
|
||||||
fn sort_key(&self) -> &String {
|
fn sort_key(&self) -> &String {
|
||||||
&self.access_key_id
|
&self.key_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge(&mut self, other: &Self) {
|
fn merge(&mut self, other: &Self) {
|
||||||
if other.deleted {
|
if other.deleted {
|
||||||
self.deleted = true;
|
self.deleted = true;
|
||||||
|
}
|
||||||
|
if self.deleted {
|
||||||
self.authorized_buckets.clear();
|
self.authorized_buckets.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if other.name_timestamp > self.name_timestamp {
|
||||||
|
self.name_timestamp = other.name_timestamp;
|
||||||
|
self.name = other.name.clone();
|
||||||
|
}
|
||||||
|
|
||||||
for ab in other.authorized_buckets.iter() {
|
for ab in other.authorized_buckets.iter() {
|
||||||
match self
|
match self
|
||||||
|
|
Loading…
Reference in a new issue