From f91d396f69536a889509305c99ae9ab5d8d740ac Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 22 Apr 2024 22:08:50 +0200 Subject: [PATCH] + basic metadata struct + unrefactored metadata utilities --- Cargo.lock | 147 +++++++++++++++++++++++++++++++++ crates/base/Cargo.toml | 1 + crates/base/src/lib.rs | 81 +++++++++++++----- crates/storage-sled/src/lib.rs | 91 +++++++++++++++++--- 4 files changed, 287 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f652ec..3884a68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.13" @@ -176,6 +191,7 @@ dependencies = [ name = "base" version = "0.1.0" dependencies = [ + "chrono", "serde", "serde_json", ] @@ -186,6 +202,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -210,12 +232,33 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.5", +] + [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "crc32fast" version = "1.4.0" @@ -441,6 +484,29 @@ dependencies = [ "tokio", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -467,6 +533,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.153" @@ -527,6 +602,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -1027,6 +1111,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.60", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + [[package]] name = "winapi" version = "0.3.9" @@ -1049,6 +1187,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/crates/base/Cargo.toml b/crates/base/Cargo.toml index 56e8d6d..9beaf96 100644 --- a/crates/base/Cargo.toml +++ b/crates/base/Cargo.toml @@ -7,5 +7,6 @@ edition = "2021" workspace = true [dependencies] +chrono = { version = "0.4.38", features = ["serde"] } serde = { version = "1.0.198", features = ["derive"] } serde_json = "1.0.116" diff --git a/crates/base/src/lib.rs b/crates/base/src/lib.rs index 4e7a343..b46c094 100644 --- a/crates/base/src/lib.rs +++ b/crates/base/src/lib.rs @@ -1,45 +1,86 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - #[cfg(test)] mod tests { use super::*; #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } - #[test] - fn print_serialized() { - let temp_secret = TempSecret { content: String::from("Hallo"), version: 12 }; + fn print_serialized_test() { + let temp_secret = TempSecret { + content: String::from("Hallo"), + version: 12, + }; let serialized = serialize_secret_json(&temp_secret); println!("string serialized: {:?}", serialized); let deserialized = deserialize_secret_struct(&serialized.unwrap()); - println!("Struct field from deserialized: {}", deserialized.unwrap().content) + println!( + "Struct field from deserialized: {}", + deserialized.unwrap().content + ) } } -use serde::{Serialize, Deserialize}; +pub fn create_mock_meta() -> SecretMeta { + SecretMeta { + cas_required: false, + created_time: DateTime::parse_from_rfc3339("2018-03-22T02:24:06.945319214Z") + .unwrap() + .with_timezone(&Utc), + current_version: 3, + delete_version_after: "3h25m19s".to_string(), + max_versions: 0, + oldest_version: 0, + updated_time: Utc::now(), + custom_metadata: HashMap::new(), + versions: vec![], + } +} + +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; #[derive(Serialize, Deserialize, Debug)] pub struct TempSecret { pub content: String, - pub version: i64 + pub version: i64, } -// /// serialize secret to JSON byte vector -// pub fn serialize_secret_json(secret: &TempSecret) -> Result> { -// serde_json::to_vec(&secret) -// } - /// serialize secret to JSON String pub fn serialize_secret_json(secret: &TempSecret) -> Result { serde_json::to_string(&secret) } /// deserialize JSON String to secret -pub fn deserialize_secret_struct(raw: &String) -> Result{ +pub fn deserialize_secret_struct(raw: &String) -> Result { + serde_json::from_str(&raw) +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct VersionMeta { + pub created_time: DateTime, + pub deletion_time: DateTime, + pub destroyed: bool, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SecretMeta { + pub cas_required: bool, + pub created_time: DateTime, + pub current_version: u32, + pub delete_version_after: String, + // TODO https://developer.hashicorp.com/vault/docs/concepts/duration-format + pub max_versions: u32, + pub oldest_version: u32, + pub updated_time: DateTime, + pub custom_metadata: HashMap, + pub versions: Vec, +} + +/// serialize metadata to JSON String +pub fn serialize_metadata_json(secret: &SecretMeta) -> Result { + serde_json::to_string(&secret) +} + +/// deserialize JSON String to metadata +pub fn deserialize_metadata_struct(raw: &String) -> Result { serde_json::from_str(&raw) } diff --git a/crates/storage-sled/src/lib.rs b/crates/storage-sled/src/lib.rs index 514e922..d7108ea 100644 --- a/crates/storage-sled/src/lib.rs +++ b/crates/storage-sled/src/lib.rs @@ -1,16 +1,7 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - #[cfg(test)] mod tests { use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } + use base::create_mock_meta; #[test] fn test_update_secret() { let db: sled::Db = sled::open("sled_db").unwrap(); @@ -26,12 +17,20 @@ mod tests { let db: sled::Db = sled::open("sled_db").unwrap(); delete_secret(&db, "foo"); } + #[test] + fn test_meta(){ + let db: sled::Db = sled::open("sled_db").unwrap(); + println!("writing metadata:"); + update_secret_meta(&db, "metatest", create_mock_meta()); + println!("getting metadata:"); + get_secretmeta(&db, "metatest"); + } } use sled::Db; -use base::{deserialize_secret_struct, serialize_secret_json, TempSecret}; +use base::{deserialize_metadata_struct, deserialize_secret_struct, serialize_metadata_json, serialize_secret_json, SecretMeta, TempSecret}; -/// [TODO] Currently no proper versioning +/// [TODO] Currently no proper versioning /// inserts a secret. If there was already a secret in the given path, the version is incremented fn update_secret(db: &Db, path: &str, mut secret: TempSecret) { match get_secret(db, path) { @@ -64,6 +63,8 @@ fn update_secret(db: &Db, path: &str, mut secret: TempSecret) { } } +// !TODO eliminate redundancy: refactor get and update functions to accept generic types! + // read and return a secret from the DB //if there is no secret, return None fn get_secret(db: &Db, path: &str) -> Option{ @@ -96,7 +97,71 @@ fn get_secret(db: &Db, path: &str) -> Option{ } } -/// delete secret at path + +// TODO write abstract get_something fn +// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#read-secret-metadata +fn get_secretmeta(db: &Db, path: &str) -> Option{ + let raw_metadata; + match db.get(path) { + Ok(Some(ivec)) => { + raw_metadata = ivec; + } + Err(e) => { + eprintln!("Error on retrieving metadata: {}", e); + return None; + } + Ok(None) => { + return None; + } + } + let as_str = String::from_utf8(raw_metadata.to_vec()).unwrap(); + match deserialize_metadata_struct(&as_str) { + Ok(meta) => { + #[cfg(test)] + println!("got some metadata: {:?}", meta); + return Some(meta); + } + Err(e) => { + eprintln!("error on secret deserialization: {}", e); + return None; + } + + } +} + +// currently early version (copied from update_secret) +fn update_secret_meta(db: &Db, path: &str, mut meta: SecretMeta) { + match get_secretmeta(db, path) { + Some(meta) => { + // case secret found. TODO save it somewhere for versioning + #[cfg(test)] + print!("something was found. new version {:?} \n", meta) + } + None => { + } + } + match serialize_metadata_json(&meta) { + Ok(meta_json) => { + #[cfg(test)] + println!("String: {:?}", meta_json.clone()); + let as_ivec = sled::IVec::from(meta_json.into_bytes()); // maybe outsource this in a fn later + #[cfg(test)] + println!("ivec: {:?}", as_ivec); + match db.insert(path, as_ivec) { + Ok(_) => println!("Metadata inserted"), + Err(e) => eprintln!("Failed to insert meta: {}", e), + } + } + Err(e) => eprintln!("Failed to serialize meta: {}", e), + } +} + + +/// TODO soft delete the secret version at path. can be undone with undelete_secret +// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-latest-version-of-secret +// https://developer.hashicorp.com/vault/api-docs/secret/kv/kv-v2#delete-secret-versions + +/// hard delete secret at path fn delete_secret(db: &Db, path: &str) { let rem = db.remove(path); } \ No newline at end of file