diff --git a/Cargo.toml b/Cargo.toml index c3007972..a04e922a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ members = [ "crates/web-plugins/didcomm-messaging/protocols/*", ] - [workspace.dependencies] database = { path = "./crates/database", version = "0.1.0" } filesystem = { path = "./crates/filesystem", version = "0.1.0" } @@ -37,12 +36,14 @@ oob-messages = { path = "./crates/web-plugins/oob-messages", version = "0.1.0" } didcomm-messaging = { path = "./crates/web-plugins/didcomm-messaging", version = "0.1.0" } did-utils = { path = "./crates/web-plugins/didcomm-messaging/did-utils", version = "0.1.0" } shared = { path = "./crates/web-plugins/didcomm-messaging/shared", version = "0.1.0" } +message-api = { path = "./crates/web-plugins/didcomm-messaging/message-api", version = "0.1.0" } pickup = { path = "./crates/web-plugins/didcomm-messaging/protocols/pickup", version = "0.1.0" } forward = { path = "./crates/web-plugins/didcomm-messaging/protocols/forward", version = "0.1.0" } trust-ping = { path = "./crates/web-plugins/didcomm-messaging/protocols/trust-ping", version = "0.1.0" } discover-features = { path = "./crates/web-plugins/didcomm-messaging/protocols/discover-features", version = "0.1.0" } mediator-coordination = { path = "./crates/web-plugins/didcomm-messaging/protocols/mediator-coordination", version = "0.1.0" } + # Other common dependencies serde = "1.0" sha2 = "0.10" @@ -127,6 +128,12 @@ plugin-did_endpoint = ["dep:did-endpoint"] plugin-oob_messages = ["dep:oob-messages"] plugin-didcomm_messaging = ["dep:didcomm-messaging"] +routing = ["plugin-didcomm_messaging", "didcomm-messaging/routing"] +pickup = ["plugin-didcomm_messaging", "didcomm-messaging/pickup"] +trust-ping = ["plugin-didcomm_messaging", "didcomm-messaging/trust-ping"] +discover-features = ["plugin-didcomm_messaging", "didcomm-messaging/discover-features"] +mediator-coordination = ["plugin-didcomm_messaging", "didcomm-messaging/mediator-coordination"] + [dev-dependencies] tower = { version = "0.4.13", features = ["util"] } diff --git a/crates/keystore/Cargo.toml b/crates/keystore/Cargo.toml index 0292c1f9..1abc205b 100644 --- a/crates/keystore/Cargo.toml +++ b/crates/keystore/Cargo.toml @@ -15,4 +15,4 @@ serde_json.workspace = true tokio = { workspace = true, features = ["full"] } [features] -test-utils = [] +test-utils = [] \ No newline at end of file diff --git a/crates/web-plugins/didcomm-messaging/Cargo.toml b/crates/web-plugins/didcomm-messaging/Cargo.toml index 4ccc5698..e0a87c7c 100644 --- a/crates/web-plugins/didcomm-messaging/Cargo.toml +++ b/crates/web-plugins/didcomm-messaging/Cargo.toml @@ -11,23 +11,45 @@ keystore.workspace = true shared.workspace = true plugin-api.workspace = true filesystem.workspace = true -forward.workspace = true -pickup.workspace = true -trust-ping.workspace = true -discover-features.workspace = true -mediator-coordination.workspace = true +message-api.workspace = true + +# optional dependencies +forward = { workspace = true, optional = true } +pickup = { workspace = true, optional = true } +trust-ping = { workspace = true, optional = true } +discover-features = { workspace = true, optional = true } +mediator-coordination = { workspace = true, optional = true } mongodb.workspace = true didcomm.workspace = true tracing.workspace = true +once_cell.workspace = true serde_json.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["full"] } hyper = { workspace = true, features = ["full"] } axum = { workspace = true, features = ["macros"] } +serde = { version = "1.0", features = ["derive"] } + + +[features] +default = [ + "routing", + "pickup", + "trust-ping", + "discover-features", + "mediator-coordination", +] + +routing = ["dep:forward"] +pickup = ["dep:pickup"] +trust-ping = ["dep:trust-ping"] +discover-features = ["dep:discover-features"] +mediator-coordination = ["dep:mediator-coordination"] [dev-dependencies] +async-trait.workspace = true mockall = "0.13.0" uuid = { workspace = true, features = ["v4"] } json-canon = "0.1.3" diff --git a/crates/web-plugins/didcomm-messaging/message-api/Cargo.toml b/crates/web-plugins/didcomm-messaging/message-api/Cargo.toml new file mode 100644 index 00000000..8bef94c7 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/message-api/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "message-api" +version = "0.1.0" +edition = "2021" + +[dependencies] +keystore.workspace = true +shared.workspace = true +database.workspace = true +filesystem.workspace = true + +async-trait.workspace = true +mongodb.workspace = true +anyhow.workspace = true +tracing.workspace = true +serde_json.workspace = true +thiserror.workspace = true +didcomm = { workspace = true, features = ["uniffi"] } +hyper = { workspace = true, features = ["full"] } +axum = { workspace = true, features = ["macros"] } + +[dev-dependencies] +keystore = { workspace = true, features = ["test-utils"] } +shared = { workspace = true, features = ["test-utils"] } +did-utils.workspace = true +uuid = { workspace = true, features = ["v4"] } +tokio = { version = "1.27.0", default-features = false, features = ["macros", "rt"] } diff --git a/crates/web-plugins/didcomm-messaging/message-api/src/lib.rs b/crates/web-plugins/didcomm-messaging/message-api/src/lib.rs new file mode 100644 index 00000000..c758d9e5 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/message-api/src/lib.rs @@ -0,0 +1,53 @@ +use async_trait::async_trait; +use axum::response::Response; +use didcomm::Message; +use shared::state::AppState; +use std::{collections::HashMap, sync::Arc}; + +#[async_trait] +pub trait MessageHandler: Send + Sync { + async fn handle(&self, state: Arc, msg: Message) + -> Result, Response>; +} + +#[derive(Default, Clone)] +pub struct MessageRouter { + handlers: HashMap>, +} + +impl MessageRouter { + pub fn new() -> Self { + Self { + handlers: HashMap::new(), + } + } + + pub fn register(mut self, msg: &str, f: F) -> Self + where + F: MessageHandler + 'static, + { + self.handlers.insert(msg.to_string(), Arc::new(f)); + self + } + + pub fn merge(mut self, other: Self) -> Self { + self.handlers.extend(other.handlers); + self + } + + pub fn get_handler(&self, msg: &str) -> Option<&Arc> { + self.handlers.get(msg) + } + + pub fn messages_types(&self) -> Vec { + self.handlers.keys().cloned().collect() + } +} + +pub trait MessagePlugin: Send + Sync { + /// Define a unique identifier + fn name(&self) -> &'static str; + + /// Return a mapping of message types to handlers + fn didcomm_routes(&self) -> MessageRouter; +} diff --git a/crates/web-plugins/didcomm-messaging/protocols/discover-features/Cargo.toml b/crates/web-plugins/didcomm-messaging/protocols/discover-features/Cargo.toml index 6de5de47..0110f841 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/discover-features/Cargo.toml +++ b/crates/web-plugins/didcomm-messaging/protocols/discover-features/Cargo.toml @@ -5,16 +5,19 @@ edition = "2021" [dependencies] shared.workspace = true +keystore.workspace = true +message-api.workspace = true + axum.workspace = true didcomm.workspace = true -serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true +uuid.workspace = true thiserror.workspace = true +serde_json.workspace = true +async-trait.workspace = true +serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["full", "rt"] } -uuid.workspace = true -keystore.workspace = true [dev-dependencies] shared = { workspace = true, features = ["test-utils"] } keystore = { workspace = true, features = ["test-utils"] } -did-utils.workspace = true \ No newline at end of file +did-utils.workspace = true diff --git a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/constants.rs b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/constants.rs new file mode 100644 index 00000000..aa86f555 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/constants.rs @@ -0,0 +1,2 @@ +pub(crate) const DISCOVER_FEATURE: &str = "https://didcomm.org/discover-features/2.0/disclose"; +pub(crate) const QUERY_FEATURE: &str = "https://didcomm.org/discover-features/2.0/queries"; diff --git a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/errors.rs b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/errors.rs index 36048f35..b215b1df 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/errors.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/errors.rs @@ -2,7 +2,7 @@ use axum::{http::StatusCode, response::IntoResponse, Json}; use thiserror::Error; #[derive(Debug, Error)] -pub enum DiscoveryError { +pub(crate) enum DiscoveryError { #[error("message body is malformed")] MalformedBody, #[error("No queries field in body")] diff --git a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/handler.rs b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/handler.rs index 092f725d..c6fde57c 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/handler.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/handler.rs @@ -1,16 +1,17 @@ use crate::{ + constants::DISCOVER_FEATURE, errors::DiscoveryError, model::{Disclosures, DisclosuresContent}, }; use didcomm::Message; use serde_json::json; -use shared::{constants::DISCOVER_FEATURE, state::AppState}; +use shared::state::AppState; use std::{collections::HashSet, sync::Arc}; use uuid::Uuid; // handle discover feature request // https://didcomm.org/discover-features/2.0/ -pub async fn handle_query_request( +pub(crate) async fn handle_query_request( state: Arc, message: Message, ) -> Result, DiscoveryError> { @@ -117,21 +118,18 @@ fn build_response(disclosed_protocols: HashSet) -> Message { #[cfg(test)] mod test { - use std::{sync::Arc, vec}; - + use crate::{constants::QUERY_FEATURE, model::Queries}; use did_utils::didcore::Document; use didcomm::Message; use keystore::tests::MockKeyStore; use serde_json::json; use shared::{ - constants::QUERY_FEATURE, repository::tests::{MockConnectionRepository, MockMessagesRepository}, state::{AppState, AppStateRepository}, }; + use std::{sync::Arc, vec}; use uuid::Uuid; - use crate::model::Queries; - use super::handle_query_request; const MEDIATION: &str = "https://didcomm.org/coordinate-mediation/2.0"; diff --git a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/lib.rs b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/lib.rs index 0fcdc3af..4e55df23 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/lib.rs @@ -1,4 +1,6 @@ +mod constants; mod errors; +mod handler; mod model; -pub mod handler; +pub mod plugin; diff --git a/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/plugin.rs new file mode 100644 index 00000000..d77795ec --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/discover-features/src/plugin.rs @@ -0,0 +1,34 @@ +use crate::constants::QUERY_FEATURE; +use async_trait::async_trait; +use axum::response::{IntoResponse, Response}; +use didcomm::Message; +use message_api::{MessageHandler, MessagePlugin, MessageRouter}; +use shared::state::AppState; +use std::sync::Arc; + +pub struct DiscoverFeaturesProtocol; + +struct DiscoverFeaturesHandler; + +#[async_trait] +impl MessageHandler for DiscoverFeaturesHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::handle_query_request(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +impl MessagePlugin for DiscoverFeaturesProtocol { + fn name(&self) -> &'static str { + "discover-features" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new().register(QUERY_FEATURE, DiscoverFeaturesHandler) + } +} diff --git a/crates/web-plugins/didcomm-messaging/protocols/forward/Cargo.toml b/crates/web-plugins/didcomm-messaging/protocols/forward/Cargo.toml index a922afd8..85278439 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/forward/Cargo.toml +++ b/crates/web-plugins/didcomm-messaging/protocols/forward/Cargo.toml @@ -7,8 +7,11 @@ edition = "2021" keystore.workspace = true shared.workspace = true database.workspace = true +filesystem.workspace = true +message-api.workspace = true mongodb.workspace = true +async-trait.workspace = true serde_json.workspace = true thiserror.workspace = true didcomm = { workspace = true, features = ["uniffi"] } diff --git a/crates/web-plugins/didcomm-messaging/protocols/forward/src/constants.rs b/crates/web-plugins/didcomm-messaging/protocols/forward/src/constants.rs new file mode 100644 index 00000000..ce2ae73c --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/forward/src/constants.rs @@ -0,0 +1 @@ +pub(crate) const MEDIATE_FORWARD_2_0: &str = "https://didcomm.org/routing/2.0/forward"; diff --git a/crates/web-plugins/didcomm-messaging/protocols/forward/src/error.rs b/crates/web-plugins/didcomm-messaging/protocols/forward/src/error.rs index 67fa1b18..5076f3bb 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/forward/src/error.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/forward/src/error.rs @@ -3,7 +3,7 @@ use hyper::StatusCode; use thiserror::Error; #[derive(Debug, Error)] -pub enum ForwardError { +pub(crate) enum ForwardError { #[error("message body is malformed")] MalformedBody, #[error("Uncoordinated sender")] diff --git a/crates/web-plugins/didcomm-messaging/protocols/forward/src/handler.rs b/crates/web-plugins/didcomm-messaging/protocols/forward/src/handler.rs index 9738c361..cc64de99 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/forward/src/handler.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/forward/src/handler.rs @@ -1,4 +1,4 @@ -use crate::ForwardError; +use crate::error::ForwardError; use database::Repository; use didcomm::{AttachmentData, Message}; use mongodb::bson::doc; @@ -11,7 +11,7 @@ use std::sync::Arc; /// Mediator receives forwarded messages, extract the next field in the message body, and the attachments in the message /// then stores the attachment with the next field as key for pickup -pub async fn mediator_forward_process( +pub(crate) async fn mediator_forward_process( state: Arc, message: Message, ) -> Result, ForwardError> { diff --git a/crates/web-plugins/didcomm-messaging/protocols/forward/src/lib.rs b/crates/web-plugins/didcomm-messaging/protocols/forward/src/lib.rs index 15606af3..4be3550a 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/forward/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/forward/src/lib.rs @@ -1,5 +1,5 @@ +mod constants; mod error; -pub mod handler; +mod handler; -// Re-exports -pub use error::ForwardError; +pub mod plugin; diff --git a/crates/web-plugins/didcomm-messaging/protocols/forward/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/forward/src/plugin.rs new file mode 100644 index 00000000..75744505 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/forward/src/plugin.rs @@ -0,0 +1,34 @@ +use crate::constants::MEDIATE_FORWARD_2_0; +use async_trait::async_trait; +use axum::response::{IntoResponse, Response}; +use didcomm::Message; +use message_api::{MessageHandler, MessagePlugin, MessageRouter}; +use shared::state::AppState; +use std::sync::Arc; + +pub struct RoutingProtocol; + +struct ForwardHandler; + +#[async_trait] +impl MessageHandler for ForwardHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::mediator_forward_process(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +impl MessagePlugin for RoutingProtocol { + fn name(&self) -> &'static str { + "routing" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new().register(MEDIATE_FORWARD_2_0, ForwardHandler) + } +} diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/Cargo.toml b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/Cargo.toml index caf38de1..f90113a3 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/Cargo.toml +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/Cargo.toml @@ -8,10 +8,13 @@ shared.workspace = true did-utils.workspace = true database.workspace = true keystore.workspace = true +filesystem.workspace = true +message-api.workspace = true mongodb.workspace = true multibase.workspace = true serde.workspace = true +async-trait.workspace = true serde_json.workspace = true thiserror.workspace = true tracing.workspace = true diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/constants.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/constants.rs new file mode 100644 index 00000000..5fe7fe5d --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/constants.rs @@ -0,0 +1,14 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused)] + +pub(crate) const MEDIATE_REQUEST_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/mediate-request"; +pub(crate) const MEDIATE_DENY_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/mediate-deny"; +pub(crate) const MEDIATE_GRANT_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/mediate-grant"; +pub(crate) const KEYLIST_UPDATE_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist-update"; +pub(crate) const KEYLIST_UPDATE_RESPONSE_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist-update-response"; +pub(crate) const KEYLIST_QUERY_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist-query"; +pub(crate) const KEYLIST_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist"; + +pub(crate) const MEDIATE_REQUEST_DIC_1_0: &str = "https://didcomm.org/coordinate-mediation/dic/1.0/mediate-request"; +pub(crate) const MEDIATE_DENY_DIC_1_0: &str = "https://didcomm.org/coordinate-mediation/dic/1.0/mediate-deny"; +pub(crate) const MEDIATE_GRANT_DIC_1_0: &str = "https://didcomm.org/coordinate-mediation/dic/1.0/mediate-grant"; diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/errors.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/errors.rs index b776eea4..7e01a08f 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/errors.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/errors.rs @@ -4,7 +4,7 @@ use thiserror::Error; /// Represents errors that can occur during mediation. #[derive(Debug, Error, PartialEq, Eq)] -pub enum MediationError { +pub(crate) enum MediationError { #[error("No return route all decoration")] NoReturnRouteAllDecoration, #[error("invalid message type")] diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/midlw.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/midlw.rs index dfa894e6..c5efadd6 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/midlw.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/midlw.rs @@ -1,7 +1,9 @@ -use crate::errors::MediationError; +use crate::{ + constants::{MEDIATE_REQUEST_2_0, MEDIATE_REQUEST_DIC_1_0}, + errors::MediationError, +}; use didcomm::Message; use serde_json::{json, Value}; -use shared::constants::{MEDIATE_REQUEST_2_0, MEDIATE_REQUEST_DIC_1_0}; /// Validate that JWM's indicative body type is a mediation request pub(crate) fn ensure_jwm_type_is_mediation_request( message: &Message, diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/stateful.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/stateful.rs index 45ad4d7b..027e89b9 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/stateful.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/handler/stateful.rs @@ -1,4 +1,5 @@ use crate::{ + constants::{KEYLIST_2_0, KEYLIST_UPDATE_RESPONSE_2_0, MEDIATE_DENY_2_0, MEDIATE_GRANT_2_0}, errors::MediationError, handler::midlw::ensure_jwm_type_is_mediation_request, model::stateful::coord::{ @@ -18,7 +19,6 @@ use keystore::Secrets; use mongodb::bson::doc; use serde_json::json; use shared::{ - constants::{KEYLIST_2_0, KEYLIST_UPDATE_RESPONSE_2_0, MEDIATE_DENY_2_0, MEDIATE_GRANT_2_0}, midlw::ensure_transport_return_route_is_decorated_all, repository::entity::Connection, state::{AppState, AppStateRepository}, @@ -27,9 +27,9 @@ use std::sync::Arc; use uuid::Uuid; /// Process a DIDComm mediate request -pub async fn process_mediate_request( +pub(crate) async fn process_mediate_request( state: Arc, - plain_message: &Message, + plain_message: Message, ) -> Result, MediationError> { // This is to Check message type compliance ensure_jwm_type_is_mediation_request(&plain_message)?; @@ -107,7 +107,7 @@ pub async fn process_mediate_request( Ok(_stored_connection) => { tracing::info!("Successfully stored agreement keys.") } - Err(error) => tracing::debug!("Error storing agreement keys: {:?}", error), + Err(error) => tracing::error!("Error storing agreement keys: {:?}", error), } let auth_keys_jwk: Jwk = auth_keys.try_into().unwrap(); @@ -122,7 +122,7 @@ pub async fn process_mediate_request( Ok(_stored_connection) => { tracing::info!("Successfully stored authentication keys.") } - Err(error) => tracing::debug!("Error storing authentication keys: {:?}", error), + Err(error) => tracing::error!("Error storing authentication keys: {:?}", error), } let mediation_grant = create_mediation_grant(&routing_did); @@ -140,7 +140,7 @@ pub async fn process_mediate_request( Ok(_stored_connection) => { tracing::info!("Successfully stored connection: ") } - Err(error) => tracing::debug!("Error storing connection: {:?}", error), + Err(error) => tracing::error!("Error storing connection: {:?}", error), } Ok(Some( @@ -202,7 +202,7 @@ fn generate_did_peer(service_endpoint: String) -> (String, Ed25519KeyPair, X2551 ) } -pub async fn process_plain_keylist_update_message( +pub(crate) async fn process_plain_keylist_update_message( state: Arc, message: Message, ) -> Result, MediationError> { @@ -330,7 +330,7 @@ pub async fn process_plain_keylist_update_message( )) } -pub async fn process_plain_keylist_query_message( +pub(crate) async fn process_plain_keylist_query_message( state: Arc, message: Message, ) -> Result, MediationError> { diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/lib.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/lib.rs index ba63c48c..3ed74c1b 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/lib.rs @@ -1,9 +1,8 @@ +mod constants; mod errors; +mod handler; mod jose; mod model; pub mod client; -pub mod handler; - -// Re-exports -pub use errors::MediationError; +pub mod plugin; diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/coord.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/coord.rs index eb4288a0..fa2d58f0 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/coord.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/coord.rs @@ -1,6 +1,6 @@ use serde::{de::Error, Deserialize, Deserializer, Serialize}; -use shared::constants::MEDIATE_REQUEST_2_0; +use crate::constants::MEDIATE_REQUEST_2_0; #[cfg(feature = "stateless")] use super::stateless::coord::MediationRequest as StatelessMediationRequest; diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/stateful/coord.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/stateful/coord.rs index e186002e..1c7cbf21 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/stateful/coord.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/model/stateful/coord.rs @@ -283,7 +283,7 @@ mod tests { use serde_json::{json, Value}; - use shared::constants::*; + use crate::constants::*; #[test] fn can_serde_return_route_header_enum() { diff --git a/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/plugin.rs new file mode 100644 index 00000000..3aab4d69 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/mediator-coordination/src/plugin.rs @@ -0,0 +1,65 @@ +use crate::constants::{KEYLIST_QUERY_2_0, KEYLIST_UPDATE_2_0, MEDIATE_REQUEST_2_0}; +use async_trait::async_trait; +use axum::response::{IntoResponse, Response}; +use didcomm::Message; +use message_api::{MessageHandler, MessagePlugin, MessageRouter}; +use shared::state::AppState; +use std::sync::Arc; + +pub struct MediatorCoordinationProtocol; + +struct MediateRequestHandler; +struct KeylistUpdateHandler; +struct KeylistQueryHandler; + +#[async_trait] +impl MessageHandler for MediateRequestHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::stateful::process_mediate_request(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +#[async_trait] +impl MessageHandler for KeylistUpdateHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::stateful::process_plain_keylist_update_message(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +#[async_trait] +impl MessageHandler for KeylistQueryHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::stateful::process_plain_keylist_query_message(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +impl MessagePlugin for MediatorCoordinationProtocol { + fn name(&self) -> &'static str { + "mediator-coordination" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new() + .register(MEDIATE_REQUEST_2_0, MediateRequestHandler) + .register(KEYLIST_UPDATE_2_0, KeylistUpdateHandler) + .register(KEYLIST_QUERY_2_0, KeylistQueryHandler) + } +} diff --git a/crates/web-plugins/didcomm-messaging/protocols/pickup/Cargo.toml b/crates/web-plugins/didcomm-messaging/protocols/pickup/Cargo.toml index 6fcc69f9..97694183 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/pickup/Cargo.toml +++ b/crates/web-plugins/didcomm-messaging/protocols/pickup/Cargo.toml @@ -5,12 +5,17 @@ edition = "2021" [dependencies] shared.workspace = true +filesystem.workspace = true +message-api.workspace = true +keystore.workspace = true +database.workspace = true serde.workspace = true didcomm.workspace = true mongodb.workspace = true serde_json.workspace = true thiserror.workspace = true +async-trait.workspace = true uuid = { workspace = true, features = ["v4"] } axum = { workspace = true, features = ["macros"] } diff --git a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/constants.rs b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/constants.rs new file mode 100644 index 00000000..4de1fb5d --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/constants.rs @@ -0,0 +1,9 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +pub(crate) const STATUS_REQUEST_3_0: &str = "https://didcomm.org/messagepickup/3.0/status-request"; +pub(crate) const STATUS_RESPONSE_3_0: &str = "https://didcomm.org/messagepickup/3.0/status"; +pub(crate) const DELIVERY_REQUEST_3_0: &str = "https://didcomm.org/messagepickup/3.0/delivery-request"; +pub(crate) const MESSAGE_DELIVERY_3_0: &str = "https://didcomm.org/messagepickup/3.0/delivery"; +pub(crate) const MESSAGE_RECEIVED_3_0: &str = "https://didcomm.org/messagepickup/3.0/messages-received"; +pub(crate) const LIVE_MODE_CHANGE_3_0: &str = "https://didcomm.org/messagepickup/3.0/live-delivery-change"; +pub(crate) const PROBLEM_REPORT_2_0: &str = "https://didcomm.org/report-problem/2.0/problem-report"; diff --git a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/error.rs b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/error.rs index 037bcd4d..4fd25b89 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/error.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/error.rs @@ -2,7 +2,7 @@ use axum::{http::StatusCode, response::IntoResponse, Json}; use thiserror::Error; #[derive(Debug, Error, PartialEq)] -pub enum PickupError { +pub(crate) enum PickupError { #[error("Missing sender DID")] MissingSenderDID, diff --git a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/handler.rs b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/handler.rs index 0530c114..87c0a2b5 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/handler.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/handler.rs @@ -1,4 +1,5 @@ use crate::{ + constants::{MESSAGE_DELIVERY_3_0, PROBLEM_REPORT_2_0, STATUS_RESPONSE_3_0}, error::PickupError, model::{ BodyDeliveryResponse, BodyLiveDeliveryChange, BodyStatusResponse, DeliveryResponse, @@ -9,7 +10,6 @@ use didcomm::{Attachment, Message, MessageBuilder}; use mongodb::bson::{doc, oid::ObjectId}; use serde_json::Value; use shared::{ - constants::{MESSAGE_DELIVERY_3_0, PROBLEM_REPORT_2_0, STATUS_RESPONSE_3_0}, midlw::ensure_transport_return_route_is_decorated_all, repository::entity::{Connection, RoutedMessage}, state::{AppState, AppStateRepository}, @@ -18,7 +18,7 @@ use std::{str::FromStr, sync::Arc}; use uuid::Uuid; // Process pickup status request -pub async fn handle_status_request( +pub(crate) async fn handle_status_request( state: Arc, message: Message, ) -> Result, PickupError> { @@ -60,7 +60,7 @@ pub async fn handle_status_request( } // Process pickup delivery request -pub async fn handle_delivery_request( +pub(crate) async fn handle_delivery_request( state: Arc, message: Message, ) -> Result, PickupError> { @@ -136,7 +136,7 @@ pub async fn handle_delivery_request( } // Process pickup messages acknowledgement -pub async fn handle_message_acknowledgement( +pub(crate) async fn handle_message_acknowledgement( state: Arc, message: Message, ) -> Result, PickupError> { @@ -200,7 +200,7 @@ pub async fn handle_message_acknowledgement( } // Process live delivery change request -pub async fn handle_live_delivery_change( +pub(crate) async fn handle_live_delivery_change( state: Arc, message: Message, ) -> Result, PickupError> { @@ -333,12 +333,12 @@ async fn client_connection( #[cfg(test)] mod tests { use super::*; + use crate::constants::{ + DELIVERY_REQUEST_3_0, LIVE_MODE_CHANGE_3_0, MESSAGE_DELIVERY_3_0, MESSAGE_RECEIVED_3_0, + PROBLEM_REPORT_2_0, STATUS_REQUEST_3_0, STATUS_RESPONSE_3_0, + }; use serde_json::json; use shared::{ - constants::{ - DELIVERY_REQUEST_3_0, LIVE_MODE_CHANGE_3_0, MESSAGE_DELIVERY_3_0, MESSAGE_RECEIVED_3_0, - PROBLEM_REPORT_2_0, STATUS_REQUEST_3_0, STATUS_RESPONSE_3_0, - }, repository::tests::{MockConnectionRepository, MockMessagesRepository}, utils::tests_utils::tests as global, }; diff --git a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/lib.rs b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/lib.rs index 1c7cc076..c09e19cb 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/lib.rs @@ -1,7 +1,6 @@ +mod constants; mod error; +mod handler; mod model; -pub mod handler; - -// Re-exports -pub use error::PickupError; +pub mod plugin; diff --git a/crates/web-plugins/didcomm-messaging/protocols/pickup/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/plugin.rs new file mode 100644 index 00000000..c40df56e --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/pickup/src/plugin.rs @@ -0,0 +1,82 @@ +use crate::constants::{ + DELIVERY_REQUEST_3_0, LIVE_MODE_CHANGE_3_0, MESSAGE_RECEIVED_3_0, STATUS_REQUEST_3_0, +}; +use async_trait::async_trait; +use axum::response::{IntoResponse, Response}; +use didcomm::Message; +use message_api::{MessageHandler, MessagePlugin, MessageRouter}; +use shared::state::AppState; +use std::sync::Arc; + +pub struct PickupProtocol; + +struct StatusRequestHandler; +struct DeliveryRequestHandler; +struct MessageReceivedHandler; +struct LiveModeChangeHandler; + +#[async_trait] +impl MessageHandler for StatusRequestHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::handle_status_request(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +#[async_trait] +impl MessageHandler for DeliveryRequestHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::handle_delivery_request(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +#[async_trait] +impl MessageHandler for MessageReceivedHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::handle_message_acknowledgement(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +#[async_trait] +impl MessageHandler for LiveModeChangeHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::handle_live_delivery_change(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +impl MessagePlugin for PickupProtocol { + fn name(&self) -> &'static str { + "pickup" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new() + .register(STATUS_REQUEST_3_0, StatusRequestHandler) + .register(DELIVERY_REQUEST_3_0, DeliveryRequestHandler) + .register(MESSAGE_RECEIVED_3_0, MessageReceivedHandler) + .register(LIVE_MODE_CHANGE_3_0, LiveModeChangeHandler) + } +} diff --git a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/Cargo.toml b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/Cargo.toml index 76a648d1..4a50e2d8 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/Cargo.toml +++ b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/Cargo.toml @@ -5,10 +5,12 @@ edition = "2021" [dependencies] shared.workspace = true +message-api.workspace = true didcomm.workspace = true serde.workspace = true uuid.workspace = true +async-trait.workspace = true serde_json.workspace = true thiserror.workspace = true hyper = { workspace = true, features = ["full"] } diff --git a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/constants.rs b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/constants.rs new file mode 100644 index 00000000..f2e0e16b --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/constants.rs @@ -0,0 +1,4 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +pub(crate) const TRUST_PING_2_0: &str = "https://didcomm.org/trust-ping/2.0/ping"; +pub(crate) const TRUST_PING_RESPONSE_2_0: &str = "https://didcomm.org/trust-ping/2.0/ping-response"; diff --git a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/error.rs b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/error.rs index 2c8d4741..48d11dbe 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/error.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/error.rs @@ -3,7 +3,7 @@ use serde::Serialize; use thiserror::Error; #[derive(Debug, Serialize, Error, PartialEq, Eq)] -pub enum TrustPingError { +pub(crate) enum TrustPingError { #[error("Missing sender DID")] MissingSenderDID, diff --git a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/handler.rs b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/handler.rs index 9d468d8d..f75503de 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/handler.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/handler.rs @@ -4,10 +4,9 @@ use uuid::Uuid; use shared::state::AppState; -use crate::{error::TrustPingError, model::TrustPingResponse}; -use shared::constants::TRUST_PING_RESPONSE_2_0; +use crate::{constants::TRUST_PING_RESPONSE_2_0, error::TrustPingError, model::TrustPingResponse}; -pub async fn handle_trust_ping( +pub(crate) async fn handle_trust_ping( state: Arc, message: Message, ) -> Result, TrustPingError> { @@ -48,7 +47,8 @@ mod tests { use serde_json::json; use super::*; - use shared::{constants::TRUST_PING_2_0, utils::tests_utils::tests as global}; + use crate::constants::TRUST_PING_2_0; + use shared::utils::tests_utils::tests as global; #[tokio::test] async fn test_request_trust_ping_response() { diff --git a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/lib.rs b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/lib.rs index 3cdf318e..c09e19cb 100644 --- a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/lib.rs @@ -1,7 +1,6 @@ +mod constants; mod error; +mod handler; mod model; -pub mod handler; - -// Re-exports -pub use error::TrustPingError; +pub mod plugin; diff --git a/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/plugin.rs b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/plugin.rs new file mode 100644 index 00000000..59982eed --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/protocols/trust-ping/src/plugin.rs @@ -0,0 +1,34 @@ +use crate::constants::TRUST_PING_2_0; +use async_trait::async_trait; +use axum::response::{IntoResponse, Response}; +use didcomm::Message; +use message_api::{MessageHandler, MessagePlugin, MessageRouter}; +use shared::state::AppState; +use std::sync::Arc; + +pub struct TrustPingProtocol; + +struct TrustPingHandler; + +#[async_trait] +impl MessageHandler for TrustPingHandler { + async fn handle( + &self, + state: Arc, + msg: Message, + ) -> Result, Response> { + crate::handler::handle_trust_ping(state, msg) + .await + .map_err(|e| e.into_response()) + } +} + +impl MessagePlugin for TrustPingProtocol { + fn name(&self) -> &'static str { + "trust-ping" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new().register(TRUST_PING_2_0, TrustPingHandler) + } +} diff --git a/crates/web-plugins/didcomm-messaging/shared/src/constants.rs b/crates/web-plugins/didcomm-messaging/shared/src/constants.rs deleted file mode 100644 index fe6df2ee..00000000 --- a/crates/web-plugins/didcomm-messaging/shared/src/constants.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![cfg_attr(rustfmt, rustfmt_skip)] - -pub const MEDIATE_REQUEST_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/mediate-request"; -pub const MEDIATE_DENY_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/mediate-deny"; -pub const MEDIATE_GRANT_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/mediate-grant"; -pub const KEYLIST_UPDATE_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist-update"; -pub const KEYLIST_UPDATE_RESPONSE_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist-update-response"; -pub const KEYLIST_QUERY_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist-query"; -pub const KEYLIST_2_0: &str = "https://didcomm.org/coordinate-mediation/2.0/keylist"; -pub const MEDIATE_FORWARD_2_0: &str = "https://didcomm.org/routing/2.0/forward"; - -pub const MEDIATE_REQUEST_DIC_1_0: &str = "https://didcomm.org/coordinate-mediation/dic/1.0/mediate-request"; -pub const MEDIATE_DENY_DIC_1_0: &str = "https://didcomm.org/coordinate-mediation/dic/1.0/mediate-deny"; -pub const MEDIATE_GRANT_DIC_1_0: &str = "https://didcomm.org/coordinate-mediation/dic/1.0/mediate-grant"; - -pub const STATUS_REQUEST_3_0: &str = "https://didcomm.org/messagepickup/3.0/status-request"; -pub const STATUS_RESPONSE_3_0: &str = "https://didcomm.org/messagepickup/3.0/status"; -pub const DELIVERY_REQUEST_3_0: &str = "https://didcomm.org/messagepickup/3.0/delivery-request"; -pub const MESSAGE_DELIVERY_3_0: &str = "https://didcomm.org/messagepickup/3.0/delivery"; -pub const MESSAGE_RECEIVED_3_0: &str = "https://didcomm.org/messagepickup/3.0/messages-received"; -pub const LIVE_MODE_CHANGE_3_0: &str = "https://didcomm.org/messagepickup/3.0/live-delivery-change"; -pub const PROBLEM_REPORT_2_0: &str = "https://didcomm.org/report-problem/2.0/problem-report"; - -pub const TRUST_PING_2_0: &str = "https://didcomm.org/trust-ping/2.0/ping"; -pub const TRUST_PING_RESPONSE_2_0: &str = "https://didcomm.org/trust-ping/2.0/ping-response"; - -pub const DIDCOMM_ENCRYPTED_MIME_TYPE: &str = "application/didcomm-encrypted+json"; -pub const DIDCOMM_ENCRYPTED_SHORT_MIME_TYPE: &str = "didcomm-encrypted+json"; -pub const DISCOVER_FEATURE: &str = "https://didcomm.org/discover-features/2.0/disclose"; -pub const QUERY_FEATURE: &str = "https://didcomm.org/discover-features/2.0/queries"; \ No newline at end of file diff --git a/crates/web-plugins/didcomm-messaging/shared/src/lib.rs b/crates/web-plugins/didcomm-messaging/shared/src/lib.rs index 204c9898..530e4728 100644 --- a/crates/web-plugins/didcomm-messaging/shared/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/shared/src/lib.rs @@ -1,4 +1,3 @@ -pub mod constants; pub mod errors; pub mod midlw; pub mod repository; diff --git a/crates/web-plugins/didcomm-messaging/shared/src/midlw.rs b/crates/web-plugins/didcomm-messaging/shared/src/midlw.rs index 08bd0211..4af7d6a4 100644 --- a/crates/web-plugins/didcomm-messaging/shared/src/midlw.rs +++ b/crates/web-plugins/didcomm-messaging/shared/src/midlw.rs @@ -21,7 +21,7 @@ pub fn ensure_transport_return_route_is_decorated_all( #[cfg(test)] mod midlw_test { use super::*; - use crate::{constants::MEDIATE_REQUEST_2_0, utils::tests_utils::tests}; + use crate::utils::tests_utils::tests; use serde_json::{json, Value}; #[tokio::test] @@ -32,7 +32,7 @@ mod midlw_test { () => { Message::build( "urn:uuid:8f8208ae-6e16-4275-bde8-7b7cb81ffa59".to_owned(), - MEDIATE_REQUEST_2_0.to_string(), + "https://didcomm.org/coordinate-mediation/2.0/mediate-request".to_string(), json!({ "did": "did:key:alice_identity_pub@alice_mediator", "services": ["inbox", "outbox"] diff --git a/crates/web-plugins/didcomm-messaging/src/constants.rs b/crates/web-plugins/didcomm-messaging/src/constants.rs new file mode 100644 index 00000000..d34e7ff1 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/src/constants.rs @@ -0,0 +1,4 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] + +pub(crate) const DIDCOMM_ENCRYPTED_MIME_TYPE: &str = "application/didcomm-encrypted+json"; +pub(crate) const DIDCOMM_ENCRYPTED_SHORT_MIME_TYPE: &str = "didcomm-encrypted+json"; diff --git a/crates/web-plugins/didcomm-messaging/src/did_rotation/did_rotation.rs b/crates/web-plugins/didcomm-messaging/src/did_rotation/did_rotation.rs index a505c092..582d3016 100644 --- a/crates/web-plugins/didcomm-messaging/src/did_rotation/did_rotation.rs +++ b/crates/web-plugins/didcomm-messaging/src/did_rotation/did_rotation.rs @@ -76,6 +76,7 @@ pub async fn did_rotation( #[cfg(test)] mod test { + use crate::constants::DIDCOMM_ENCRYPTED_MIME_TYPE; use std::{sync::Arc, vec}; use did_utils::{didcore::Document, jwk::Jwk}; diff --git a/crates/web-plugins/didcomm-messaging/src/lib.rs b/crates/web-plugins/didcomm-messaging/src/lib.rs index eb70cd10..24b2096b 100644 --- a/crates/web-plugins/didcomm-messaging/src/lib.rs +++ b/crates/web-plugins/didcomm-messaging/src/lib.rs @@ -1,6 +1,9 @@ +mod constants; mod did_rotation; mod error; +mod manager; mod midlw; +mod protocols; mod web; pub mod plugin; diff --git a/crates/web-plugins/didcomm-messaging/src/manager.rs b/crates/web-plugins/didcomm-messaging/src/manager.rs new file mode 100644 index 00000000..4168cee1 --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/src/manager.rs @@ -0,0 +1,171 @@ +use crate::protocols::DIDCOMM_PLUGINS; +use message_api::{MessagePlugin, MessageRouter}; +use std::{collections::HashSet, sync::Arc}; + +#[derive(Debug, PartialEq)] +pub(crate) enum MessageContainerError { + DuplicateEntry, + Unloaded, +} + +pub(crate) struct MessagePluginContainer<'a> { + pub(crate) loaded: bool, + pub(crate) collected_routes: Vec, + pub(crate) message_plugins: &'a Vec>, +} + +impl<'a> MessagePluginContainer<'a> { + pub(crate) fn new() -> Self { + Self { + loaded: false, + collected_routes: vec![], + message_plugins: &DIDCOMM_PLUGINS, + } + } + + pub(crate) fn load(&mut self) -> Result<(), MessageContainerError> { + tracing::debug!("Loading DIDCcomm protocols container"); + + let mut seen_names = HashSet::new(); + for protocol in self.message_plugins.iter() { + if !seen_names.insert(protocol.name()) { + tracing::error!( + "found duplicate entry in DIDComm protocols container: {}", + protocol.name() + ); + return Err(MessageContainerError::DuplicateEntry); + } + } + + // Reset route collection + self.collected_routes.clear(); + + // Collect didcomm messages routes + for protocol in self.message_plugins.iter() { + tracing::info!("registering didcomm protocol: {}", protocol.name()); + self.collected_routes.push(protocol.didcomm_routes()); + } + + // Update loaded status + self.loaded = true; + tracing::debug!("DIDComm protocols container loaded successfully"); + Ok(()) + } + + pub(crate) fn didcomm_routes(&self) -> Result { + if !self.loaded { + return Err(MessageContainerError::Unloaded); + } + + Ok(self + .collected_routes + .iter() + .fold(MessageRouter::new(), |acc: MessageRouter, e| { + acc.merge(e.clone()) + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Arc; + + // Real plugin implementations + struct FirstPlugin; + impl MessagePlugin for FirstPlugin { + fn name(&self) -> &'static str { + "first" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new() + } + } + + struct SecondPlugin; + impl MessagePlugin for SecondPlugin { + fn name(&self) -> &'static str { + "second" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new() + } + } + + #[test] + fn test_loading_plugins() { + let plugins: Vec> = + vec![Arc::new(FirstPlugin {}), Arc::new(SecondPlugin {})]; + + let mut container = MessagePluginContainer { + loaded: false, + collected_routes: vec![], + message_plugins: &plugins, + }; + + assert!(container.load().is_ok()); + assert!(container.loaded); + + assert_eq!(container.collected_routes.len(), 2); + } + + #[test] + fn test_loading_duplicate_plugins() { + let plugins: Vec> = vec![ + Arc::new(FirstPlugin {}), + Arc::new(SecondPlugin {}), + Arc::new(SecondPlugin {}), + ]; + + let mut container = MessagePluginContainer { + loaded: false, + collected_routes: vec![], + message_plugins: &plugins, + }; + + assert_eq!( + container.load().unwrap_err(), + MessageContainerError::DuplicateEntry + ); + + assert_eq!(container.collected_routes.len(), 0); + } + + #[test] + fn test_double_loading() { + let plugins: Vec> = + vec![Arc::new(FirstPlugin {}), Arc::new(SecondPlugin {})]; + + let mut container = MessagePluginContainer { + loaded: false, + collected_routes: vec![], + message_plugins: &plugins, + }; + + assert!(container.load().is_ok()); + assert!(container.load().is_ok()); + } + #[test] + fn test_routes_without_loading() { + let plugins: Vec> = + vec![Arc::new(FirstPlugin {}), Arc::new(SecondPlugin {})]; + + let container = MessagePluginContainer { + loaded: false, + collected_routes: vec![], + message_plugins: &plugins, + }; + + // Attempt to access routes without loading + assert!( + container.didcomm_routes().is_err(), + "Routes should not be accessible without loading" + ); + + if let Err(err) = container.didcomm_routes() { + assert_eq!(err, MessageContainerError::Unloaded); + } + } +} diff --git a/crates/web-plugins/didcomm-messaging/src/midlw.rs b/crates/web-plugins/didcomm-messaging/src/midlw.rs index 037b6813..31839e4d 100644 --- a/crates/web-plugins/didcomm-messaging/src/midlw.rs +++ b/crates/web-plugins/didcomm-messaging/src/midlw.rs @@ -10,9 +10,12 @@ use serde_json::Value; use std::sync::Arc; // use super::{error::MediationError, AppState}; -use crate::{did_rotation::did_rotation::did_rotation, error::Error}; -use shared::{ +use crate::{ constants::{DIDCOMM_ENCRYPTED_MIME_TYPE, DIDCOMM_ENCRYPTED_SHORT_MIME_TYPE}, + did_rotation::did_rotation::did_rotation, + error::Error, +}; +use shared::{ state::{AppState, AppStateRepository}, utils::resolvers::{LocalDIDResolver, LocalSecretsResolver}, }; diff --git a/crates/web-plugins/didcomm-messaging/src/plugin.rs b/crates/web-plugins/didcomm-messaging/src/plugin.rs index 2522b54e..3a88c8a0 100644 --- a/crates/web-plugins/didcomm-messaging/src/plugin.rs +++ b/crates/web-plugins/didcomm-messaging/src/plugin.rs @@ -1,7 +1,8 @@ -use crate::web; +use crate::{manager::MessagePluginContainer, web}; use axum::Router; use filesystem::StdFileSystem; use mongodb::Database; +use once_cell::sync::OnceCell; use plugin_api::{Plugin, PluginError}; use shared::{ repository::{MongoConnectionRepository, MongoMessagesRepository}, @@ -9,11 +10,15 @@ use shared::{ utils, }; use std::sync::Arc; +use tokio::sync::RwLock; + +pub(crate) static MESSAGE_CONTAINER: OnceCell> = OnceCell::new(); #[derive(Default)] -pub struct MediatorCoordination { +pub struct DidcommMessaging { env: Option, db: Option, + msg_types: Option>, } struct DidcommMessagingPluginEnv { @@ -23,12 +28,16 @@ struct DidcommMessagingPluginEnv { /// Loads environment variables required for this plugin fn load_plugin_env() -> Result { - let public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").map_err(|_| { - PluginError::InitError("SERVER_PUBLIC_DOMAIN env variable required".to_owned()) + let public_domain = std::env::var("SERVER_PUBLIC_DOMAIN").map_err(|err| { + PluginError::InitError(format!( + "SERVER_PUBLIC_DOMAIN env variable required: {:?}", + err + )) })?; - let storage_dirpath = std::env::var("STORAGE_DIRPATH") - .map_err(|_| PluginError::InitError("STORAGE_DIRPATH env variable required".to_owned()))?; + let storage_dirpath = std::env::var("STORAGE_DIRPATH").map_err(|err| { + PluginError::InitError(format!("STORAGE_DIRPATH env variable required: {:?}", err)) + })?; Ok(DidcommMessagingPluginEnv { public_domain, @@ -36,9 +45,9 @@ fn load_plugin_env() -> Result { }) } -impl Plugin for MediatorCoordination { +impl Plugin for DidcommMessaging { fn name(&self) -> &'static str { - "mediator_coordination" + "didcomm_messaging" } fn mount(&mut self) -> Result<(), PluginError> { @@ -48,14 +57,35 @@ impl Plugin for MediatorCoordination { let keystore = keystore::KeyStore::get(); // Expect DID document from file system - if did_endpoint::validate_diddoc(env.storage_dirpath.as_ref(), &keystore, &mut filesystem) - .is_err() + if let Err(err) = + did_endpoint::validate_diddoc(env.storage_dirpath.as_ref(), &keystore, &mut filesystem) { - return Err(PluginError::InitError( - "diddoc validation failed; is plugin did-endpoint mounted?".to_owned(), - )); + return Err(PluginError::InitError(format!( + "DID document validation failed: {:?}", + err + ))); + } + + // Load message container + let mut container = MessagePluginContainer::new(); + if let Err(err) = container.load() { + return Err(PluginError::InitError(format!( + "Error loading didcomm messages container: {:?}", + err + ))); } + // Get didcomm message types + let msg_types = container + .didcomm_routes() + .map_err(|_| PluginError::InitError("Failed to get didcomm message types".to_owned()))? + .messages_types(); + + // Set the message container + MESSAGE_CONTAINER + .set(RwLock::new(container)) + .map_err(|_| PluginError::InitError("Container already initialized".to_owned()))?; + // Check connectivity to database let db = tokio::task::block_in_place(|| { let rt = tokio::runtime::Handle::current(); @@ -66,9 +96,10 @@ impl Plugin for MediatorCoordination { }) }); - // Save the environment and MongoDB connection in the struct + // Save the environment,MongoDB connection and didcomm message types in the struct self.env = Some(env); self.db = Some(db); + self.msg_types = Some(msg_types); Ok(()) } @@ -85,26 +116,34 @@ impl Plugin for MediatorCoordination { let db = self.db.as_ref().ok_or(PluginError::Other( "Failed to get database handle. Check if the plugin is mounted".to_owned(), ))?; + let msg_types = self.msg_types.as_ref().ok_or(PluginError::Other( + "Failed to get message types. Check if the plugin is mounted".to_owned(), + ))?; // Load crypto identity let fs = StdFileSystem; - let diddoc = utils::read_diddoc(&fs, &env.storage_dirpath).map_err(|_| { - PluginError::Other("This should not occur following successful mounting.".to_owned()) + let diddoc = utils::read_diddoc(&fs, &env.storage_dirpath).map_err(|err| { + PluginError::Other(format!( + "This should not occur following successful mounting: {:?}", + err + )) })?; // Load persistence layer let repository = AppStateRepository { - connection_repository: Arc::new(MongoConnectionRepository::from_db(&db)), + connection_repository: Arc::new(MongoConnectionRepository::from_db(db)), keystore: Arc::new(keystore::KeyStore::get()), - message_repository: Arc::new(MongoMessagesRepository::from_db(&db)), + message_repository: Arc::new(MongoMessagesRepository::from_db(db)), }; // Compile state - let state = AppState::from(env.public_domain.clone(), diddoc, None, Some(repository)) - .map_err(|err| { - tracing::error!("Failed to load app state: {:?}", err); - PluginError::Other("Failed to load app state".to_owned()) - })?; + let state = AppState::from( + env.public_domain.clone(), + diddoc, + Some(msg_types.clone()), + Some(repository), + ) + .map_err(|err| PluginError::Other(format!("Failed to load app state: {:?}", err)))?; // Build router Ok(web::routes(Arc::new(state))) diff --git a/crates/web-plugins/didcomm-messaging/src/protocols.rs b/crates/web-plugins/didcomm-messaging/src/protocols.rs new file mode 100644 index 00000000..8ce2158e --- /dev/null +++ b/crates/web-plugins/didcomm-messaging/src/protocols.rs @@ -0,0 +1,18 @@ +use message_api::MessagePlugin; +use once_cell::sync::Lazy; +use std::sync::Arc; + +pub(crate) static DIDCOMM_PLUGINS: Lazy>> = Lazy::new(|| { + vec![ + #[cfg(feature = "routing")] + Arc::new(forward::plugin::RoutingProtocol), + #[cfg(feature = "pickup")] + Arc::new(pickup::plugin::PickupProtocol), + #[cfg(feature = "trust-ping")] + Arc::new(trust_ping::plugin::TrustPingProtocol), + #[cfg(feature = "discover-features")] + Arc::new(discover_features::plugin::DiscoverFeaturesProtocol), + #[cfg(feature = "mediator-coordination")] + Arc::new(mediator_coordination::plugin::MediatorCoordinationProtocol), + ] +}); diff --git a/crates/web-plugins/didcomm-messaging/src/web/dispatcher.rs b/crates/web-plugins/didcomm-messaging/src/web/dispatcher.rs index 51fdd67b..42ba828f 100644 --- a/crates/web-plugins/didcomm-messaging/src/web/dispatcher.rs +++ b/crates/web-plugins/didcomm-messaging/src/web/dispatcher.rs @@ -1,3 +1,4 @@ +use crate::{constants::DIDCOMM_ENCRYPTED_MIME_TYPE, plugin::MESSAGE_CONTAINER}; use axum::{ extract::State, response::{IntoResponse, Response}, @@ -5,15 +6,7 @@ use axum::{ }; use didcomm::Message; use hyper::{header::CONTENT_TYPE, StatusCode}; -use mediator_coordination::handler; -use shared::{ - constants::{ - DELIVERY_REQUEST_3_0, DIDCOMM_ENCRYPTED_MIME_TYPE, DISCOVER_FEATURE, KEYLIST_QUERY_2_0, - KEYLIST_UPDATE_2_0, LIVE_MODE_CHANGE_3_0, MEDIATE_FORWARD_2_0, MEDIATE_REQUEST_2_0, - MESSAGE_RECEIVED_3_0, STATUS_REQUEST_3_0, TRUST_PING_2_0, - }, - state::AppState, -}; +use shared::state::AppState; use std::sync::Arc; #[axum::debug_handler] @@ -21,61 +14,20 @@ pub(crate) async fn process_didcomm_message( State(state): State>, Extension(message): Extension, ) -> Response { - let response: Result, Response> = match message.type_.as_str() { - MEDIATE_FORWARD_2_0 => forward::handler::mediator_forward_process(state.clone(), message) - .await - .map_err(|e| e.into_response()), - - MEDIATE_REQUEST_2_0 => handler::stateful::process_mediate_request(state.clone(), &message) - .await - .map_err(|e| e.into_response()), - - KEYLIST_UPDATE_2_0 => { - handler::stateful::process_plain_keylist_update_message(Arc::clone(&state), message) - .await - .map_err(|e| e.into_response()) - } - - KEYLIST_QUERY_2_0 => { - handler::stateful::process_plain_keylist_query_message(state.clone(), message) - .await - .map_err(|e| e.into_response()) - } - - STATUS_REQUEST_3_0 => pickup::handler::handle_status_request(state.clone(), message) - .await - .map_err(|e| e.into_response()), - - DELIVERY_REQUEST_3_0 => pickup::handler::handle_delivery_request(state.clone(), message) - .await - .map_err(|e| e.into_response()), - - MESSAGE_RECEIVED_3_0 => { - pickup::handler::handle_message_acknowledgement(state.clone(), message) - .await - .map_err(|e| e.into_response()) - } - - LIVE_MODE_CHANGE_3_0 => { - pickup::handler::handle_live_delivery_change(state.clone(), message) - .await - .map_err(|e| e.into_response()) - } - - TRUST_PING_2_0 => trust_ping::handler::handle_trust_ping(state.clone(), message) - .await - .map_err(|e| e.into_response()), - - DISCOVER_FEATURE => { - discover_features::handler::handle_query_request(state.clone(), message) - .await - .map_err(|e| e.into_response()) - } - - _ => return (StatusCode::BAD_REQUEST, "Unsupported operation".to_string()).into_response(), - }; + if let Some(handler) = MESSAGE_CONTAINER + .get() + .unwrap() + .read() + .await + .didcomm_routes() + .unwrap_or_default() + .get_handler(&message.type_) + { + let response = handler.handle(state.clone(), message).await; + return process_response(state, response).await; + } - process_response(state, response).await + (StatusCode::BAD_REQUEST, "Unsupported didcomm message").into_response() } async fn process_response( @@ -108,54 +60,93 @@ async fn process_response( #[cfg(test)] mod tests { use super::*; + use crate::manager::MessagePluginContainer; use axum::Router; use hyper::{Body, Method, Request}; + use message_api::{MessageHandler, MessagePlugin, MessageRouter}; + use once_cell::sync::Lazy; use serde_json::{json, Value}; - use shared::{ - constants::KEYLIST_UPDATE_RESPONSE_2_0, repository::tests::MockConnectionRepository, - state::AppStateRepository, utils::tests_utils::tests as global, - }; + use shared::utils::tests_utils::tests as global; + use tokio::sync::RwLock; use tower::ServiceExt; #[allow(clippy::needless_update)] pub fn setup() -> (Router, Arc) { let state = global::setup(); + let app = crate::web::routes(state.clone()); - let mut state = match Arc::try_unwrap(state) { - Ok(state) => state, - Err(_) => panic!(), - }; + (app, state) + } - state.repository = Some(AppStateRepository { - connection_repository: Arc::new(MockConnectionRepository::from( - serde_json::from_str( - r##"[ - { - "_id": { - "$oid": "6580701fd2d92bb3cd291b2a" - }, - "client_did": "did:key:z6MkfyTREjTxQ8hUwSwBPeDHf3uPL3qCjSSuNPwsyMpWUGH7", - "mediator_did": "did:web:alice-mediator.com:alice_mediator_pub", - "routing_did": "did:key:generated", - "keylist": [ - "did:key:alice_identity_pub1@alice_mediator" - ] - } - ]"##, - ) - .unwrap(), - )), - ..state.repository.unwrap() - }); + #[derive(Debug)] + struct MockKeylistUpdateHandler; + struct MockProtocol; + + #[async_trait::async_trait] + impl MessageHandler for MockKeylistUpdateHandler { + async fn handle( + &self, + _state: Arc, + message: Message, + ) -> Result, Response> { + let response_body = json!({ + "updated": [ + { + "recipient_did": "did:key:alice_identity_pub1@alice_mediator", + "action": "remove", + "result": "success" + }, + { + "recipient_did": "did:key:alice_identity_pub2@alice_mediator", + "action": "add", + "result": "success" + }, + ] + }); - let state = Arc::new(state); - let app = crate::web::routes(state.clone()); + let response = Message::build( + message.id.clone(), + "https://didcomm.org/coordinate-mediation/2.0/keylist-update-response".to_owned(), + response_body, + ) + .to(message.from.unwrap()) + .from(message.to.unwrap()[0].clone()) + .finalize(); - (app, state) + Ok(Some(response)) + } } + impl MessagePlugin for MockProtocol { + fn name(&self) -> &'static str { + "mock_protocol" + } + + fn didcomm_routes(&self) -> MessageRouter { + MessageRouter::new().register( + "https://didcomm.org/coordinate-mediation/2.0/keylist-update", + MockKeylistUpdateHandler, + ) + } + } + + static MOCK_PLUGINS: Lazy>> = + Lazy::new(|| vec![Arc::new(MockProtocol)]); + #[tokio::test] async fn test_keylist_update_via_didcomm() { + let mut container = MessagePluginContainer { + loaded: false, + collected_routes: vec![], + message_plugins: &MOCK_PLUGINS, + }; + + assert!(container.load().is_ok()); + + if let Err(_) = MESSAGE_CONTAINER.set(RwLock::new(container)) { + panic!("Failed to initialize MESSAGE_CONTAINER"); + } + let (app, state) = setup(); // Build message @@ -221,7 +212,10 @@ mod tests { .unwrap(); // Assert metadata - assert_eq!(response.type_, KEYLIST_UPDATE_RESPONSE_2_0); + assert_eq!( + response.type_, + "https://didcomm.org/coordinate-mediation/2.0/keylist-update-response" + ); assert_eq!(response.from.unwrap(), global::_mediator_did(&state)); assert_eq!(response.to.unwrap(), vec![global::_edge_did()]); diff --git a/src/lib.rs b/src/lib.rs index f934c616..51364f59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ pub mod plugins; use axum::Router; use eyre::{eyre, Result}; -use plugins::handler::PluginContainer; +use plugins::manager::PluginContainer; use tower_http::{catch_panic::CatchPanicLayer, trace::TraceLayer}; pub fn app() -> Result<(PluginContainer<'static>, Router)> { diff --git a/src/plugins.rs b/src/plugins.rs index 75f75937..34f69389 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -1,8 +1,8 @@ -pub(crate) mod handler; #[cfg(feature = "plugin-index")] pub(crate) mod index; +pub(crate) mod manager; -pub use handler::{PluginContainer, PluginContainerError}; +pub use manager::{PluginContainer, PluginContainerError}; use lazy_static::lazy_static; use std::sync::{Arc, Mutex}; @@ -19,7 +19,7 @@ lazy_static! { Arc::new(Mutex::new(oob_messages::plugin::OOBMessages {})), #[cfg(feature = "plugin-didcomm_messaging")] Arc::new(Mutex::new( - didcomm_messaging::plugin::MediatorCoordination::default() + didcomm_messaging::plugin::DidcommMessaging::default() )), ]; } diff --git a/src/plugins/handler.rs b/src/plugins/manager.rs similarity index 99% rename from src/plugins/handler.rs rename to src/plugins/manager.rs index f4fd74f7..9aec46f5 100644 --- a/src/plugins/handler.rs +++ b/src/plugins/manager.rs @@ -164,7 +164,7 @@ impl<'a> PluginContainer<'a> { Ok(self .collected_routes .iter() - .fold(Router::new(), |acc, e| acc.merge(e.clone()))) + .fold(Router::new(), |acc: Router, e| acc.merge(e.clone()))) } else { Err(PluginContainerError::Unloaded) }