From 080ca592e672359826739b2588d4d71093d0493f Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Thu, 28 Sep 2023 17:32:43 +0100 Subject: [PATCH 01/13] Scaffold plugin architecture --- sample-pop-server/Cargo.toml | 8 ++ sample-pop-server/src/lib.rs | 8 +- sample-pop-server/src/model/mod.rs | 1 - sample-pop-server/src/plugin/index/mod.rs | 22 ++++++ .../src/{web/index.rs => plugin/index/web.rs} | 0 sample-pop-server/src/plugin/loader.rs | 76 +++++++++++++++++++ sample-pop-server/src/plugin/mod.rs | 14 ++++ sample-pop-server/src/plugin/traits.rs | 36 +++++++++ sample-pop-server/src/web/mod.rs | 2 - 9 files changed, 163 insertions(+), 4 deletions(-) delete mode 100644 sample-pop-server/src/model/mod.rs create mode 100644 sample-pop-server/src/plugin/index/mod.rs rename sample-pop-server/src/{web/index.rs => plugin/index/web.rs} (100%) create mode 100644 sample-pop-server/src/plugin/loader.rs create mode 100644 sample-pop-server/src/plugin/mod.rs create mode 100644 sample-pop-server/src/plugin/traits.rs diff --git a/sample-pop-server/Cargo.toml b/sample-pop-server/Cargo.toml index c01c0b24..b32aba6b 100644 --- a/sample-pop-server/Cargo.toml +++ b/sample-pop-server/Cargo.toml @@ -13,8 +13,10 @@ chrono = "0.4.26" did-utils = { path = "../did-utils" } dotenv-flow = "0.15.0" hyper = { version = "0.14.27", features = ["full"] } +lazy_static = "1.4.0" multibase = "0.8.0" # earlier version due to 'did-utils' serde_json = "1.0.104" +thiserror = "1.0.49" tokio = { version = "1.30.0", features = ["full"] } tower-http = { version = "0.4.3", features = ["catch-panic", "trace"] } tracing = "0.1.37" @@ -25,3 +27,9 @@ uuid = { version = "1.4.1", features = ["v4"] } [dev-dependencies] tempdir = "0.3.7" tower = { version = "0.4.13", features = ["util"] } + +[features] +default = ["plugin.index"] + +# plugins +"plugin.index" = [] diff --git a/sample-pop-server/src/lib.rs b/sample-pop-server/src/lib.rs index 93ab9b98..9205af7a 100644 --- a/sample-pop-server/src/lib.rs +++ b/sample-pop-server/src/lib.rs @@ -1,8 +1,10 @@ pub mod didgen; -pub mod model; +pub mod plugin; pub mod util; pub mod web; +use plugin::loader::PluginLoader; + use axum::Router; use tower_http::catch_panic::CatchPanicLayer; use tower_http::trace::TraceLayer; @@ -12,7 +14,11 @@ pub const DIDDOC_DIR: &str = "storage"; pub const KEYSTORE_DIR: &str = "storage/keystore"; pub fn app() -> Router { + let mut loader = PluginLoader::default(); + let _ = loader.load(); + web::routes() // + .merge(loader.routes().unwrap_or_default()) .layer(TraceLayer::new_for_http()) .layer(CatchPanicLayer::new()) } diff --git a/sample-pop-server/src/model/mod.rs b/sample-pop-server/src/model/mod.rs deleted file mode 100644 index 8b137891..00000000 --- a/sample-pop-server/src/model/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/sample-pop-server/src/plugin/index/mod.rs b/sample-pop-server/src/plugin/index/mod.rs new file mode 100644 index 00000000..806f053a --- /dev/null +++ b/sample-pop-server/src/plugin/index/mod.rs @@ -0,0 +1,22 @@ +mod web; + +use axum::Router; + +use super::traits::{Plugin, PluginError}; + +#[derive(Default)] +pub struct IndexPlugin; + +impl Plugin for IndexPlugin { + fn name(&self) -> &'static str { + "index" + } + + fn initialize(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn routes(&self) -> Router { + web::routes() + } +} diff --git a/sample-pop-server/src/web/index.rs b/sample-pop-server/src/plugin/index/web.rs similarity index 100% rename from sample-pop-server/src/web/index.rs rename to sample-pop-server/src/plugin/index/web.rs diff --git a/sample-pop-server/src/plugin/loader.rs b/sample-pop-server/src/plugin/loader.rs new file mode 100644 index 00000000..d65d612a --- /dev/null +++ b/sample-pop-server/src/plugin/loader.rs @@ -0,0 +1,76 @@ +use std::{collections::HashMap, vec}; + +use axum::Router; + +use super::{ + traits::{Plugin, PluginError}, + PLUGINS, +}; + +pub struct PluginLoader { + loaded: bool, + plugins: &'static Vec>, + collected_routes: Vec, +} + +impl PluginLoader { + /// Instantiate an object aware of all statically registered plugins + pub fn new() -> Self { + Self { + loaded: false, + plugins: &*PLUGINS, + collected_routes: vec![], + } + } + + /// Load referenced plugins + /// + /// This entails initializing them and merging their routes internally (only + /// upon successful initialization). All plugins failing to be initialized + /// are returned in a map with respectively raised errors. + pub fn load(&mut self) -> Result<(), HashMap<&dyn Plugin, PluginError>> { + // Reset collection of routes + self.collected_routes.truncate(0); + + // Initialize plugins and collect routes on successful status + let errors: HashMap<_, _> = self + .plugins + .iter() + .filter_map(|plugin| match plugin.initialize() { + Ok(_) => { + self.collected_routes.push(plugin.routes()); + None + } + Err(err) => Some((&**plugin, err)), + }) + .collect(); + + // Flag as loaded + self.loaded = true; + + // Return state of completion + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + } + + /// Merge collected routes from all plugins successfully initialized. + pub fn routes(&self) -> Result { + if self.loaded { + Ok(self + .collected_routes + .iter() + .fold(Router::new(), |acc, e| acc.merge(e.clone()))) + } else { + Err(PluginError::Unloaded) + } + } +} + +impl Default for PluginLoader { + fn default() -> Self { + Self::new() + } +} diff --git a/sample-pop-server/src/plugin/mod.rs b/sample-pop-server/src/plugin/mod.rs new file mode 100644 index 00000000..b2911f68 --- /dev/null +++ b/sample-pop-server/src/plugin/mod.rs @@ -0,0 +1,14 @@ +pub mod loader; +pub mod traits; + +use lazy_static::lazy_static; +use traits::Plugin; + +mod index; + +lazy_static! { + pub static ref PLUGINS: Vec> = vec![ + #[cfg(feature = "plugin.index")] + Box::::default() + ]; +} diff --git a/sample-pop-server/src/plugin/traits.rs b/sample-pop-server/src/plugin/traits.rs new file mode 100644 index 00000000..c03cd1c6 --- /dev/null +++ b/sample-pop-server/src/plugin/traits.rs @@ -0,0 +1,36 @@ +use std::hash::{Hash, Hasher}; + +use axum::Router; + +pub enum PluginError { + InitError, + Unloaded, +} + +pub trait Plugin: Sync { + /// Define a unique identifier + fn name(&self) -> &'static str; + + /// Provide initialization actions as needed + fn initialize(&self) -> Result<(), PluginError>; + + /// Export managed endpoints + fn routes(&self) -> Router; +} + +impl Eq for dyn Plugin {} + +impl PartialEq for dyn Plugin { + fn eq(&self, other: &Self) -> bool { + self.name() == other.name() + } +} + +impl Hash for dyn Plugin { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.name().hash(state) + } +} diff --git a/sample-pop-server/src/web/mod.rs b/sample-pop-server/src/web/mod.rs index 3aa9ddcf..1d6f8609 100644 --- a/sample-pop-server/src/web/mod.rs +++ b/sample-pop-server/src/web/mod.rs @@ -1,10 +1,8 @@ mod did; -mod index; use axum::Router; pub fn routes() -> Router { Router::new() // - .merge(index::routes()) .merge(did::routes()) } From c998fe09a88fc0f80d3ea2ba1c7ff7cbf16a747f Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Tue, 3 Oct 2023 09:48:15 +0100 Subject: [PATCH 02/13] Improve implementation and provide tests for the plugin container --- sample-pop-server/src/lib.rs | 4 +- sample-pop-server/src/main.rs | 7 +- sample-pop-server/src/plugin/container.rs | 270 ++++++++++++++++++++++ sample-pop-server/src/plugin/index/mod.rs | 6 +- sample-pop-server/src/plugin/loader.rs | 76 ------ sample-pop-server/src/plugin/mod.rs | 3 +- sample-pop-server/src/plugin/traits.rs | 12 +- 7 files changed, 291 insertions(+), 87 deletions(-) create mode 100644 sample-pop-server/src/plugin/container.rs delete mode 100644 sample-pop-server/src/plugin/loader.rs diff --git a/sample-pop-server/src/lib.rs b/sample-pop-server/src/lib.rs index 9205af7a..4b9d71ae 100644 --- a/sample-pop-server/src/lib.rs +++ b/sample-pop-server/src/lib.rs @@ -3,7 +3,7 @@ pub mod plugin; pub mod util; pub mod web; -use plugin::loader::PluginLoader; +use plugin::container::PluginContainer; use axum::Router; use tower_http::catch_panic::CatchPanicLayer; @@ -14,7 +14,7 @@ pub const DIDDOC_DIR: &str = "storage"; pub const KEYSTORE_DIR: &str = "storage/keystore"; pub fn app() -> Router { - let mut loader = PluginLoader::default(); + let mut loader = PluginContainer::default(); let _ = loader.load(); web::routes() // diff --git a/sample-pop-server/src/main.rs b/sample-pop-server/src/main.rs index c6d4b1d3..aa87b6c8 100644 --- a/sample-pop-server/src/main.rs +++ b/sample-pop-server/src/main.rs @@ -32,10 +32,9 @@ fn config_tracing() { let tracing_layer = tracing_subscriber::fmt::layer(); let filter = filter::Targets::new() - .with_target("tower_http::trace::on_response", Level::DEBUG) - .with_target("tower_http::trace::on_request", Level::DEBUG) - .with_target("tower_http::trace::make_span", Level::DEBUG) - .with_default(Level::INFO); + .with_target("hyper::proto", Level::INFO) + .with_target("tower_http::trace", Level::DEBUG) + .with_default(Level::DEBUG); tracing_subscriber::registry() .with(tracing_layer) diff --git a/sample-pop-server/src/plugin/container.rs b/sample-pop-server/src/plugin/container.rs new file mode 100644 index 00000000..e086b6d3 --- /dev/null +++ b/sample-pop-server/src/plugin/container.rs @@ -0,0 +1,270 @@ +use std::collections::{HashMap, HashSet}; + +use axum::Router; + +use super::{ + traits::{Plugin, PluginError}, + PLUGINS, +}; + +#[derive(Debug, PartialEq)] +pub enum PluginContainerError { + DuplicateEntry, + Unloaded, + PluginErrorMap(HashMap), +} + +pub struct PluginContainer<'a> { + loaded: bool, + collected_routes: Vec, + plugins: &'a Vec>, +} + +impl<'a> Default for PluginContainer<'a> { + fn default() -> Self { + Self::new() + } +} + +impl<'a> PluginContainer<'a> { + /// Instantiate an object aware of all statically registered plugins + pub fn new() -> Self { + Self { + loaded: false, + collected_routes: vec![], + plugins: &*PLUGINS, + } + } + + /// Search loaded plugin based on name string + pub fn find_plugin(&self, name: &str) -> Option<&dyn Plugin> { + self.plugins + .iter() + .find_map(|plugin| (name == plugin.name()).then_some(&**plugin)) + } + + /// Load referenced plugins + /// + /// This entails mounting them and merging their routes internally (only + /// upon successful initialization). An error is returned if plugins + /// bearing the same name are found. Also, all plugins failing to be + /// initialized are returned in a map with respectively raised errors. + pub fn load(&mut self) -> Result<(), PluginContainerError> { + tracing::debug!("loading plugin container"); + + // Checking for duplicates + let unique_plugins: HashSet<_> = self.plugins.iter().collect(); + if unique_plugins.len() != self.plugins.len() { + tracing::error!("found duplicate entries in plugin registry"); + return Err(PluginContainerError::DuplicateEntry); + } + + // Reset collection of routes + self.collected_routes.truncate(0); + + // Mount plugins and collect routes on successful status + let errors: HashMap<_, _> = self + .plugins + .iter() + .filter_map(|plugin| match plugin.mount() { + Ok(_) => { + tracing::info!("mounted plugin {}", plugin.name()); + self.collected_routes.push(plugin.routes()); + None + } + Err(err) => { + tracing::error!("error mounting plugin {}", plugin.name()); + Some((plugin.name().to_string(), err)) + } + }) + .collect(); + + // Flag as loaded + self.loaded = true; + + // Return state of completion + if errors.is_empty() { + tracing::debug!("plugin container loaded"); + Ok(()) + } else { + Err(PluginContainerError::PluginErrorMap(errors)) + } + } + + /// Merge collected routes from all plugins successfully initialized. + pub fn routes(&self) -> Result { + if self.loaded { + Ok(self + .collected_routes + .iter() + .fold(Router::new(), |acc, e| acc.merge(e.clone()))) + } else { + Err(PluginContainerError::Unloaded) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use axum::routing::get; + + struct FirstPlugin; + impl Plugin for FirstPlugin { + fn name(&self) -> &'static str { + "first" + } + + fn mount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn unmount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn routes(&self) -> Router { + Router::new().route("/first", get(|| async {})) + } + } + + struct SecondPlugin; + impl Plugin for SecondPlugin { + fn name(&self) -> &'static str { + "second" + } + + fn mount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn unmount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn routes(&self) -> Router { + Router::new().route("/second", get(|| async {})) + } + } + + struct SecondAgainPlugin; + impl Plugin for SecondAgainPlugin { + fn name(&self) -> &'static str { + "second" + } + + fn mount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn unmount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn routes(&self) -> Router { + Router::new().route("/second", get(|| async {})) + } + } + + struct FaultyPlugin; + impl Plugin for FaultyPlugin { + fn name(&self) -> &'static str { + "faulty" + } + + fn mount(&self) -> Result<(), PluginError> { + Err(PluginError::InitError) + } + + fn unmount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn routes(&self) -> Router { + Router::new().route("/faulty", get(|| async {})) + } + } + + #[test] + fn test_loading() { + let mut container = PluginContainer { + loaded: false, + collected_routes: vec![], + plugins: &vec![Box::new(FirstPlugin {}), Box::new(SecondPlugin {})], + }; + + assert!(container.load().is_ok()); + assert!(container.routes().is_ok()); + + assert!(container.find_plugin("first").is_some()); + assert!(container.find_plugin("second").is_some()); + assert!(container.find_plugin("non-existent").is_none()); + + // The actual routes collected are actually hard to test + // given that axum::Router seems not to provide public + // directives to inquire internal state. + // See: https://github.com/tokio-rs/axum/discussions/860 + assert_eq!(container.collected_routes.len(), 2); + } + + #[test] + fn test_double_loading() { + let mut container = PluginContainer { + loaded: false, + collected_routes: vec![], + plugins: &vec![Box::new(FirstPlugin {}), Box::new(SecondPlugin {})], + }; + + assert!(container.load().is_ok()); + assert!(container.load().is_ok()); + + assert_eq!(container.collected_routes.len(), 2); + } + + #[test] + fn test_loading_with_duplicates() { + let mut container = PluginContainer { + loaded: false, + collected_routes: vec![], + plugins: &vec![Box::new(SecondPlugin {}), Box::new(SecondAgainPlugin {})], + }; + + assert_eq!( + container.load().unwrap_err(), + PluginContainerError::DuplicateEntry + ); + } + + #[test] + fn test_loading_with_failing_plugin() { + let mut container = PluginContainer { + loaded: false, + collected_routes: vec![], + plugins: &vec![Box::new(FirstPlugin {}), Box::new(FaultyPlugin {})], + }; + + let err = container.load().unwrap_err(); + + assert_eq!( + err, + PluginContainerError::PluginErrorMap( + [("faulty".to_string(), PluginError::InitError)] + .into_iter() + .collect() + ) + ); + + assert_eq!(container.collected_routes.len(), 1); + } + + #[test] + fn test_route_extraction_without_loading() { + let container = PluginContainer { + loaded: false, + collected_routes: vec![], + plugins: &vec![Box::new(FirstPlugin {}), Box::new(SecondPlugin {})], + }; + + assert_eq!(container.routes().unwrap_err(), PluginContainerError::Unloaded); + } +} diff --git a/sample-pop-server/src/plugin/index/mod.rs b/sample-pop-server/src/plugin/index/mod.rs index 806f053a..12020cee 100644 --- a/sample-pop-server/src/plugin/index/mod.rs +++ b/sample-pop-server/src/plugin/index/mod.rs @@ -12,7 +12,11 @@ impl Plugin for IndexPlugin { "index" } - fn initialize(&self) -> Result<(), PluginError> { + fn mount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn unmount(&self) -> Result<(), PluginError> { Ok(()) } diff --git a/sample-pop-server/src/plugin/loader.rs b/sample-pop-server/src/plugin/loader.rs deleted file mode 100644 index d65d612a..00000000 --- a/sample-pop-server/src/plugin/loader.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::{collections::HashMap, vec}; - -use axum::Router; - -use super::{ - traits::{Plugin, PluginError}, - PLUGINS, -}; - -pub struct PluginLoader { - loaded: bool, - plugins: &'static Vec>, - collected_routes: Vec, -} - -impl PluginLoader { - /// Instantiate an object aware of all statically registered plugins - pub fn new() -> Self { - Self { - loaded: false, - plugins: &*PLUGINS, - collected_routes: vec![], - } - } - - /// Load referenced plugins - /// - /// This entails initializing them and merging their routes internally (only - /// upon successful initialization). All plugins failing to be initialized - /// are returned in a map with respectively raised errors. - pub fn load(&mut self) -> Result<(), HashMap<&dyn Plugin, PluginError>> { - // Reset collection of routes - self.collected_routes.truncate(0); - - // Initialize plugins and collect routes on successful status - let errors: HashMap<_, _> = self - .plugins - .iter() - .filter_map(|plugin| match plugin.initialize() { - Ok(_) => { - self.collected_routes.push(plugin.routes()); - None - } - Err(err) => Some((&**plugin, err)), - }) - .collect(); - - // Flag as loaded - self.loaded = true; - - // Return state of completion - if errors.is_empty() { - Ok(()) - } else { - Err(errors) - } - } - - /// Merge collected routes from all plugins successfully initialized. - pub fn routes(&self) -> Result { - if self.loaded { - Ok(self - .collected_routes - .iter() - .fold(Router::new(), |acc, e| acc.merge(e.clone()))) - } else { - Err(PluginError::Unloaded) - } - } -} - -impl Default for PluginLoader { - fn default() -> Self { - Self::new() - } -} diff --git a/sample-pop-server/src/plugin/mod.rs b/sample-pop-server/src/plugin/mod.rs index b2911f68..b35079b2 100644 --- a/sample-pop-server/src/plugin/mod.rs +++ b/sample-pop-server/src/plugin/mod.rs @@ -1,9 +1,10 @@ -pub mod loader; +pub mod container; pub mod traits; use lazy_static::lazy_static; use traits::Plugin; +#[cfg(feature = "plugin.index")] mod index; lazy_static! { diff --git a/sample-pop-server/src/plugin/traits.rs b/sample-pop-server/src/plugin/traits.rs index c03cd1c6..7a590213 100644 --- a/sample-pop-server/src/plugin/traits.rs +++ b/sample-pop-server/src/plugin/traits.rs @@ -1,10 +1,13 @@ -use std::hash::{Hash, Hasher}; +use std::{ + fmt::Debug, + hash::{Hash, Hasher}, +}; use axum::Router; +#[derive(Debug, PartialEq)] pub enum PluginError { InitError, - Unloaded, } pub trait Plugin: Sync { @@ -12,7 +15,10 @@ pub trait Plugin: Sync { fn name(&self) -> &'static str; /// Provide initialization actions as needed - fn initialize(&self) -> Result<(), PluginError>; + fn mount(&self) -> Result<(), PluginError>; + + /// Reverse initialization actions as needed + fn unmount(&self) -> Result<(), PluginError>; /// Export managed endpoints fn routes(&self) -> Router; From a24da0872bdb426f11c2ce96b7f338fb2f34aa2d Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Tue, 3 Oct 2023 13:03:21 +0100 Subject: [PATCH 03/13] Move didpop under dedicated plugin --- sample-pop-server/Cargo.toml | 17 +++++---- sample-pop-server/src/bin/didgen.rs | 29 ++++++++++---- sample-pop-server/src/lib.rs | 14 ++----- sample-pop-server/src/main.rs | 9 +---- sample-pop-server/src/plugin/container.rs | 5 ++- .../src/{ => plugin/didpop}/didgen.rs | 33 ++++++++-------- sample-pop-server/src/plugin/didpop/mod.rs | 38 +++++++++++++++++++ .../src/{ => plugin/didpop}/util/didweb.rs | 0 .../src/{ => plugin/didpop}/util/keystore.rs | 11 ++---- .../src/plugin/didpop/util/mod.rs | 4 ++ .../src/{web/did.rs => plugin/didpop/web.rs} | 2 +- sample-pop-server/src/plugin/mod.rs | 10 +++-- sample-pop-server/src/plugin/traits.rs | 2 +- sample-pop-server/src/util/mod.rs | 5 --- sample-pop-server/src/web/mod.rs | 8 ---- 15 files changed, 110 insertions(+), 77 deletions(-) rename sample-pop-server/src/{ => plugin/didpop}/didgen.rs (89%) create mode 100644 sample-pop-server/src/plugin/didpop/mod.rs rename sample-pop-server/src/{ => plugin/didpop}/util/didweb.rs (100%) rename sample-pop-server/src/{ => plugin/didpop}/util/keystore.rs (94%) create mode 100644 sample-pop-server/src/plugin/didpop/util/mod.rs rename sample-pop-server/src/{web/did.rs => plugin/didpop/web.rs} (99%) delete mode 100644 sample-pop-server/src/web/mod.rs diff --git a/sample-pop-server/Cargo.toml b/sample-pop-server/Cargo.toml index b32aba6b..a595bc47 100644 --- a/sample-pop-server/Cargo.toml +++ b/sample-pop-server/Cargo.toml @@ -9,27 +9,30 @@ default-run = "sample-pop-server" [dependencies] async-trait = "0.1.73" axum = { version = "0.6.20" } -chrono = "0.4.26" -did-utils = { path = "../did-utils" } +chrono = { version = "0.4.26", optional = true } dotenv-flow = "0.15.0" hyper = { version = "0.14.27", features = ["full"] } lazy_static = "1.4.0" -multibase = "0.8.0" # earlier version due to 'did-utils' serde_json = "1.0.104" thiserror = "1.0.49" tokio = { version = "1.30.0", features = ["full"] } tower-http = { version = "0.4.3", features = ["catch-panic", "trace"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["json"] } -url = "2.4.0" -uuid = { version = "1.4.1", features = ["v4"] } + +# didpop +did-utils = { path = "../did-utils", optional = true } +multibase = { version = "0.8.0", optional = true } # earlier version due to 'did-utils' +url = { version = "2.4.0", optional = true } +uuid = { version = "1.4.1", features = ["v4"], optional = true } [dev-dependencies] tempdir = "0.3.7" tower = { version = "0.4.13", features = ["util"] } [features] -default = ["plugin.index"] +default = ["plugin-index", "plugin-didpop"] # plugins -"plugin.index" = [] +plugin-index = ["dep:chrono"] +plugin-didpop = ["dep:chrono", "dep:did-utils", "dep:multibase", "dep:url", "dep:uuid"] diff --git a/sample-pop-server/src/bin/didgen.rs b/sample-pop-server/src/bin/didgen.rs index 56811239..12af891f 100644 --- a/sample-pop-server/src/bin/didgen.rs +++ b/sample-pop-server/src/bin/didgen.rs @@ -1,14 +1,27 @@ -use sample_pop_server::didgen; +#[cfg(feature = "plugin-didpop")] +use sample_pop_server::plugin::didpop::didgen; /// Program entry fn main() -> Result<(), Box> { - // Load dotenv-flow variables - dotenv_flow::dotenv_flow().ok(); + #[cfg(feature = "plugin-didpop")] + { + use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - // Enable tracing - tracing_subscriber::fmt::init(); + // Load dotenv-flow variables + dotenv_flow::dotenv_flow().ok(); - // Run didgen logic - didgen::didgen()?; - Ok(()) + // Enable logging + tracing_subscriber::registry() + .with(tracing_subscriber::fmt::layer()) + .init(); + + // Run didgen logic + didgen::didgen()?; + Ok(()) + } + + #[cfg(not(feature = "plugin-didpop"))] + { + Err("You must enable `plugin-didpop` to run this command.".into()) + } } diff --git a/sample-pop-server/src/lib.rs b/sample-pop-server/src/lib.rs index 4b9d71ae..5cfbd604 100644 --- a/sample-pop-server/src/lib.rs +++ b/sample-pop-server/src/lib.rs @@ -1,7 +1,5 @@ -pub mod didgen; pub mod plugin; pub mod util; -pub mod web; use plugin::container::PluginContainer; @@ -9,16 +7,12 @@ use axum::Router; use tower_http::catch_panic::CatchPanicLayer; use tower_http::trace::TraceLayer; -#[allow(unused)] -pub const DIDDOC_DIR: &str = "storage"; -pub const KEYSTORE_DIR: &str = "storage/keystore"; - pub fn app() -> Router { - let mut loader = PluginContainer::default(); - let _ = loader.load(); + let mut container = PluginContainer::default(); + let _ = container.load(); - web::routes() // - .merge(loader.routes().unwrap_or_default()) + Router::new() // + .merge(container.routes().unwrap_or_default()) .layer(TraceLayer::new_for_http()) .layer(CatchPanicLayer::new()) } diff --git a/sample-pop-server/src/main.rs b/sample-pop-server/src/main.rs index aa87b6c8..4af84d4a 100644 --- a/sample-pop-server/src/main.rs +++ b/sample-pop-server/src/main.rs @@ -1,4 +1,4 @@ -use sample_pop_server::{app, didgen}; +use sample_pop_server::app; use axum::Server; use std::net::SocketAddr; @@ -8,14 +8,9 @@ async fn main() { // Load dotenv-flow variables dotenv_flow::dotenv_flow().ok(); - // Enable tracing + // Enable logging config_tracing(); - // Run `didgen` if necessary - if didgen::validate_diddoc().is_err() { - didgen::didgen().expect("Failed to generate an initial keystore and its DID document."); - }; - // Start server let port = std::env::var("SERVER_LOCAL_PORT").unwrap_or("3000".to_owned()); let addr: SocketAddr = format!("127.0.0.1:{port}").parse().unwrap(); diff --git a/sample-pop-server/src/plugin/container.rs b/sample-pop-server/src/plugin/container.rs index e086b6d3..c45beb14 100644 --- a/sample-pop-server/src/plugin/container.rs +++ b/sample-pop-server/src/plugin/container.rs @@ -265,6 +265,9 @@ mod tests { plugins: &vec![Box::new(FirstPlugin {}), Box::new(SecondPlugin {})], }; - assert_eq!(container.routes().unwrap_err(), PluginContainerError::Unloaded); + assert_eq!( + container.routes().unwrap_err(), + PluginContainerError::Unloaded + ); } } diff --git a/sample-pop-server/src/didgen.rs b/sample-pop-server/src/plugin/didpop/didgen.rs similarity index 89% rename from sample-pop-server/src/didgen.rs rename to sample-pop-server/src/plugin/didpop/didgen.rs index eaf6a768..35a4579d 100644 --- a/sample-pop-server/src/didgen.rs +++ b/sample-pop-server/src/plugin/didpop/didgen.rs @@ -1,4 +1,4 @@ -use crate::{ +use crate::plugin::didpop::{ util::{didweb, KeyStore}, DIDDOC_DIR, }; @@ -11,35 +11,40 @@ use did_utils::{ }; use std::path::Path; -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum Error { + #[error("KeyGenerationError")] KeyGenerationError, + #[error("MissingServerPublicDomain")] MissingServerPublicDomain, + #[error("DidAddressDerivationError")] DidAddressDerivationError, + #[error("PersistenceError")] PersistenceError, - Unknown(String), + #[error("Generic: {0}")] + Generic(String), } /// Generates keys and forward them for DID generation pub fn didgen() -> Result { // Create a new store, which is timestamp-aware let mut store = KeyStore::new(); - tracing::info!("Keystore: {}", store.path()); + tracing::info!("keystore: {}", store.path()); // Generate authentication key - tracing::info!("Generating authentication key..."); + tracing::debug!("generating authentication key"); let authentication_key = store .gen_ed25519_jwk() .map_err(|_| Error::KeyGenerationError)?; // Generate assertion key - tracing::info!("Generating assertion key..."); + tracing::debug!("generating assertion key"); let assertion_key = store .gen_ed25519_jwk() .map_err(|_| Error::KeyGenerationError)?; // Generate agreement key - tracing::info!("Generating agreement key..."); + tracing::debug!("generating agreement key"); let agreement_key = store .gen_x25519_jwk() .map_err(|_| Error::KeyGenerationError)?; @@ -48,7 +53,7 @@ pub fn didgen() -> Result { let diddoc = gen_diddoc(authentication_key, assertion_key, agreement_key)?; // Mark successful completion - tracing::info!("Successful completion."); + tracing::debug!("successful completion"); Ok(diddoc) } @@ -58,7 +63,7 @@ fn gen_diddoc( assertion_key: Jwk, agreement_key: Jwk, ) -> Result { - tracing::info!("Building DID document..."); + tracing::info!("building DID document"); // Prepare DID address @@ -141,7 +146,7 @@ fn gen_diddoc( std::fs::write(format!("{DIDDOC_DIR}/did.json"), &did_json) .map_err(|_| Error::PersistenceError)?; - tracing::info!("Persisted DID document to file."); + tracing::info!("persisted DID document to disk"); Ok(did_json) } @@ -186,11 +191,3 @@ pub fn validate_diddoc() -> Result<(), String> { Ok(()) } - -impl std::error::Error for Error {} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{self:?}") - } -} diff --git a/sample-pop-server/src/plugin/didpop/mod.rs b/sample-pop-server/src/plugin/didpop/mod.rs new file mode 100644 index 00000000..aad31514 --- /dev/null +++ b/sample-pop-server/src/plugin/didpop/mod.rs @@ -0,0 +1,38 @@ +pub mod didgen; +mod util; +mod web; + +use super::traits::{Plugin, PluginError}; +use axum::Router; + +pub const DIDDOC_DIR: &str = "storage"; +pub const KEYSTORE_DIR: &str = "storage/keystore"; + +#[derive(Default)] +pub struct DidPopPlugin; + +impl Plugin for DidPopPlugin { + fn name(&self) -> &'static str { + "didpop" + } + + fn mount(&self) -> Result<(), PluginError> { + if didgen::validate_diddoc().is_err() { + didgen::didgen().map_err(|_| { + tracing::error!("failed to generate an initial keystore and its DID document"); + + PluginError::InitError + })?; + }; + + Ok(()) + } + + fn unmount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn routes(&self) -> Router { + web::routes() + } +} diff --git a/sample-pop-server/src/util/didweb.rs b/sample-pop-server/src/plugin/didpop/util/didweb.rs similarity index 100% rename from sample-pop-server/src/util/didweb.rs rename to sample-pop-server/src/plugin/didpop/util/didweb.rs diff --git a/sample-pop-server/src/util/keystore.rs b/sample-pop-server/src/plugin/didpop/util/keystore.rs similarity index 94% rename from sample-pop-server/src/util/keystore.rs rename to sample-pop-server/src/plugin/didpop/util/keystore.rs index 3abef953..5c8ef39b 100644 --- a/sample-pop-server/src/util/keystore.rs +++ b/sample-pop-server/src/plugin/didpop/util/keystore.rs @@ -1,15 +1,10 @@ use did_utils::{ - crypto::{ - ed25519::Ed25519KeyPair, - traits::{Generate, KeyMaterial}, - x25519::X25519KeyPair, - }, + crypto::{ed25519::Ed25519KeyPair, traits::Generate, x25519::X25519KeyPair}, didcore::Jwk, }; -use serde_json::{json, Value}; use std::error::Error; -use crate::KEYSTORE_DIR; +use crate::plugin::didpop::KEYSTORE_DIR; pub struct KeyStore { path: String, @@ -145,7 +140,7 @@ mod tests { impl KeyStore { fn destroy(self) { - std::fs::remove_file(self.path); + let _ = std::fs::remove_file(self.path); } } diff --git a/sample-pop-server/src/plugin/didpop/util/mod.rs b/sample-pop-server/src/plugin/didpop/util/mod.rs new file mode 100644 index 00000000..e649243c --- /dev/null +++ b/sample-pop-server/src/plugin/didpop/util/mod.rs @@ -0,0 +1,4 @@ +pub mod didweb; +pub mod keystore; + +pub use keystore::KeyStore; diff --git a/sample-pop-server/src/web/did.rs b/sample-pop-server/src/plugin/didpop/web.rs similarity index 99% rename from sample-pop-server/src/web/did.rs rename to sample-pop-server/src/plugin/didpop/web.rs index 0dd148ce..280993c9 100644 --- a/sample-pop-server/src/web/did.rs +++ b/sample-pop-server/src/plugin/didpop/web.rs @@ -14,7 +14,7 @@ use multibase::Base; use serde_json::{json, Value}; use std::collections::HashMap; -use crate::{util::KeyStore, DIDDOC_DIR}; +use crate::plugin::didpop::{util::KeyStore, DIDDOC_DIR}; const DEFAULT_CONTEXT_V2: &str = "https://www.w3.org/ns/credentials/v2"; diff --git a/sample-pop-server/src/plugin/mod.rs b/sample-pop-server/src/plugin/mod.rs index b35079b2..634d5138 100644 --- a/sample-pop-server/src/plugin/mod.rs +++ b/sample-pop-server/src/plugin/mod.rs @@ -4,12 +4,16 @@ pub mod traits; use lazy_static::lazy_static; use traits::Plugin; -#[cfg(feature = "plugin.index")] +#[cfg(feature = "plugin-didpop")] +pub mod didpop; +#[cfg(feature = "plugin-index")] mod index; lazy_static! { pub static ref PLUGINS: Vec> = vec![ - #[cfg(feature = "plugin.index")] - Box::::default() + #[cfg(feature = "plugin-index")] + Box::::default(), + #[cfg(feature = "plugin-didpop")] + Box::::default(), ]; } diff --git a/sample-pop-server/src/plugin/traits.rs b/sample-pop-server/src/plugin/traits.rs index 7a590213..7c4a334b 100644 --- a/sample-pop-server/src/plugin/traits.rs +++ b/sample-pop-server/src/plugin/traits.rs @@ -17,7 +17,7 @@ pub trait Plugin: Sync { /// Provide initialization actions as needed fn mount(&self) -> Result<(), PluginError>; - /// Reverse initialization actions as needed + /// Revert initialization actions as needed fn unmount(&self) -> Result<(), PluginError>; /// Export managed endpoints diff --git a/sample-pop-server/src/util/mod.rs b/sample-pop-server/src/util/mod.rs index 038c50d6..5310108e 100644 --- a/sample-pop-server/src/util/mod.rs +++ b/sample-pop-server/src/util/mod.rs @@ -1,10 +1,5 @@ #![allow(unused)] -pub mod didweb; - -mod keystore; -pub use keystore::KeyStore; - pub fn crate_name() -> String { let current_dir = std::env::current_dir().unwrap(); let basename = current_dir.file_name().unwrap().to_str().unwrap(); diff --git a/sample-pop-server/src/web/mod.rs b/sample-pop-server/src/web/mod.rs deleted file mode 100644 index 1d6f8609..00000000 --- a/sample-pop-server/src/web/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod did; - -use axum::Router; - -pub fn routes() -> Router { - Router::new() // - .merge(did::routes()) -} From bf19e11754a3a53e86d4b4b7071cfa1d34c93de2 Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Tue, 3 Oct 2023 17:23:46 +0100 Subject: [PATCH 04/13] Rename sample-pop-server to generic-server --- did-endpoint/Cargo.toml | 8 ++++++++ did-endpoint/src/lib.rs | 14 ++++++++++++++ {sample-pop-server => generic-server}/.env | 0 {sample-pop-server => generic-server}/.gitignore | 0 {sample-pop-server => generic-server}/Cargo.toml | 4 ++-- {sample-pop-server => generic-server}/README.md | 0 .../src/bin/didgen.rs | 2 +- {sample-pop-server => generic-server}/src/lib.rs | 0 {sample-pop-server => generic-server}/src/main.rs | 2 +- .../src/plugin/container.rs | 0 .../src/plugin/didpop/didgen.rs | 0 .../src/plugin/didpop/mod.rs | 0 .../src/plugin/didpop/util/didweb.rs | 0 .../src/plugin/didpop/util/keystore.rs | 0 .../src/plugin/didpop/util/mod.rs | 0 .../src/plugin/didpop/web.rs | 0 .../src/plugin/index/mod.rs | 0 .../src/plugin/index/web.rs | 0 .../src/plugin/mod.rs | 0 .../src/plugin/traits.rs | 0 .../src/util/mod.rs | 0 .../storage/.gitignore | 0 .../storage/keystore/.gitkeep | 0 23 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 did-endpoint/Cargo.toml create mode 100644 did-endpoint/src/lib.rs rename {sample-pop-server => generic-server}/.env (100%) rename {sample-pop-server => generic-server}/.gitignore (100%) rename {sample-pop-server => generic-server}/Cargo.toml (94%) rename {sample-pop-server => generic-server}/README.md (100%) rename {sample-pop-server => generic-server}/src/bin/didgen.rs (93%) rename {sample-pop-server => generic-server}/src/lib.rs (100%) rename {sample-pop-server => generic-server}/src/main.rs (97%) rename {sample-pop-server => generic-server}/src/plugin/container.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/didpop/didgen.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/didpop/mod.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/didpop/util/didweb.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/didpop/util/keystore.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/didpop/util/mod.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/didpop/web.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/index/mod.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/index/web.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/mod.rs (100%) rename {sample-pop-server => generic-server}/src/plugin/traits.rs (100%) rename {sample-pop-server => generic-server}/src/util/mod.rs (100%) rename {sample-pop-server => generic-server}/storage/.gitignore (100%) rename {sample-pop-server => generic-server}/storage/keystore/.gitkeep (100%) diff --git a/did-endpoint/Cargo.toml b/did-endpoint/Cargo.toml new file mode 100644 index 00000000..4cc5f155 --- /dev/null +++ b/did-endpoint/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "did-endpoint" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/did-endpoint/src/lib.rs b/did-endpoint/src/lib.rs new file mode 100644 index 00000000..7d12d9af --- /dev/null +++ b/did-endpoint/src/lib.rs @@ -0,0 +1,14 @@ +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); + } +} diff --git a/sample-pop-server/.env b/generic-server/.env similarity index 100% rename from sample-pop-server/.env rename to generic-server/.env diff --git a/sample-pop-server/.gitignore b/generic-server/.gitignore similarity index 100% rename from sample-pop-server/.gitignore rename to generic-server/.gitignore diff --git a/sample-pop-server/Cargo.toml b/generic-server/Cargo.toml similarity index 94% rename from sample-pop-server/Cargo.toml rename to generic-server/Cargo.toml index a595bc47..016fe3bc 100644 --- a/sample-pop-server/Cargo.toml +++ b/generic-server/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "sample-pop-server" +name = "generic-server" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -default-run = "sample-pop-server" +default-run = "generic-server" [dependencies] async-trait = "0.1.73" diff --git a/sample-pop-server/README.md b/generic-server/README.md similarity index 100% rename from sample-pop-server/README.md rename to generic-server/README.md diff --git a/sample-pop-server/src/bin/didgen.rs b/generic-server/src/bin/didgen.rs similarity index 93% rename from sample-pop-server/src/bin/didgen.rs rename to generic-server/src/bin/didgen.rs index 12af891f..1b611764 100644 --- a/sample-pop-server/src/bin/didgen.rs +++ b/generic-server/src/bin/didgen.rs @@ -1,5 +1,5 @@ #[cfg(feature = "plugin-didpop")] -use sample_pop_server::plugin::didpop::didgen; +use generic_server::plugin::didpop::didgen; /// Program entry fn main() -> Result<(), Box> { diff --git a/sample-pop-server/src/lib.rs b/generic-server/src/lib.rs similarity index 100% rename from sample-pop-server/src/lib.rs rename to generic-server/src/lib.rs diff --git a/sample-pop-server/src/main.rs b/generic-server/src/main.rs similarity index 97% rename from sample-pop-server/src/main.rs rename to generic-server/src/main.rs index 4af84d4a..33d6a5de 100644 --- a/sample-pop-server/src/main.rs +++ b/generic-server/src/main.rs @@ -1,4 +1,4 @@ -use sample_pop_server::app; +use generic_server::app; use axum::Server; use std::net::SocketAddr; diff --git a/sample-pop-server/src/plugin/container.rs b/generic-server/src/plugin/container.rs similarity index 100% rename from sample-pop-server/src/plugin/container.rs rename to generic-server/src/plugin/container.rs diff --git a/sample-pop-server/src/plugin/didpop/didgen.rs b/generic-server/src/plugin/didpop/didgen.rs similarity index 100% rename from sample-pop-server/src/plugin/didpop/didgen.rs rename to generic-server/src/plugin/didpop/didgen.rs diff --git a/sample-pop-server/src/plugin/didpop/mod.rs b/generic-server/src/plugin/didpop/mod.rs similarity index 100% rename from sample-pop-server/src/plugin/didpop/mod.rs rename to generic-server/src/plugin/didpop/mod.rs diff --git a/sample-pop-server/src/plugin/didpop/util/didweb.rs b/generic-server/src/plugin/didpop/util/didweb.rs similarity index 100% rename from sample-pop-server/src/plugin/didpop/util/didweb.rs rename to generic-server/src/plugin/didpop/util/didweb.rs diff --git a/sample-pop-server/src/plugin/didpop/util/keystore.rs b/generic-server/src/plugin/didpop/util/keystore.rs similarity index 100% rename from sample-pop-server/src/plugin/didpop/util/keystore.rs rename to generic-server/src/plugin/didpop/util/keystore.rs diff --git a/sample-pop-server/src/plugin/didpop/util/mod.rs b/generic-server/src/plugin/didpop/util/mod.rs similarity index 100% rename from sample-pop-server/src/plugin/didpop/util/mod.rs rename to generic-server/src/plugin/didpop/util/mod.rs diff --git a/sample-pop-server/src/plugin/didpop/web.rs b/generic-server/src/plugin/didpop/web.rs similarity index 100% rename from sample-pop-server/src/plugin/didpop/web.rs rename to generic-server/src/plugin/didpop/web.rs diff --git a/sample-pop-server/src/plugin/index/mod.rs b/generic-server/src/plugin/index/mod.rs similarity index 100% rename from sample-pop-server/src/plugin/index/mod.rs rename to generic-server/src/plugin/index/mod.rs diff --git a/sample-pop-server/src/plugin/index/web.rs b/generic-server/src/plugin/index/web.rs similarity index 100% rename from sample-pop-server/src/plugin/index/web.rs rename to generic-server/src/plugin/index/web.rs diff --git a/sample-pop-server/src/plugin/mod.rs b/generic-server/src/plugin/mod.rs similarity index 100% rename from sample-pop-server/src/plugin/mod.rs rename to generic-server/src/plugin/mod.rs diff --git a/sample-pop-server/src/plugin/traits.rs b/generic-server/src/plugin/traits.rs similarity index 100% rename from sample-pop-server/src/plugin/traits.rs rename to generic-server/src/plugin/traits.rs diff --git a/sample-pop-server/src/util/mod.rs b/generic-server/src/util/mod.rs similarity index 100% rename from sample-pop-server/src/util/mod.rs rename to generic-server/src/util/mod.rs diff --git a/sample-pop-server/storage/.gitignore b/generic-server/storage/.gitignore similarity index 100% rename from sample-pop-server/storage/.gitignore rename to generic-server/storage/.gitignore diff --git a/sample-pop-server/storage/keystore/.gitkeep b/generic-server/storage/keystore/.gitkeep similarity index 100% rename from sample-pop-server/storage/keystore/.gitkeep rename to generic-server/storage/keystore/.gitkeep From 6039a484843c5f58d993e5fca40da609d148f8a7 Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Wed, 4 Oct 2023 17:47:32 +0100 Subject: [PATCH 05/13] WIP: Move didpop plugin into an external module --- did-endpoint/.env | 2 + did-endpoint/.gitignore | 1 + did-endpoint/Cargo.toml | 22 +++++ .../didpop => did-endpoint/src}/didgen.rs | 36 +++++--- did-endpoint/src/lib.rs | 52 +++++++++-- .../src}/util/didweb.rs | 0 .../src}/util/keystore.rs | 92 ++++++++----------- .../didpop => did-endpoint/src}/util/mod.rs | 0 .../plugin/didpop => did-endpoint/src}/web.rs | 23 ++++- generic-server/Cargo.toml | 10 +- generic-server/src/bin/{didgen.rs => didgen} | 0 generic-server/src/plugin/didpop/mod.rs | 38 -------- generic-server/src/plugin/index/web.rs | 6 +- generic-server/src/plugin/mod.rs | 9 +- 14 files changed, 153 insertions(+), 138 deletions(-) create mode 100644 did-endpoint/.env create mode 100644 did-endpoint/.gitignore rename {generic-server/src/plugin/didpop => did-endpoint/src}/didgen.rs (84%) rename {generic-server/src/plugin/didpop => did-endpoint/src}/util/didweb.rs (100%) rename {generic-server/src/plugin/didpop => did-endpoint/src}/util/keystore.rs (66%) rename {generic-server/src/plugin/didpop => did-endpoint/src}/util/mod.rs (100%) rename {generic-server/src/plugin/didpop => did-endpoint/src}/web.rs (91%) rename generic-server/src/bin/{didgen.rs => didgen} (100%) delete mode 100644 generic-server/src/plugin/didpop/mod.rs diff --git a/did-endpoint/.env b/did-endpoint/.env new file mode 100644 index 00000000..332923a9 --- /dev/null +++ b/did-endpoint/.env @@ -0,0 +1,2 @@ +STORAGE_DIRPATH="storage.test" +SERVER_PUBLIC_DOMAIN="example.com" diff --git a/did-endpoint/.gitignore b/did-endpoint/.gitignore new file mode 100644 index 00000000..2d5fc243 --- /dev/null +++ b/did-endpoint/.gitignore @@ -0,0 +1 @@ +storage* diff --git a/did-endpoint/Cargo.toml b/did-endpoint/Cargo.toml index 4cc5f155..13259e28 100644 --- a/did-endpoint/Cargo.toml +++ b/did-endpoint/Cargo.toml @@ -6,3 +6,25 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-trait = "0.1.73" +axum = { version = "0.6.20" } +chrono = { version = "0.4.26" } +did-utils = { path = "../did-utils"} +dotenv-flow = "0.15.0" +generic-server = { path = "../generic-server", default-features = false } +hyper = { version = "0.14.27", features = ["full"] } +lazy_static = "1.4.0" +multibase = { version = "0.8.0" } # earlier version due to 'did-utils' +serde_json = "1.0.104" +thiserror = "1.0.49" +tokio = { version = "1.30.0", features = ["full"] } +tower-http = { version = "0.4.3", features = ["catch-panic", "trace"] } +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["json"] } +url = { version = "2.4.0" } +uuid = { version = "1.4.1", features = ["v4"] } + + +[dev-dependencies] +tempdir = "0.3.7" +tower = { version = "0.4.13", features = ["util"] } diff --git a/generic-server/src/plugin/didpop/didgen.rs b/did-endpoint/src/didgen.rs similarity index 84% rename from generic-server/src/plugin/didpop/didgen.rs rename to did-endpoint/src/didgen.rs index 35a4579d..a8f93be9 100644 --- a/generic-server/src/plugin/didpop/didgen.rs +++ b/did-endpoint/src/didgen.rs @@ -1,7 +1,4 @@ -use crate::plugin::didpop::{ - util::{didweb, KeyStore}, - DIDDOC_DIR, -}; +use crate::util::{didweb, KeyStore}; use did_utils::{ didcore::{ AssertionMethod, Authentication, Document, Jwk, KeyAgreement, KeyFormat, Service, @@ -26,9 +23,11 @@ pub enum Error { } /// Generates keys and forward them for DID generation -pub fn didgen() -> Result { +/// +/// All persistence is handled at `storage_dirpath`. +pub fn didgen(storage_dirpath: &str, server_public_domain: &str) -> Result { // Create a new store, which is timestamp-aware - let mut store = KeyStore::new(); + let mut store = KeyStore::new(storage_dirpath); tracing::info!("keystore: {}", store.path()); // Generate authentication key @@ -50,7 +49,13 @@ pub fn didgen() -> Result { .map_err(|_| Error::KeyGenerationError)?; // Build DID document - let diddoc = gen_diddoc(authentication_key, assertion_key, agreement_key)?; + let diddoc = gen_diddoc( + storage_dirpath, + server_public_domain, + authentication_key, + assertion_key, + agreement_key, + )?; // Mark successful completion tracing::debug!("successful completion"); @@ -59,6 +64,8 @@ pub fn didgen() -> Result { /// Builds and persists DID document fn gen_diddoc( + storage_dirpath: &str, + server_public_domain: &str, authentication_key: Jwk, assertion_key: Jwk, agreement_key: Jwk, @@ -67,9 +74,7 @@ fn gen_diddoc( // Prepare DID address - let public_domain = std::env::var("SERVER_PUBLIC_DOMAIN") // - .map_err(|_| Error::MissingServerPublicDomain)?; - let did = didweb::url_to_did_web_id(&public_domain) // + let did = didweb::url_to_did_web_id(server_public_domain) .map_err(|_| Error::DidAddressDerivationError)?; // Prepare authentication verification method @@ -110,7 +115,7 @@ fn gen_diddoc( let service = Service::new( did.clone() + "#pop-domain", String::from("LinkedDomains"), - format!("{public_domain}/.well-known/did/pop.json"), + format!("{server_public_domain}/.well-known/did/pop.json"), ); // Build document @@ -143,7 +148,8 @@ fn gen_diddoc( let did_json = serde_json::to_string_pretty(&doc).unwrap(); - std::fs::write(format!("{DIDDOC_DIR}/did.json"), &did_json) + std::fs::create_dir_all(storage_dirpath).map_err(|_| Error::PersistenceError)?; + std::fs::write(format!("{storage_dirpath}/did.json"), &did_json) .map_err(|_| Error::PersistenceError)?; tracing::info!("persisted DID document to disk"); @@ -151,17 +157,17 @@ fn gen_diddoc( } /// Validates the integrity of the persisted diddoc -pub fn validate_diddoc() -> Result<(), String> { +pub fn validate_diddoc(storage_dirpath: &str) -> Result<(), String> { // Validate that did.json exists - let didpath = format!("{DIDDOC_DIR}/did.json"); + let didpath = format!("{storage_dirpath}/did.json"); if !Path::new(&didpath).exists() { return Err(String::from("Missing did.json")); }; // Validate that keystore exists - let store = KeyStore::latest(); + let store = KeyStore::latest(storage_dirpath); if store.is_none() { return Err(String::from("Missing keystore")); } diff --git a/did-endpoint/src/lib.rs b/did-endpoint/src/lib.rs index 7d12d9af..4af02d64 100644 --- a/did-endpoint/src/lib.rs +++ b/did-endpoint/src/lib.rs @@ -1,14 +1,46 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +pub mod didgen; +mod util; +mod web; + +use axum::Router; +use generic_server::plugin::traits::{Plugin, PluginError}; + +#[derive(Default)] +pub struct DidPopPlugin; + +impl Plugin for DidPopPlugin { + fn name(&self) -> &'static str { + "didpop" + } + + fn mount(&self) -> Result<(), PluginError> { + let storage_dirpath = std::env::var("STORAGE_DIRPATH").map_err(|_| { + tracing::error!("STORAGE_DIRPATH env variable required"); + PluginError::InitError + })?; -#[cfg(test)] -mod tests { - use super::*; + if didgen::validate_diddoc(&storage_dirpath).is_err() { + tracing::debug!("diddoc validation failed, will generate one"); + + let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").map_err(|_| { + tracing::error!("SERVER_PUBLIC_DOMAIN env variable required"); + PluginError::InitError + })?; + + didgen::didgen(&storage_dirpath, &server_public_domain).map_err(|_| { + tracing::error!("failed to generate an initial keystore and its DID document"); + PluginError::InitError + })?; + }; + + Ok(()) + } + + fn unmount(&self) -> Result<(), PluginError> { + Ok(()) + } - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn routes(&self) -> Router { + web::routes() } } diff --git a/generic-server/src/plugin/didpop/util/didweb.rs b/did-endpoint/src/util/didweb.rs similarity index 100% rename from generic-server/src/plugin/didpop/util/didweb.rs rename to did-endpoint/src/util/didweb.rs diff --git a/generic-server/src/plugin/didpop/util/keystore.rs b/did-endpoint/src/util/keystore.rs similarity index 66% rename from generic-server/src/plugin/didpop/util/keystore.rs rename to did-endpoint/src/util/keystore.rs index 5c8ef39b..7f9d65a4 100644 --- a/generic-server/src/plugin/didpop/util/keystore.rs +++ b/did-endpoint/src/util/keystore.rs @@ -1,37 +1,38 @@ +use chrono::Utc; use did_utils::{ crypto::{ed25519::Ed25519KeyPair, traits::Generate, x25519::X25519KeyPair}, didcore::Jwk, }; use std::error::Error; -use crate::plugin::didpop::KEYSTORE_DIR; - pub struct KeyStore { - path: String, + dirpath: String, + filename: String, keys: Vec, } -struct KeyStoreFactory { - location: String, -} - -impl KeyStoreFactory { - fn create(&self) -> KeyStore { - KeyStore { - path: format!("{}/{}.json", self.location, chrono::Utc::now().timestamp()), +impl KeyStore { + /// Constructs file-based key-value store. + pub fn new(storage_dirpath: &str) -> Self { + Self { + dirpath: format!("{storage_dirpath}/keystore"), + filename: format!("{}.json", Utc::now().timestamp()), keys: vec![], } } - fn latest(&self) -> Option { + /// Returns latest store on disk, if any. + pub fn latest(storage_dirpath: &str) -> Option { + let dirpath = format!("{storage_dirpath}/keystore"); + let msg = "Error parsing keystore directory"; - let file = std::fs::read_dir(&self.location) + let file = std::fs::read_dir(&dirpath) .expect(msg) .map(|x| x.expect(msg).path().to_str().expect(msg).to_string()) .filter(|p| p.ends_with(".json")) .max_by_key(|p| { let p = p - .trim_start_matches(&format!("{}/", self.location)) + .trim_start_matches(&format!("{}/", &dirpath)) .trim_end_matches(".json"); p.parse::().expect(msg) }); @@ -42,39 +43,31 @@ impl KeyStoreFactory { Err(_) => None, Ok(content) => match serde_json::from_str::>(&content) { Err(_) => None, - Ok(keys) => Some(KeyStore { path, keys }), + Ok(keys) => { + let filename = path + .trim_start_matches(&format!("{}/", &dirpath)) + .to_string(); + + Some(KeyStore { + dirpath, + filename, + keys, + }) + } }, }, } } -} - -impl KeyStore { - /// Constructs file-based key-value store. - pub fn new() -> Self { - Self::factory(KEYSTORE_DIR).create() - } - - /// Returns latest store on disk, if any. - pub fn latest() -> Option { - Self::factory(KEYSTORE_DIR).latest() - } - - /// Returns location-aware factory - fn factory(location: &str) -> KeyStoreFactory { - KeyStoreFactory { - location: location.to_string(), - } - } /// Gets path pub fn path(&self) -> String { - self.path.clone() + format!("{}/{}", self.dirpath, self.filename) } /// Persists store on disk fn persist(&self) -> std::io::Result<()> { - std::fs::write(self.path.clone(), serde_json::to_string_pretty(&self.keys)?) + std::fs::create_dir_all(&self.dirpath)?; + std::fs::write(self.path(), serde_json::to_string_pretty(&self.keys)?) } /// Searches keypair given public key @@ -113,12 +106,6 @@ impl KeyStore { } } -impl Default for KeyStore { - fn default() -> Self { - Self::new() - } -} - trait ToPublic { fn to_public(&self) -> Self; } @@ -135,21 +122,15 @@ impl ToPublic for Jwk { #[cfg(test)] mod tests { use super::*; - use crate::util::crate_name; - use tempdir::TempDir; - - impl KeyStore { - fn destroy(self) { - let _ = std::fs::remove_file(self.path); - } - } #[test] fn test_keystore_flow() { - let location = TempDir::new(&crate_name()).unwrap(); - let factory = KeyStore::factory(location.path().to_str().unwrap()); + dotenv_flow::dotenv_flow().ok(); + let storage_dirpath = std::env::var("STORAGE_DIRPATH") + .map(|p| format!("{}/{}", p, uuid::Uuid::new_v4())) + .unwrap(); - let mut store = factory.create(); + let mut store = KeyStore::new(&storage_dirpath); let jwk = store.gen_ed25519_jwk().unwrap(); assert!(store.find_keypair(&jwk).is_some()); @@ -157,9 +138,10 @@ mod tests { let jwk = store.gen_x25519_jwk().unwrap(); assert!(store.find_keypair(&jwk).is_some()); - let latest = factory.latest(); + let latest = KeyStore::latest(&storage_dirpath); assert!(latest.is_some()); - store.destroy(); + // cleanup + std::fs::remove_dir_all(&storage_dirpath).unwrap(); } } diff --git a/generic-server/src/plugin/didpop/util/mod.rs b/did-endpoint/src/util/mod.rs similarity index 100% rename from generic-server/src/plugin/didpop/util/mod.rs rename to did-endpoint/src/util/mod.rs diff --git a/generic-server/src/plugin/didpop/web.rs b/did-endpoint/src/web.rs similarity index 91% rename from generic-server/src/plugin/didpop/web.rs rename to did-endpoint/src/web.rs index 280993c9..5660eefb 100644 --- a/generic-server/src/plugin/didpop/web.rs +++ b/did-endpoint/src/web.rs @@ -14,7 +14,7 @@ use multibase::Base; use serde_json::{json, Value}; use std::collections::HashMap; -use crate::plugin::didpop::{util::KeyStore, DIDDOC_DIR}; +use crate::util::KeyStore; const DEFAULT_CONTEXT_V2: &str = "https://www.w3.org/ns/credentials/v2"; @@ -25,7 +25,12 @@ pub fn routes() -> Router { } pub async fn diddoc() -> Result, StatusCode> { - match tokio::fs::read_to_string(&format!("{DIDDOC_DIR}/did.json")).await { + let storage_dirpath = std::env::var("STORAGE_DIRPATH").map_err(|_| { + tracing::error!("STORAGE_DIRPATH env variable required"); + StatusCode::NOT_FOUND + })?; + + match tokio::fs::read_to_string(&format!("{storage_dirpath}/did.json")).await { Ok(content) => Ok(Json(serde_json::from_str(&content).unwrap())), Err(_) => Err(StatusCode::NOT_FOUND), } @@ -35,7 +40,14 @@ pub async fn didpop( Query(params): Query>, ) -> Result, StatusCode> { let challenge = params.get("challenge").ok_or(StatusCode::BAD_REQUEST)?; - let keystore = KeyStore::latest().expect("Keystore file probably missing"); + + // Retrieve keystore + + let storage_dirpath = std::env::var("STORAGE_DIRPATH").map_err(|_| { + tracing::error!("STORAGE_DIRPATH env variable required"); + StatusCode::NOT_FOUND + })?; + let keystore = KeyStore::latest(&storage_dirpath).expect("Keystore file probably missing"); // Load DID document and its verification methods @@ -164,7 +176,8 @@ fn inspect_vm_relationship(diddoc: &Document, vm_id: &str) -> Option { #[cfg(test)] mod tests { - use crate::app; + use super::*; + use axum::{ body::Body, http::{Request, StatusCode}, @@ -179,7 +192,7 @@ mod tests { #[tokio::test] async fn verify_didpop() { - let app = app(); + let app = routes(); let response = app .oneshot( diff --git a/generic-server/Cargo.toml b/generic-server/Cargo.toml index 016fe3bc..ee95f2f5 100644 --- a/generic-server/Cargo.toml +++ b/generic-server/Cargo.toml @@ -14,20 +14,12 @@ dotenv-flow = "0.15.0" hyper = { version = "0.14.27", features = ["full"] } lazy_static = "1.4.0" serde_json = "1.0.104" -thiserror = "1.0.49" tokio = { version = "1.30.0", features = ["full"] } tower-http = { version = "0.4.3", features = ["catch-panic", "trace"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["json"] } -# didpop -did-utils = { path = "../did-utils", optional = true } -multibase = { version = "0.8.0", optional = true } # earlier version due to 'did-utils' -url = { version = "2.4.0", optional = true } -uuid = { version = "1.4.1", features = ["v4"], optional = true } - [dev-dependencies] -tempdir = "0.3.7" tower = { version = "0.4.13", features = ["util"] } [features] @@ -35,4 +27,4 @@ default = ["plugin-index", "plugin-didpop"] # plugins plugin-index = ["dep:chrono"] -plugin-didpop = ["dep:chrono", "dep:did-utils", "dep:multibase", "dep:url", "dep:uuid"] +plugin-didpop = [] diff --git a/generic-server/src/bin/didgen.rs b/generic-server/src/bin/didgen similarity index 100% rename from generic-server/src/bin/didgen.rs rename to generic-server/src/bin/didgen diff --git a/generic-server/src/plugin/didpop/mod.rs b/generic-server/src/plugin/didpop/mod.rs deleted file mode 100644 index aad31514..00000000 --- a/generic-server/src/plugin/didpop/mod.rs +++ /dev/null @@ -1,38 +0,0 @@ -pub mod didgen; -mod util; -mod web; - -use super::traits::{Plugin, PluginError}; -use axum::Router; - -pub const DIDDOC_DIR: &str = "storage"; -pub const KEYSTORE_DIR: &str = "storage/keystore"; - -#[derive(Default)] -pub struct DidPopPlugin; - -impl Plugin for DidPopPlugin { - fn name(&self) -> &'static str { - "didpop" - } - - fn mount(&self) -> Result<(), PluginError> { - if didgen::validate_diddoc().is_err() { - didgen::didgen().map_err(|_| { - tracing::error!("failed to generate an initial keystore and its DID document"); - - PluginError::InitError - })?; - }; - - Ok(()) - } - - fn unmount(&self) -> Result<(), PluginError> { - Ok(()) - } - - fn routes(&self) -> Router { - web::routes() - } -} diff --git a/generic-server/src/plugin/index/web.rs b/generic-server/src/plugin/index/web.rs index 53d4319d..abcd79fb 100644 --- a/generic-server/src/plugin/index/web.rs +++ b/generic-server/src/plugin/index/web.rs @@ -21,7 +21,9 @@ pub async fn index() -> Json { #[cfg(test)] mod tests { - use crate::{app, util::crate_name}; + use super::*; + use crate::util::crate_name; + use axum::{ body::Body, http::{Request, StatusCode}, @@ -31,7 +33,7 @@ mod tests { #[tokio::test] async fn index() { - let app = app(); + let app = routes(); let response = app .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap()) diff --git a/generic-server/src/plugin/mod.rs b/generic-server/src/plugin/mod.rs index 634d5138..992acc6a 100644 --- a/generic-server/src/plugin/mod.rs +++ b/generic-server/src/plugin/mod.rs @@ -4,16 +4,17 @@ pub mod traits; use lazy_static::lazy_static; use traits::Plugin; -#[cfg(feature = "plugin-didpop")] -pub mod didpop; #[cfg(feature = "plugin-index")] mod index; +// #[cfg(feature = "plugin-didpop")] +// pub mod didpop; + lazy_static! { pub static ref PLUGINS: Vec> = vec![ #[cfg(feature = "plugin-index")] Box::::default(), - #[cfg(feature = "plugin-didpop")] - Box::::default(), + // #[cfg(feature = "plugin-didpop")] + // Box::::default(), ]; } From 9fa8d22460cc80ea8b2e169e108fa00cc3130544 Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Thu, 5 Oct 2023 11:05:54 +0100 Subject: [PATCH 06/13] Improve didpop testing --- did-endpoint/Cargo.toml | 1 + did-endpoint/src/didgen.rs | 12 ++++++------ did-endpoint/src/web.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/did-endpoint/Cargo.toml b/did-endpoint/Cargo.toml index 13259e28..aeeeba6a 100644 --- a/did-endpoint/Cargo.toml +++ b/did-endpoint/Cargo.toml @@ -26,5 +26,6 @@ uuid = { version = "1.4.1", features = ["v4"] } [dev-dependencies] +json-canon = "0.1.3" tempdir = "0.3.7" tower = { version = "0.4.13", features = ["util"] } diff --git a/did-endpoint/src/didgen.rs b/did-endpoint/src/didgen.rs index a8f93be9..7bb7fb64 100644 --- a/did-endpoint/src/didgen.rs +++ b/did-endpoint/src/didgen.rs @@ -25,7 +25,7 @@ pub enum Error { /// Generates keys and forward them for DID generation /// /// All persistence is handled at `storage_dirpath`. -pub fn didgen(storage_dirpath: &str, server_public_domain: &str) -> Result { +pub fn didgen(storage_dirpath: &str, server_public_domain: &str) -> Result { // Create a new store, which is timestamp-aware let mut store = KeyStore::new(storage_dirpath); tracing::info!("keystore: {}", store.path()); @@ -69,7 +69,7 @@ fn gen_diddoc( authentication_key: Jwk, assertion_key: Jwk, agreement_key: Jwk, -) -> Result { +) -> Result { tracing::info!("building DID document"); // Prepare DID address @@ -125,7 +125,7 @@ fn gen_diddoc( String::from("https://w3id.org/security/suites/jws-2020/v1"), ]); - let doc = Document { + let diddoc = Document { authentication: Some(vec![Authentication::Reference( authentication_method.id.clone(), // )]), @@ -146,14 +146,14 @@ fn gen_diddoc( // Serialize and persist to file - let did_json = serde_json::to_string_pretty(&doc).unwrap(); + let did_json = serde_json::to_string_pretty(&diddoc).unwrap(); std::fs::create_dir_all(storage_dirpath).map_err(|_| Error::PersistenceError)?; - std::fs::write(format!("{storage_dirpath}/did.json"), &did_json) + std::fs::write(format!("{storage_dirpath}/did.json"), did_json) .map_err(|_| Error::PersistenceError)?; tracing::info!("persisted DID document to disk"); - Ok(did_json) + Ok(diddoc) } /// Validates the integrity of the persisted diddoc diff --git a/did-endpoint/src/web.rs b/did-endpoint/src/web.rs index 5660eefb..c55c9838 100644 --- a/did-endpoint/src/web.rs +++ b/did-endpoint/src/web.rs @@ -177,6 +177,7 @@ fn inspect_vm_relationship(diddoc: &Document, vm_id: &str) -> Option { #[cfg(test)] mod tests { use super::*; + use crate::didgen; use axum::{ body::Body, @@ -192,8 +193,10 @@ mod tests { #[tokio::test] async fn verify_didpop() { - let app = routes(); + // Generate test-restricted did.json + let (storage_dirpath, expected_diddoc) = gen_ephemeral_diddoc(); + let app = routes(); let response = app .oneshot( Request::builder() @@ -215,6 +218,11 @@ mod tests { let vc = vp.verifiable_credential.get(0).unwrap(); let diddoc = serde_json::from_value(json!(vc.credential_subject)).unwrap(); + assert_eq!( + json_canon::to_string(&diddoc).unwrap(), + json_canon::to_string(&expected_diddoc).unwrap() + ); + let Some(proofs) = &vp.proof else { panic!("Verifiable presentation carries no proof") }; let Proofs::SetOfProofs(proofs) = proofs else { unreachable!() }; for proof in proofs { @@ -228,6 +236,23 @@ mod tests { assert!(verifier.verify(json!(vp)).is_ok()); } + + // cleanup + std::fs::remove_dir_all(&storage_dirpath).unwrap(); + } + + fn gen_ephemeral_diddoc() -> (String, Document) { + dotenv_flow::dotenv_flow().ok(); + + let storage_dirpath = std::env::var("STORAGE_DIRPATH") + .map(|p| format!("{}/{}", p, uuid::Uuid::new_v4())) + .unwrap(); + let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").unwrap(); + + let diddoc = didgen::didgen(&storage_dirpath, &server_public_domain).unwrap(); + + std::env::set_var("STORAGE_DIRPATH", &storage_dirpath); + (storage_dirpath, diddoc) } fn resolve_vm_for_public_key(diddoc: &Document, vm_id: &str) -> Option { From 02738b2438eaecbd5be885cd72e09a71b3a072eb Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Thu, 5 Oct 2023 11:10:43 +0100 Subject: [PATCH 07/13] Remove unused dependencies --- did-endpoint/Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/did-endpoint/Cargo.toml b/did-endpoint/Cargo.toml index aeeeba6a..c6a5754a 100644 --- a/did-endpoint/Cargo.toml +++ b/did-endpoint/Cargo.toml @@ -18,14 +18,11 @@ multibase = { version = "0.8.0" } # earlier version due to 'did-utils' serde_json = "1.0.104" thiserror = "1.0.49" tokio = { version = "1.30.0", features = ["full"] } -tower-http = { version = "0.4.3", features = ["catch-panic", "trace"] } +tower-http = { version = "0.4.3" } tracing = "0.1.37" -tracing-subscriber = { version = "0.3.17", features = ["json"] } url = { version = "2.4.0" } uuid = { version = "1.4.1", features = ["v4"] } - [dev-dependencies] json-canon = "0.1.3" -tempdir = "0.3.7" tower = { version = "0.4.13", features = ["util"] } From ca790842bcb4c80012ab4a36ca8d6285f677a656 Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Thu, 5 Oct 2023 12:27:12 +0100 Subject: [PATCH 08/13] Charter did-enpoint plugin --- did-endpoint/Cargo.toml | 1 - did-endpoint/src/lib.rs | 46 +------------------ did-endpoint/src/web.rs | 4 +- generic-server/.env | 2 + generic-server/.gitignore | 3 +- generic-server/Cargo.toml | 9 ++-- generic-server/README.md | 5 +- generic-server/src/bin/didgen | 27 ----------- generic-server/src/bin/didgen.rs | 29 ++++++++++++ generic-server/src/plugin/did_endpoint/mod.rs | 44 ++++++++++++++++++ generic-server/src/plugin/mod.rs | 8 ++-- generic-server/storage/.gitignore | 3 -- generic-server/storage/keystore/.gitkeep | 0 13 files changed, 93 insertions(+), 88 deletions(-) delete mode 100644 generic-server/src/bin/didgen create mode 100644 generic-server/src/bin/didgen.rs create mode 100644 generic-server/src/plugin/did_endpoint/mod.rs delete mode 100644 generic-server/storage/.gitignore delete mode 100644 generic-server/storage/keystore/.gitkeep diff --git a/did-endpoint/Cargo.toml b/did-endpoint/Cargo.toml index c6a5754a..7ca3173e 100644 --- a/did-endpoint/Cargo.toml +++ b/did-endpoint/Cargo.toml @@ -11,7 +11,6 @@ axum = { version = "0.6.20" } chrono = { version = "0.4.26" } did-utils = { path = "../did-utils"} dotenv-flow = "0.15.0" -generic-server = { path = "../generic-server", default-features = false } hyper = { version = "0.14.27", features = ["full"] } lazy_static = "1.4.0" multibase = { version = "0.8.0" } # earlier version due to 'did-utils' diff --git a/did-endpoint/src/lib.rs b/did-endpoint/src/lib.rs index 4af02d64..a53cdfe1 100644 --- a/did-endpoint/src/lib.rs +++ b/did-endpoint/src/lib.rs @@ -1,46 +1,4 @@ pub mod didgen; -mod util; -mod web; - -use axum::Router; -use generic_server::plugin::traits::{Plugin, PluginError}; - -#[derive(Default)] -pub struct DidPopPlugin; - -impl Plugin for DidPopPlugin { - fn name(&self) -> &'static str { - "didpop" - } - - fn mount(&self) -> Result<(), PluginError> { - let storage_dirpath = std::env::var("STORAGE_DIRPATH").map_err(|_| { - tracing::error!("STORAGE_DIRPATH env variable required"); - PluginError::InitError - })?; - - if didgen::validate_diddoc(&storage_dirpath).is_err() { - tracing::debug!("diddoc validation failed, will generate one"); +pub mod web; - let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").map_err(|_| { - tracing::error!("SERVER_PUBLIC_DOMAIN env variable required"); - PluginError::InitError - })?; - - didgen::didgen(&storage_dirpath, &server_public_domain).map_err(|_| { - tracing::error!("failed to generate an initial keystore and its DID document"); - PluginError::InitError - })?; - }; - - Ok(()) - } - - fn unmount(&self) -> Result<(), PluginError> { - Ok(()) - } - - fn routes(&self) -> Router { - web::routes() - } -} +mod util; diff --git a/did-endpoint/src/web.rs b/did-endpoint/src/web.rs index c55c9838..01b8e9e6 100644 --- a/did-endpoint/src/web.rs +++ b/did-endpoint/src/web.rs @@ -24,7 +24,7 @@ pub fn routes() -> Router { .route("/.well-known/did/pop.json", get(didpop)) } -pub async fn diddoc() -> Result, StatusCode> { +async fn diddoc() -> Result, StatusCode> { let storage_dirpath = std::env::var("STORAGE_DIRPATH").map_err(|_| { tracing::error!("STORAGE_DIRPATH env variable required"); StatusCode::NOT_FOUND @@ -36,7 +36,7 @@ pub async fn diddoc() -> Result, StatusCode> { } } -pub async fn didpop( +async fn didpop( Query(params): Query>, ) -> Result, StatusCode> { let challenge = params.get("challenge").ok_or(StatusCode::BAD_REQUEST)?; diff --git a/generic-server/.env b/generic-server/.env index 29daf860..4baaa49c 100644 --- a/generic-server/.env +++ b/generic-server/.env @@ -3,3 +3,5 @@ SERVER_PUBLIC_DOMAIN=https://example.com SERVER_LOCAL_PORT=3000 + +STORAGE_DIRPATH="storage" diff --git a/generic-server/.gitignore b/generic-server/.gitignore index 3a8fe5ed..fc159a02 100644 --- a/generic-server/.gitignore +++ b/generic-server/.gitignore @@ -1 +1,2 @@ -.env.local \ No newline at end of file +storage* +.env.local diff --git a/generic-server/Cargo.toml b/generic-server/Cargo.toml index ee95f2f5..2ab4e9f8 100644 --- a/generic-server/Cargo.toml +++ b/generic-server/Cargo.toml @@ -9,7 +9,6 @@ default-run = "generic-server" [dependencies] async-trait = "0.1.73" axum = { version = "0.6.20" } -chrono = { version = "0.4.26", optional = true } dotenv-flow = "0.15.0" hyper = { version = "0.14.27", features = ["full"] } lazy_static = "1.4.0" @@ -19,12 +18,16 @@ tower-http = { version = "0.4.3", features = ["catch-panic", "trace"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["json"] } +# optional +chrono = { version = "0.4.26", optional = true } +did-endpoint = { path = "../did-endpoint", optional = true } + [dev-dependencies] tower = { version = "0.4.13", features = ["util"] } [features] -default = ["plugin-index", "plugin-didpop"] +default = ["plugin-index", "plugin-did_endpoint"] # plugins plugin-index = ["dep:chrono"] -plugin-didpop = [] +plugin-did_endpoint = ["dep:did-endpoint"] diff --git a/generic-server/README.md b/generic-server/README.md index 9849bd5e..d11c06c4 100644 --- a/generic-server/README.md +++ b/generic-server/README.md @@ -1,3 +1,2 @@ -# sample-pop-server -This project builds a sample did:web-compatible server that can prove -possession of the secrets paired with its DID document. +# generic-server +This server aggregates features provided by configurable plugins. diff --git a/generic-server/src/bin/didgen b/generic-server/src/bin/didgen deleted file mode 100644 index 1b611764..00000000 --- a/generic-server/src/bin/didgen +++ /dev/null @@ -1,27 +0,0 @@ -#[cfg(feature = "plugin-didpop")] -use generic_server::plugin::didpop::didgen; - -/// Program entry -fn main() -> Result<(), Box> { - #[cfg(feature = "plugin-didpop")] - { - use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - - // Load dotenv-flow variables - dotenv_flow::dotenv_flow().ok(); - - // Enable logging - tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer()) - .init(); - - // Run didgen logic - didgen::didgen()?; - Ok(()) - } - - #[cfg(not(feature = "plugin-didpop"))] - { - Err("You must enable `plugin-didpop` to run this command.".into()) - } -} diff --git a/generic-server/src/bin/didgen.rs b/generic-server/src/bin/didgen.rs new file mode 100644 index 00000000..7055447b --- /dev/null +++ b/generic-server/src/bin/didgen.rs @@ -0,0 +1,29 @@ +#[cfg(feature = "plugin-did_endpoint")] +use did_endpoint::didgen; + +/// Program entry +fn main() -> Result<(), Box> { + #[cfg(feature = "plugin-did_endpoint")] + { + use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + + // Load dotenv-flow variables + dotenv_flow::dotenv_flow().ok(); + + // Enable logging + tracing_subscriber::registry() + .with(tracing_subscriber::fmt::layer()) + .init(); + + // Run didgen logic + let storage_dirpath = std::env::var("STORAGE_DIRPATH")?; + let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN")?; + didgen::didgen(&storage_dirpath, &server_public_domain)?; + Ok(()) + } + + #[cfg(not(feature = "plugin-did_endpoint"))] + { + Err("You must enable `plugin-did_endpoint` to run this command.".into()) + } +} diff --git a/generic-server/src/plugin/did_endpoint/mod.rs b/generic-server/src/plugin/did_endpoint/mod.rs new file mode 100644 index 00000000..f6d0efc3 --- /dev/null +++ b/generic-server/src/plugin/did_endpoint/mod.rs @@ -0,0 +1,44 @@ +use axum::Router; +use did_endpoint::{didgen, web}; + +use super::traits::{Plugin, PluginError}; + +#[derive(Default)] +pub struct DidEndpointPlugin; + +impl Plugin for DidEndpointPlugin { + fn name(&self) -> &'static str { + "did_endpoint" + } + + fn mount(&self) -> Result<(), PluginError> { + let storage_dirpath = std::env::var("STORAGE_DIRPATH").map_err(|_| { + tracing::error!("STORAGE_DIRPATH env variable required"); + PluginError::InitError + })?; + + if didgen::validate_diddoc(&storage_dirpath).is_err() { + tracing::debug!("diddoc validation failed, will generate one"); + + let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").map_err(|_| { + tracing::error!("SERVER_PUBLIC_DOMAIN env variable required"); + PluginError::InitError + })?; + + didgen::didgen(&storage_dirpath, &server_public_domain).map_err(|_| { + tracing::error!("failed to generate an initial keystore and its DID document"); + PluginError::InitError + })?; + }; + + Ok(()) + } + + fn unmount(&self) -> Result<(), PluginError> { + Ok(()) + } + + fn routes(&self) -> Router { + web::routes() + } +} diff --git a/generic-server/src/plugin/mod.rs b/generic-server/src/plugin/mod.rs index 992acc6a..78c7282a 100644 --- a/generic-server/src/plugin/mod.rs +++ b/generic-server/src/plugin/mod.rs @@ -7,14 +7,14 @@ use traits::Plugin; #[cfg(feature = "plugin-index")] mod index; -// #[cfg(feature = "plugin-didpop")] -// pub mod didpop; +#[cfg(feature = "plugin-did_endpoint")] +mod did_endpoint; lazy_static! { pub static ref PLUGINS: Vec> = vec![ #[cfg(feature = "plugin-index")] Box::::default(), - // #[cfg(feature = "plugin-didpop")] - // Box::::default(), + #[cfg(feature = "plugin-did_endpoint")] + Box::::default(), ]; } diff --git a/generic-server/storage/.gitignore b/generic-server/storage/.gitignore deleted file mode 100644 index 2b1de7e7..00000000 --- a/generic-server/storage/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -did.json -keystore/* -!keystore/.gitkeep diff --git a/generic-server/storage/keystore/.gitkeep b/generic-server/storage/keystore/.gitkeep deleted file mode 100644 index e69de29b..00000000 From 2695f771f49311388e9099993b18cc01f30f8fac Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Thu, 5 Oct 2023 13:38:46 +0100 Subject: [PATCH 09/13] Provide additional testing for didgen --- did-endpoint/src/didgen.rs | 129 ++++++++++++++++++++++++++++++ did-endpoint/src/util/keystore.rs | 19 +++-- did-endpoint/src/util/mod.rs | 8 ++ did-endpoint/src/web.rs | 46 ++++++----- 4 files changed, 175 insertions(+), 27 deletions(-) diff --git a/did-endpoint/src/didgen.rs b/did-endpoint/src/didgen.rs index 7bb7fb64..1af0dc76 100644 --- a/did-endpoint/src/didgen.rs +++ b/did-endpoint/src/didgen.rs @@ -197,3 +197,132 @@ pub fn validate_diddoc(storage_dirpath: &str) -> Result<(), String> { Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::util::dotenv_flow_read; + + fn setup() -> (String, String) { + let storage_dirpath = dotenv_flow_read("STORAGE_DIRPATH") + .map(|p| format!("{}/{}", p, uuid::Uuid::new_v4())) + .unwrap(); + + let server_public_domain = dotenv_flow_read("SERVER_PUBLIC_DOMAIN").unwrap(); + + (storage_dirpath, server_public_domain) + } + + fn cleanup(storage_dirpath: &str) { + std::fs::remove_dir_all(storage_dirpath).unwrap(); + } + + // Verifies that the didgen function returns a DID document. + // Does not validate the DID document. + #[test] + fn test_didgen() { + let (storage_dirpath, server_public_domain) = setup(); + + let diddoc = didgen(&storage_dirpath, &server_public_domain).unwrap(); + assert_eq!(diddoc.id, "did:web:example.com"); + + cleanup(&storage_dirpath); + } + + // Produces did doc from keys and validate that corresponding verification methods are present. + #[test] + fn test_gen_diddoc() { + let (storage_dirpath, server_public_domain) = setup(); + + let authentication_key = Jwk { + key_id: None, + key_type: String::from("OKP"), + curve: String::from("Ed25519"), + x: Some(String::from( + "d75a980182b10ab2463c5b1be1b4d97e06ec21ebac8552059996bd962d77f259", + )), + y: None, + d: None, + }; + + let assertion_key = Jwk { + key_id: None, + key_type: String::from("OKP"), + curve: String::from("Ed25519"), + x: Some(String::from( + "d75a980182b10ab2463c5b1be1b4d97e06ec21ebac8552059996bd962d77f259", + )), + y: None, + d: None, + }; + + let agreement_key = Jwk { + key_id: None, + key_type: String::from("OKP"), + curve: String::from("X25519"), + x: Some(String::from( + "d75a980182b10ab2463c5b1be1b4d97e06ec21ebac8552059996bd962d77f259", + )), + y: None, + d: None, + }; + + let diddoc = gen_diddoc( + &storage_dirpath, + &server_public_domain, + authentication_key.clone(), + assertion_key.clone(), + agreement_key.clone(), + ) + .unwrap(); + + // Verify that the DID contains exactly the defined verification methods. + let expected_verification_methods = vec![ + VerificationMethod { + id: "did:web:example.com#keys-1".to_string(), + public_key: Some(KeyFormat::Jwk(authentication_key)), + ..VerificationMethod::new( + "did:web:example.com#keys-1".to_string(), + String::from("JsonWebKey2020"), + "did:web:example.com".to_string(), + ) + }, + VerificationMethod { + id: "did:web:example.com#keys-2".to_string(), + public_key: Some(KeyFormat::Jwk(assertion_key)), + ..VerificationMethod::new( + "did:web:example.com#keys-2".to_string(), + String::from("JsonWebKey2020"), + "did:web:example.com".to_string(), + ) + }, + VerificationMethod { + id: "did:web:example.com#keys-3".to_string(), + public_key: Some(KeyFormat::Jwk(agreement_key)), + ..VerificationMethod::new( + "did:web:example.com#keys-3".to_string(), + String::from("JsonWebKey2020"), + "did:web:example.com".to_string(), + ) + }, + ]; + + let actual_verification_methods = diddoc.verification_method.unwrap(); + + let actual = json_canon::to_string(&actual_verification_methods).unwrap(); + let expected = json_canon::to_string(&expected_verification_methods).unwrap(); + assert_eq!(expected, actual); + + cleanup(&storage_dirpath); + } + + #[test] + fn test_validate_diddoc() { + let (storage_dirpath, server_public_domain) = setup(); + + didgen(&storage_dirpath, &server_public_domain).unwrap(); + assert!(validate_diddoc(&storage_dirpath).is_ok()); + + cleanup(&storage_dirpath); + } +} diff --git a/did-endpoint/src/util/keystore.rs b/did-endpoint/src/util/keystore.rs index 7f9d65a4..10fec9db 100644 --- a/did-endpoint/src/util/keystore.rs +++ b/did-endpoint/src/util/keystore.rs @@ -122,13 +122,21 @@ impl ToPublic for Jwk { #[cfg(test)] mod tests { use super::*; + use crate::util::dotenv_flow_read; + + fn setup() -> String { + dotenv_flow_read("STORAGE_DIRPATH") + .map(|p| format!("{}/{}", p, uuid::Uuid::new_v4())) + .unwrap() + } + + fn cleanup(storage_dirpath: &str) { + std::fs::remove_dir_all(storage_dirpath).unwrap(); + } #[test] fn test_keystore_flow() { - dotenv_flow::dotenv_flow().ok(); - let storage_dirpath = std::env::var("STORAGE_DIRPATH") - .map(|p| format!("{}/{}", p, uuid::Uuid::new_v4())) - .unwrap(); + let storage_dirpath = setup(); let mut store = KeyStore::new(&storage_dirpath); @@ -141,7 +149,6 @@ mod tests { let latest = KeyStore::latest(&storage_dirpath); assert!(latest.is_some()); - // cleanup - std::fs::remove_dir_all(&storage_dirpath).unwrap(); + cleanup(&storage_dirpath); } } diff --git a/did-endpoint/src/util/mod.rs b/did-endpoint/src/util/mod.rs index e649243c..aaa9f56b 100644 --- a/did-endpoint/src/util/mod.rs +++ b/did-endpoint/src/util/mod.rs @@ -2,3 +2,11 @@ pub mod didweb; pub mod keystore; pub use keystore::KeyStore; + +#[cfg(test)] +pub fn dotenv_flow_read(key: &str) -> Option { + dotenv_flow::dotenv_iter().unwrap().find_map(|item| { + let (k, v) = item.unwrap(); + (k == key).then_some(v) + }) +} diff --git a/did-endpoint/src/web.rs b/did-endpoint/src/web.rs index 01b8e9e6..2d7d09cd 100644 --- a/did-endpoint/src/web.rs +++ b/did-endpoint/src/web.rs @@ -36,9 +36,7 @@ async fn diddoc() -> Result, StatusCode> { } } -async fn didpop( - Query(params): Query>, -) -> Result, StatusCode> { +async fn didpop(Query(params): Query>) -> Result, StatusCode> { let challenge = params.get("challenge").ok_or(StatusCode::BAD_REQUEST)?; // Retrieve keystore @@ -177,7 +175,7 @@ fn inspect_vm_relationship(diddoc: &Document, vm_id: &str) -> Option { #[cfg(test)] mod tests { use super::*; - use crate::didgen; + use crate::{didgen, util::dotenv_flow_read}; use axum::{ body::Body, @@ -191,10 +189,31 @@ mod tests { use serde_json::json; use tower::util::ServiceExt; + fn setup_ephemeral_diddoc() -> (String, Document) { + let storage_dirpath = dotenv_flow_read("STORAGE_DIRPATH") + .map(|p| format!("{}/{}", p, uuid::Uuid::new_v4())) + .unwrap(); + + let server_public_domain = dotenv_flow_read("SERVER_PUBLIC_DOMAIN").unwrap(); + + // Run didgen logic + let diddoc = didgen::didgen(&storage_dirpath, &server_public_domain).unwrap(); + + // TODO! Find a race-free way to accomodate this. Maybe a test mutex? + std::env::set_var("STORAGE_DIRPATH", &storage_dirpath); + + (storage_dirpath, diddoc) + } + + fn cleanup(storage_dirpath: &str) { + std::env::remove_var("STORAGE_DIRPATH"); + std::fs::remove_dir_all(storage_dirpath).unwrap(); + } + #[tokio::test] async fn verify_didpop() { // Generate test-restricted did.json - let (storage_dirpath, expected_diddoc) = gen_ephemeral_diddoc(); + let (storage_dirpath, expected_diddoc) = setup_ephemeral_diddoc(); let app = routes(); let response = app @@ -237,22 +256,7 @@ mod tests { assert!(verifier.verify(json!(vp)).is_ok()); } - // cleanup - std::fs::remove_dir_all(&storage_dirpath).unwrap(); - } - - fn gen_ephemeral_diddoc() -> (String, Document) { - dotenv_flow::dotenv_flow().ok(); - - let storage_dirpath = std::env::var("STORAGE_DIRPATH") - .map(|p| format!("{}/{}", p, uuid::Uuid::new_v4())) - .unwrap(); - let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").unwrap(); - - let diddoc = didgen::didgen(&storage_dirpath, &server_public_domain).unwrap(); - - std::env::set_var("STORAGE_DIRPATH", &storage_dirpath); - (storage_dirpath, diddoc) + cleanup(&storage_dirpath); } fn resolve_vm_for_public_key(diddoc: &Document, vm_id: &str) -> Option { From 777b8b5af0b4aa7b86ceb6f344ea9df772e0a9fd Mon Sep 17 00:00:00 2001 From: Ingrid Kamga Date: Thu, 5 Oct 2023 16:45:05 +0100 Subject: [PATCH 10/13] Change default storage dirpath --- did-endpoint/.env | 2 +- did-endpoint/.gitignore | 1 - did-endpoint/Cargo.toml | 1 + did-endpoint/src/util/keystore.rs | 9 +++++++++ generic-server/.env | 2 +- generic-server/.gitignore | 1 - 6 files changed, 12 insertions(+), 4 deletions(-) delete mode 100644 did-endpoint/.gitignore diff --git a/did-endpoint/.env b/did-endpoint/.env index 332923a9..e1888346 100644 --- a/did-endpoint/.env +++ b/did-endpoint/.env @@ -1,2 +1,2 @@ -STORAGE_DIRPATH="storage.test" +STORAGE_DIRPATH="target/storage" SERVER_PUBLIC_DOMAIN="example.com" diff --git a/did-endpoint/.gitignore b/did-endpoint/.gitignore deleted file mode 100644 index 2d5fc243..00000000 --- a/did-endpoint/.gitignore +++ /dev/null @@ -1 +0,0 @@ -storage* diff --git a/did-endpoint/Cargo.toml b/did-endpoint/Cargo.toml index 7ca3173e..815f0021 100644 --- a/did-endpoint/Cargo.toml +++ b/did-endpoint/Cargo.toml @@ -21,6 +21,7 @@ tower-http = { version = "0.4.3" } tracing = "0.1.37" url = { version = "2.4.0" } uuid = { version = "1.4.1", features = ["v4"] } +zeroize = { version = "1.6.0" } [dev-dependencies] json-canon = "0.1.3" diff --git a/did-endpoint/src/util/keystore.rs b/did-endpoint/src/util/keystore.rs index 10fec9db..21987453 100644 --- a/did-endpoint/src/util/keystore.rs +++ b/did-endpoint/src/util/keystore.rs @@ -4,6 +4,7 @@ use did_utils::{ didcore::Jwk, }; use std::error::Error; +use zeroize::Zeroize; pub struct KeyStore { dirpath: String, @@ -106,6 +107,14 @@ impl KeyStore { } } +impl Drop for KeyStore { + fn drop(&mut self) { + for jwk in &mut self.keys { + jwk.d.zeroize(); + }; + } +} + trait ToPublic { fn to_public(&self) -> Self; } diff --git a/generic-server/.env b/generic-server/.env index 4baaa49c..591c498d 100644 --- a/generic-server/.env +++ b/generic-server/.env @@ -4,4 +4,4 @@ SERVER_PUBLIC_DOMAIN=https://example.com SERVER_LOCAL_PORT=3000 -STORAGE_DIRPATH="storage" +STORAGE_DIRPATH="target/storage" diff --git a/generic-server/.gitignore b/generic-server/.gitignore index fc159a02..11ee7581 100644 --- a/generic-server/.gitignore +++ b/generic-server/.gitignore @@ -1,2 +1 @@ -storage* .env.local From 5dcd232757195d6d4955c89d7121f82e7abf33ca Mon Sep 17 00:00:00 2001 From: Francis Pouatcha Date: Thu, 5 Oct 2023 14:40:04 -0400 Subject: [PATCH 11/13] Modified github action to fit project structure --- .github/workflows/rust.yml | 17 +++++++++++++++-- run_test.sh | 10 ++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100755 run_test.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 87d9c5db..eb87209d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,9 +25,22 @@ jobs: cargo build --verbose cargo test --verbose - - name: Build and Test Sample Pop Server + - name: Build and Test Did Endpoint run: | - cd sample-pop-server + cd did-endpoint + cargo build --verbose + cargo test --verbose + + - name: Build and Test Generic Server + run: | + cd generic-server + cargo build --verbose + cargo run --bin didgen + cargo test --verbose + + - name: Build and Mediator Server + run: | + cd mediator-server cargo build --verbose cargo run --bin didgen cargo test --verbose diff --git a/run_test.sh b/run_test.sh new file mode 100755 index 00000000..12d693f5 --- /dev/null +++ b/run_test.sh @@ -0,0 +1,10 @@ +#!/bin/bash +for dir in ./did-utils/ ./did-endpoint/ ./generic-server/ ./mediator-server/ +do + cd "${dir}" + if [ -f Cargo.toml ]; then + echo "Running tests in: ${dir}" + cargo test + fi + cd .. +done \ No newline at end of file From 83f5497403c365e8da608087b29decabcd5eb57b Mon Sep 17 00:00:00 2001 From: Francis Pouatcha Date: Thu, 5 Oct 2023 15:22:57 -0400 Subject: [PATCH 12/13] Fixing github action --- .github/workflows/rust.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index eb87209d..0cf99f6f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,25 +22,23 @@ jobs: - name: Build and Test Did Utils run: | cd did-utils - cargo build --verbose - cargo test --verbose + cargo build + cargo test - name: Build and Test Did Endpoint run: | cd did-endpoint - cargo build --verbose - cargo test --verbose + cargo build + cargo test - name: Build and Test Generic Server run: | cd generic-server - cargo build --verbose - cargo run --bin didgen - cargo test --verbose + cargo build + cargo test - name: Build and Mediator Server run: | cd mediator-server - cargo build --verbose - cargo run --bin didgen - cargo test --verbose + cargo build + cargo test From f392b85d4ae7f944c0c5600c5054d070c7a5ff20 Mon Sep 17 00:00:00 2001 From: Francis Pouatcha Date: Thu, 5 Oct 2023 19:28:24 -0400 Subject: [PATCH 13/13] Moving plugin traits to top module such as to isolate plugin impl. --- did-endpoint/Cargo.toml | 6 ++-- did-endpoint/src/lib.rs | 1 + .../mod.rs => did-endpoint/src/plugin.rs | 5 ++-- generic-server/Cargo.toml | 3 ++ generic-server/src/bin/didgen.rs | 29 ------------------- generic-server/src/plugin/container.rs | 6 ++-- generic-server/src/plugin/index/mod.rs | 3 +- generic-server/src/plugin/mod.rs | 8 ++--- run_test.sh | 2 +- server-plugin/Cargo.toml | 9 ++++++ .../traits.rs => server-plugin/src/lib.rs | 0 11 files changed, 24 insertions(+), 48 deletions(-) rename generic-server/src/plugin/did_endpoint/mod.rs => did-endpoint/src/plugin.rs (93%) delete mode 100644 generic-server/src/bin/didgen.rs create mode 100644 server-plugin/Cargo.toml rename generic-server/src/plugin/traits.rs => server-plugin/src/lib.rs (100%) diff --git a/did-endpoint/Cargo.toml b/did-endpoint/Cargo.toml index 815f0021..e2294428 100644 --- a/did-endpoint/Cargo.toml +++ b/did-endpoint/Cargo.toml @@ -6,23 +6,23 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.73" axum = { version = "0.6.20" } chrono = { version = "0.4.26" } did-utils = { path = "../did-utils"} dotenv-flow = "0.15.0" hyper = { version = "0.14.27", features = ["full"] } -lazy_static = "1.4.0" multibase = { version = "0.8.0" } # earlier version due to 'did-utils' serde_json = "1.0.104" thiserror = "1.0.49" tokio = { version = "1.30.0", features = ["full"] } -tower-http = { version = "0.4.3" } tracing = "0.1.37" url = { version = "2.4.0" } uuid = { version = "1.4.1", features = ["v4"] } zeroize = { version = "1.6.0" } +# Plugins traits +server-plugin = { path = "../server-plugin" } + [dev-dependencies] json-canon = "0.1.3" tower = { version = "0.4.13", features = ["util"] } diff --git a/did-endpoint/src/lib.rs b/did-endpoint/src/lib.rs index a53cdfe1..c83b050d 100644 --- a/did-endpoint/src/lib.rs +++ b/did-endpoint/src/lib.rs @@ -1,4 +1,5 @@ pub mod didgen; pub mod web; +pub mod plugin; mod util; diff --git a/generic-server/src/plugin/did_endpoint/mod.rs b/did-endpoint/src/plugin.rs similarity index 93% rename from generic-server/src/plugin/did_endpoint/mod.rs rename to did-endpoint/src/plugin.rs index f6d0efc3..4c1bc2e1 100644 --- a/generic-server/src/plugin/did_endpoint/mod.rs +++ b/did-endpoint/src/plugin.rs @@ -1,7 +1,6 @@ use axum::Router; -use did_endpoint::{didgen, web}; - -use super::traits::{Plugin, PluginError}; +use super::{didgen, web}; +use server_plugin::{Plugin, PluginError}; #[derive(Default)] pub struct DidEndpointPlugin; diff --git a/generic-server/Cargo.toml b/generic-server/Cargo.toml index 2ab4e9f8..01e8a2bf 100644 --- a/generic-server/Cargo.toml +++ b/generic-server/Cargo.toml @@ -18,6 +18,9 @@ tower-http = { version = "0.4.3", features = ["catch-panic", "trace"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["json"] } +# Plugins traits +server-plugin = { path = "../server-plugin" } + # optional chrono = { version = "0.4.26", optional = true } did-endpoint = { path = "../did-endpoint", optional = true } diff --git a/generic-server/src/bin/didgen.rs b/generic-server/src/bin/didgen.rs deleted file mode 100644 index 7055447b..00000000 --- a/generic-server/src/bin/didgen.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[cfg(feature = "plugin-did_endpoint")] -use did_endpoint::didgen; - -/// Program entry -fn main() -> Result<(), Box> { - #[cfg(feature = "plugin-did_endpoint")] - { - use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - - // Load dotenv-flow variables - dotenv_flow::dotenv_flow().ok(); - - // Enable logging - tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer()) - .init(); - - // Run didgen logic - let storage_dirpath = std::env::var("STORAGE_DIRPATH")?; - let server_public_domain = std::env::var("SERVER_PUBLIC_DOMAIN")?; - didgen::didgen(&storage_dirpath, &server_public_domain)?; - Ok(()) - } - - #[cfg(not(feature = "plugin-did_endpoint"))] - { - Err("You must enable `plugin-did_endpoint` to run this command.".into()) - } -} diff --git a/generic-server/src/plugin/container.rs b/generic-server/src/plugin/container.rs index c45beb14..caafcb0c 100644 --- a/generic-server/src/plugin/container.rs +++ b/generic-server/src/plugin/container.rs @@ -1,11 +1,9 @@ use std::collections::{HashMap, HashSet}; use axum::Router; +use server_plugin::{Plugin, PluginError}; -use super::{ - traits::{Plugin, PluginError}, - PLUGINS, -}; +use super::PLUGINS; #[derive(Debug, PartialEq)] pub enum PluginContainerError { diff --git a/generic-server/src/plugin/index/mod.rs b/generic-server/src/plugin/index/mod.rs index 12020cee..d225143c 100644 --- a/generic-server/src/plugin/index/mod.rs +++ b/generic-server/src/plugin/index/mod.rs @@ -1,8 +1,7 @@ mod web; use axum::Router; - -use super::traits::{Plugin, PluginError}; +use server_plugin::{Plugin, PluginError}; #[derive(Default)] pub struct IndexPlugin; diff --git a/generic-server/src/plugin/mod.rs b/generic-server/src/plugin/mod.rs index 78c7282a..90d7c8fc 100644 --- a/generic-server/src/plugin/mod.rs +++ b/generic-server/src/plugin/mod.rs @@ -1,20 +1,16 @@ pub mod container; -pub mod traits; use lazy_static::lazy_static; -use traits::Plugin; +use server_plugin::Plugin; #[cfg(feature = "plugin-index")] mod index; -#[cfg(feature = "plugin-did_endpoint")] -mod did_endpoint; - lazy_static! { pub static ref PLUGINS: Vec> = vec![ #[cfg(feature = "plugin-index")] Box::::default(), #[cfg(feature = "plugin-did_endpoint")] - Box::::default(), + Box::::default(), ]; } diff --git a/run_test.sh b/run_test.sh index 12d693f5..12b0224a 100755 --- a/run_test.sh +++ b/run_test.sh @@ -1,5 +1,5 @@ #!/bin/bash -for dir in ./did-utils/ ./did-endpoint/ ./generic-server/ ./mediator-server/ +for dir in ./did-utils/ ./did-endpoint/ ./generic-server/ do cd "${dir}" if [ -f Cargo.toml ]; then diff --git a/server-plugin/Cargo.toml b/server-plugin/Cargo.toml new file mode 100644 index 00000000..69c210fe --- /dev/null +++ b/server-plugin/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "server-plugin" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +axum = { version = "0.6.20" } diff --git a/generic-server/src/plugin/traits.rs b/server-plugin/src/lib.rs similarity index 100% rename from generic-server/src/plugin/traits.rs rename to server-plugin/src/lib.rs