From 44a1089d9569b442c098c2ceebb3f691816e52d2 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Thu, 23 Apr 2020 18:16:33 +0000 Subject: Make table objects slightly more fool-proof; add key table --- src/store/bucket_table.rs | 46 +++++++++++++++-- src/store/key_table.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++ src/store/mod.rs | 1 + src/store/object_table.rs | 47 +++++++++++++++--- src/store/version_table.rs | 38 +++++++++++++- 5 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 src/store/key_table.rs (limited to 'src/store') diff --git a/src/store/bucket_table.rs b/src/store/bucket_table.rs index 5604049c..7778b8f9 100644 --- a/src/store/bucket_table.rs +++ b/src/store/bucket_table.rs @@ -15,7 +15,44 @@ pub struct Bucket { pub deleted: bool, // Authorized keys - pub authorized_keys: Vec, + authorized_keys: Vec, +} + +impl Bucket { + pub fn new( + name: String, + timestamp: u64, + deleted: bool, + authorized_keys: Vec, + ) -> Self { + let mut ret = Bucket { + name, + timestamp, + deleted, + authorized_keys: vec![], + }; + for key in authorized_keys { + ret.add_key(key) + .expect("Duplicate AllowedKey in Bucket constructor"); + } + ret + } + /// Add a key only if it is not already present + pub fn add_key(&mut self, key: AllowedKey) -> Result<(), ()> { + match self + .authorized_keys + .binary_search_by(|k| k.access_key_id.cmp(&key.access_key_id)) + { + Err(i) => { + self.authorized_keys.insert(i, key); + Ok(()) + } + Ok(_) => Err(()), + } + } + pub fn authorized_keys(&self) -> &[AllowedKey] { + &self.authorized_keys[..] + } } #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] @@ -39,9 +76,10 @@ impl Entry for Bucket { *self = other.clone(); return; } - if self.timestamp > other.timestamp { + if self.timestamp > other.timestamp || self.deleted { return; } + for ak in other.authorized_keys.iter() { match self .authorized_keys @@ -50,9 +88,7 @@ impl Entry for Bucket { Ok(i) => { let our_ak = &mut self.authorized_keys[i]; if ak.timestamp > our_ak.timestamp { - our_ak.timestamp = ak.timestamp; - our_ak.allowed_read = ak.allowed_read; - our_ak.allowed_write = ak.allowed_write; + *our_ak = ak.clone(); } } Err(i) => { diff --git a/src/store/key_table.rs b/src/store/key_table.rs new file mode 100644 index 00000000..7476622f --- /dev/null +++ b/src/store/key_table.rs @@ -0,0 +1,121 @@ +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; + +use crate::error::Error; +use crate::table::*; + +#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] +pub struct Key { + // Primary key + pub access_key_id: String, + + // Associated secret key (immutable) + pub secret_access_key: String, + + // Deletion + pub deleted: bool, + + // Authorized keys + authorized_buckets: Vec, +} + +impl Key { + pub fn new(buckets: Vec) -> Self { + let access_key_id = format!("GK{}", hex::encode(&rand::random::<[u8; 12]>()[..])); + let secret_access_key = hex::encode(&rand::random::<[u8; 32]>()[..]); + let mut ret = Self { + access_key_id, + secret_access_key, + deleted: false, + authorized_buckets: vec![], + }; + for b in buckets { + ret.add_bucket(b); + } + ret + } + pub fn delete(access_key_id: String, secret_access_key: String) -> Self { + Self { + access_key_id, + secret_access_key, + deleted: true, + authorized_buckets: vec![], + } + } + /// Add an authorized bucket, only if it wasn't there before + pub fn add_bucket(&mut self, new: AllowedBucket) -> Result<(), ()> { + match self + .authorized_buckets + .binary_search_by(|b| b.bucket.cmp(&new.bucket)) + { + Err(i) => { + self.authorized_buckets.insert(i, new); + Ok(()) + } + Ok(_) => Err(()), + } + } + pub fn authorized_buckets(&self) -> &[AllowedBucket] { + &self.authorized_buckets[..] + } +} + +#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] +pub struct AllowedBucket { + pub bucket: String, + pub timestamp: u64, + pub allowed_read: bool, + pub allowed_write: bool, +} + +impl Entry for Key { + fn partition_key(&self) -> &EmptyKey { + &EmptyKey + } + fn sort_key(&self) -> &String { + &self.access_key_id + } + + fn merge(&mut self, other: &Self) { + if other.deleted { + self.deleted = true; + self.authorized_buckets.clear(); + return; + } + + for ab in other.authorized_buckets.iter() { + match self + .authorized_buckets + .binary_search_by(|our_ab| our_ab.bucket.cmp(&ab.bucket)) + { + Ok(i) => { + let our_ab = &mut self.authorized_buckets[i]; + if ab.timestamp > our_ab.timestamp { + *our_ab = ab.clone(); + } + } + Err(i) => { + self.authorized_buckets.insert(i, ab.clone()); + } + } + } + } +} + +pub struct KeyTable; + +#[async_trait] +impl TableSchema for KeyTable { + type P = EmptyKey; + type S = String; + type E = Key; + type Filter = (); + + async fn updated(&self, _old: Option, _new: Option) -> Result<(), Error> { + Ok(()) + } + + fn matches_filter(entry: &Self::E, _filter: &Self::Filter) -> bool { + !entry.deleted + } +} diff --git a/src/store/mod.rs b/src/store/mod.rs index afadc9bb..b6a8dc46 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -1,5 +1,6 @@ pub mod block; pub mod block_ref_table; pub mod bucket_table; +pub mod key_table; pub mod object_table; pub mod version_table; diff --git a/src/store/object_table.rs b/src/store/object_table.rs index 97de0cdb..f329a7f4 100644 --- a/src/store/object_table.rs +++ b/src/store/object_table.rs @@ -20,7 +20,38 @@ pub struct Object { pub key: String, // Data - pub versions: Vec>, + versions: Vec, +} + +impl Object { + pub fn new(bucket: String, key: String, versions: Vec) -> Self { + let mut ret = Self { + bucket, + key, + versions: vec![], + }; + for v in versions { + ret.add_version(v) + .expect("Twice the same ObjectVersion in Object constructor"); + } + ret + } + /// Adds a version if it wasn't already present + pub fn add_version(&mut self, new: ObjectVersion) -> Result<(), ()> { + match self + .versions + .binary_search_by(|v| v.cmp_key().cmp(&new.cmp_key())) + { + Err(i) => { + self.versions.insert(i, new); + Ok(()) + } + Ok(_) => Err(()), + } + } + pub fn versions(&self) -> &[ObjectVersion] { + &self.versions[..] + } } #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] @@ -113,13 +144,13 @@ impl TableSchema for ObjectTable { .binary_search_by(|nv| nv.cmp_key().cmp(&v.cmp_key())) .is_err() { - let deleted_version = Version { - uuid: v.uuid, - deleted: true, - blocks: vec![], - bucket: old_v.bucket.clone(), - key: old_v.key.clone(), - }; + let deleted_version = Version::new( + v.uuid, + old_v.bucket.clone(), + old_v.key.clone(), + true, + vec![], + ); version_table.insert(&deleted_version).await?; } } diff --git a/src/store/version_table.rs b/src/store/version_table.rs index d25a56ca..6d304cda 100644 --- a/src/store/version_table.rs +++ b/src/store/version_table.rs @@ -18,7 +18,7 @@ pub struct Version { // Actual data: the blocks for this version pub deleted: bool, - pub blocks: Vec, + blocks: Vec, // Back link to bucket+key so that we can figure if // this was deleted later on @@ -26,6 +26,42 @@ pub struct Version { pub key: String, } +impl Version { + pub fn new( + uuid: UUID, + bucket: String, + key: String, + deleted: bool, + blocks: Vec, + ) -> Self { + let mut ret = Self { + uuid, + deleted, + blocks: vec![], + bucket, + key, + }; + for b in blocks { + ret.add_block(b) + .expect("Twice the same VersionBlock in Version constructor"); + } + ret + } + /// Adds a block if it wasn't already present + pub fn add_block(&mut self, new: VersionBlock) -> Result<(), ()> { + match self.blocks.binary_search_by(|b| b.offset.cmp(&new.offset)) { + Err(i) => { + self.blocks.insert(i, new); + Ok(()) + } + Ok(_) => Err(()), + } + } + pub fn blocks(&self) -> &[VersionBlock] { + &self.blocks[..] + } +} + #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub struct VersionBlock { pub offset: u64, -- cgit v1.2.3