From 3c9c441d2261c2a1bfa61bcc59a2e850242c29bc Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 1 Oct 2024 12:37:23 +0200 Subject: [PATCH 01/18] feat: add authentication to integrtion tests --- .../tests/integration_test.rs | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 affinidi-messaging-mediator/tests/integration_test.rs diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs new file mode 100644 index 0000000..4b1dcd1 --- /dev/null +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -0,0 +1,239 @@ +use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, DIDCacheClient}; +use affinidi_messaging_didcomm::{secrets::SecretsResolver, Message, PackEncryptedOptions}; +use affinidi_messaging_mediator::{resolvers::affinidi_secrets::AffinidiSecrets, server::start}; +use affinidi_messaging_sdk::{ + config::Config, + conversions::secret_from_str, + errors::ATMError, + messages::{AuthenticationChallenge, AuthorizationResponse, SuccessResponse}, +}; +use core::panic; +use reqwest::{Certificate, Client, ClientBuilder}; +use serde_json::json; +use std::time::SystemTime; +use uuid::Uuid; + +const MY_DID: &str = "did:peer:2.Vz6MkjS2RdXzLJqkRQYTUNi7G5vY7YqL1cXqoemERGADuz3dr.Ez6MknGys6JPaaCmwqwrakMfo1ehrsw8DN2vnFH3LvgUSje3T.EzQ3shu53gkVSo1PzDFyXDdm9LzzK7JnkTf5zM5oq37EatqBJY.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzciLCJhY2NlcHQiOlsiZGlkY29tbS92MiJdLCJyb3V0aW5nX2tleXMiOltdfSwiaWQiOm51bGx9"; +const MEDIATOR_API: &str = "https://localhost:7037/mediator/v1"; + +#[tokio::test] +async fn test_mediator_server() { + _start_mediator_server().await; + + let config = Config::builder() + .with_ssl_certificates(&mut vec![ + "../affinidi-messaging-mediator/conf/keys/client.chain".into(), + ]) + .build() + .unwrap(); + + // Signing and verification key + let v1 = json!({ + "crv": "Ed25519", + "d": "LLWCf83n8VsUYq31zlZRe0NNMCcn1N4Dh85dGpIqSFw", + "kty": "OKP", + "x": "Hn8T4ZjjT0oJ6rjhqox8AykwC3GDFsJF6KkaYZExwQo" + }); + + // Encryption key + let e1 = json!({ + "crv": "secp256k1", + "d": "oi-dXG4EqfNODFPjv2vkieoLdbQZH9k6dwPDV8HDoms", + "kty": "EC", + "x": "DhfaXbhwo0KkOiyA5V1K1RZx6Ikr86h_lX5GOwxjmjE", + "y": "PpYqybOwMsm64vftt-7gBCQPIUbglMmyy_6rloSSAPk" + }); + let did_resolver = DIDCacheClient::new(ClientConfigBuilder::default().build()) + .await + .unwrap(); + let secrets_resolver = AffinidiSecrets::new(vec![ + secret_from_str(&format!("{}#key-2", MY_DID), &e1), + secret_from_str(&format!("{}#key-1", MY_DID), &v1), + ]); + // Set up the HTTP/HTTPS client + let mut client = ClientBuilder::new() + .use_rustls_tls() + .https_only(true) + .user_agent("Affinidi Trusted Messaging"); + + for cert in config.get_ssl_certificates() { + client = + client.add_root_certificate(Certificate::from_der(cert.to_vec().as_slice()).unwrap()); + } + + let client = match client.build() { + Ok(client) => client, + Err(e) => return assert!(false, "{:?}", e), + }; + + let mediator_did = _well_known(client.clone()).await; + + let authentication_challenge = _authenticate_challenge(client.clone()).await; + + let auth_response_msg = _create_auth_challenge_response( + &authentication_challenge, + MY_DID, + &mediator_did, + "authenticate", + ); + let authentication_response = _authenticate( + client.clone(), + auth_response_msg, + MY_DID, + &mediator_did, + &did_resolver, + &secrets_resolver, + ) + .await; + + assert!(!authentication_response.access_token.is_empty()); + assert!(!authentication_response.refresh_token.is_empty()); +} + +async fn _start_mediator_server() { + tokio::spawn(async move { start().await }); + println!("Server running"); +} + +async fn _well_known(client: Client) -> String { + let well_known_did_atm_api = format!("{}/.well-known/did", MEDIATOR_API); + + let res = client + .get(well_known_did_atm_api) + .header("Content-Type", "application/json") + .send() + .await + .unwrap(); + + let status = res.status(); + assert_eq!(status, 200); + println!("API response: status({})", status); + let body = res.text().await.unwrap(); + + let body = serde_json::from_str::>(&body) + .ok() + .unwrap(); + let did = if let Some(did) = body.data { + did + } else { + panic!("Not able to fetch mediator did"); + }; + assert!(!did.is_empty()); + + did +} + +async fn _authenticate_challenge(client: Client) -> AuthenticationChallenge { + let res = client + .post(format!("{}/authenticate/challenge", MEDIATOR_API)) + .header("Content-Type", "application/json") + .body(format!("{{\"did\": \"{}\"}}", MY_DID).to_string()) + .send() + .await + .unwrap(); + + let status = res.status(); + assert_eq!(status, 200); + + let body = res.text().await.unwrap(); + + if !status.is_success() { + println!("Failed to get authentication challenge. Body: {:?}", body); + assert!( + false, + "Failed to get authentication challenge. Body: {:?}", + body + ); + } + let body = serde_json::from_str::>(&body) + .ok() + .unwrap(); + + let challenge = if let Some(challenge) = body.data { + challenge + } else { + panic!("No challenge received from ATM"); + }; + assert!(!challenge.challenge.is_empty()); + assert!(!challenge.session_id.is_empty()); + + challenge +} + +async fn _authenticate<'sr>( + client: Client, + auth_response: Message, + my_did: &str, + atm_did: &str, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> AuthorizationResponse { + let (auth_msg, _) = auth_response + .pack_encrypted( + atm_did, + Some(my_did), + Some(my_did), + did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .map_err(|e| { + ATMError::MsgSendError(format!( + "Couldn't pack authentication response message: {:?}", + e + )) + }) + .unwrap(); + + let res = client + .post(format!("{}/authenticate", MEDIATOR_API)) + .header("Content-Type", "application/json") + .body(auth_msg) + .send() + .await + .map_err(|e| { + ATMError::TransportError(format!("Could not post authentication response: {:?}", e)) + }) + .unwrap(); + + let status = res.status(); + println!("Authentication response: status({})", status); + + let body = res.text().await.unwrap(); + + if !status.is_success() { + println!("Failed to get authentication response. Body: {:?}", body); + panic!("Failed to get authentication response"); + } + let body = serde_json::from_str::>(&body).unwrap(); + + if let Some(tokens) = body.data { + return tokens.clone(); + } else { + panic!("No tokens received from ATM"); + } +} + +fn _create_auth_challenge_response( + body: &AuthenticationChallenge, + my_did: &str, + atm_did: &str, + protocol_message: &str, +) -> Message { + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + + Message::build( + Uuid::new_v4().into(), + format!("https://affinidi.com/atm/1.0/{}", protocol_message), + json!(body), + ) + .to(atm_did.to_owned()) + .from(my_did.to_owned()) + .created_time(now) + .expires_time(now + 60) + .finalize() +} From c575edcf8cd6a1eb9ce951303ecfa0697b5c3c98 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Fri, 4 Oct 2024 17:50:15 +0200 Subject: [PATCH 02/18] test: add integration tests covering different message types for /inbound --- .../tests/integration_test.rs | 524 +++++++++++++++++- 1 file changed, 518 insertions(+), 6 deletions(-) diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index 4b1dcd1..5d97d2a 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -1,25 +1,42 @@ use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, DIDCacheClient}; -use affinidi_messaging_didcomm::{secrets::SecretsResolver, Message, PackEncryptedOptions}; +use affinidi_messaging_didcomm::{ + envelope::MetaEnvelope, secrets::SecretsResolver, AttachmentData, Message, + PackEncryptedOptions, UnpackMetadata, UnpackOptions, +}; use affinidi_messaging_mediator::{resolvers::affinidi_secrets::AffinidiSecrets, server::start}; use affinidi_messaging_sdk::{ config::Config, conversions::secret_from_str, errors::ATMError, - messages::{AuthenticationChallenge, AuthorizationResponse, SuccessResponse}, + messages::{ + sending::InboundMessageResponse, AuthenticationChallenge, AuthorizationResponse, + GenericDataStruct, SuccessResponse, + }, + protocols::{ + message_pickup::{MessagePickupDeliveryRequest, MessagePickupStatusReply}, + trust_ping::TrustPingSent, + }, + transports::SendMessageResponse, }; +use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine}; use core::panic; use reqwest::{Certificate, Client, ClientBuilder}; -use serde_json::json; -use std::time::SystemTime; +use serde_json::{json, Value}; +use sha256::digest; +use std::time::{Duration, SystemTime}; +use tokio::time::sleep; use uuid::Uuid; -const MY_DID: &str = "did:peer:2.Vz6MkjS2RdXzLJqkRQYTUNi7G5vY7YqL1cXqoemERGADuz3dr.Ez6MknGys6JPaaCmwqwrakMfo1ehrsw8DN2vnFH3LvgUSje3T.EzQ3shu53gkVSo1PzDFyXDdm9LzzK7JnkTf5zM5oq37EatqBJY.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzciLCJhY2NlcHQiOlsiZGlkY29tbS92MiJdLCJyb3V0aW5nX2tleXMiOltdfSwiaWQiOm51bGx9"; +const MY_DID: &str = "did:peer:2.Vz6MkgWJfVmPELozq6aCycK3CpxHN8Upphn3WSuQkWY6iqsjF.EzQ3shfb7vwQaTJqFkt8nRfo7Nu98tmeYpdDfWgrqQitDaqXRz"; const MEDIATOR_API: &str = "https://localhost:7037/mediator/v1"; #[tokio::test] async fn test_mediator_server() { _start_mediator_server().await; + // Allow some time for the server to start + sleep(Duration::from_millis(1000)).await; + let config = Config::builder() .with_ssl_certificates(&mut vec![ "../affinidi-messaging-mediator/conf/keys/client.chain".into(), @@ -43,6 +60,7 @@ async fn test_mediator_server() { "x": "DhfaXbhwo0KkOiyA5V1K1RZx6Ikr86h_lX5GOwxjmjE", "y": "PpYqybOwMsm64vftt-7gBCQPIUbglMmyy_6rloSSAPk" }); + let did_resolver = DIDCacheClient::new(ClientConfigBuilder::default().build()) .await .unwrap(); @@ -50,7 +68,8 @@ async fn test_mediator_server() { secret_from_str(&format!("{}#key-2", MY_DID), &e1), secret_from_str(&format!("{}#key-1", MY_DID), &v1), ]); - // Set up the HTTP/HTTPS client + + // Set up the HTTPS client let mut client = ClientBuilder::new() .use_rustls_tls() .https_only(true) @@ -68,6 +87,7 @@ async fn test_mediator_server() { let mediator_did = _well_known(client.clone()).await; + // Start Authentication let authentication_challenge = _authenticate_challenge(client.clone()).await; let auth_response_msg = _create_auth_challenge_response( @@ -88,6 +108,129 @@ async fn test_mediator_server() { assert!(!authentication_response.access_token.is_empty()); assert!(!authentication_response.refresh_token.is_empty()); + + // POST /inbound + // MessageType=TrustPing + // Send signed ping and expecting response + let (signed_ping_msg, mut signed_ping_msg_info) = _build_ping_message( + &mediator_did, + MY_DID.into(), + true, + true, + &did_resolver, + &secrets_resolver, + ) + .await; + + let signed_ping_res: SendMessageResponse = _send_inbound_message( + client.clone(), + authentication_response.clone(), + &signed_ping_msg, + true, + 200, + ) + .await; + + signed_ping_msg_info.response = + if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Stored(m))) = + signed_ping_res + { + if let Some((_, msg_id)) = m.messages.first() { + Some(msg_id.to_owned()) + } else { + None + } + } else { + None + }; + + let pong_msg_id = signed_ping_msg_info.response.unwrap(); + assert!(!pong_msg_id.is_empty()); + + // Send anonymous ping + let (anon_ping_msg, _) = _build_ping_message( + &mediator_did, + MY_DID.into(), + false, + false, + &did_resolver, + &secrets_resolver, + ) + .await; + let _anon_ping_res: SendMessageResponse = _send_inbound_message( + client.clone(), + authentication_response.clone(), + &anon_ping_msg, + false, + 500, + ) + .await; + + // MessageType=MessagePickupStatusRequest + let status_request_msg = _build_status_request_message( + &mediator_did, + MY_DID.into(), + &did_resolver, + &secrets_resolver, + ) + .await; + let status_reply: SendMessageResponse = _send_inbound_message( + client.clone(), + authentication_response.clone(), + &status_request_msg, + false, + 200, + ) + .await; + _validate_status_reply(status_reply, &did_resolver, &secrets_resolver).await; + + // MessageType=MessagePickupDeliveryRequest + let delivery_request_msg = _build_delivery_request_message( + &mediator_did, + MY_DID.into(), + &did_resolver, + &secrets_resolver, + ) + .await; + let message_delivery: SendMessageResponse = _send_inbound_message( + client.clone(), + authentication_response.clone(), + &delivery_request_msg, + true, + 200, + ) + .await; + let message_received_ids = _validate_message_delivery( + message_delivery, + &did_resolver, + &secrets_resolver, + &pong_msg_id, + ) + .await; + + // MessageType=MessagePickupMessagesReceived + let message_received_msg = _build_message_received_message( + &mediator_did, + &did_resolver, + &secrets_resolver, + message_received_ids.clone(), + ) + .await; + let message_received_status_reply: SendMessageResponse = + _send_inbound_message( + client.clone(), + authentication_response.clone(), + &message_received_msg, + true, + 200, + ) + .await; + _validate_message_received_status_reply( + message_received_status_reply, + &did_resolver, + &secrets_resolver, + ) + .await } async fn _start_mediator_server() { @@ -198,6 +341,7 @@ async fn _authenticate<'sr>( .unwrap(); let status = res.status(); + assert!(status.is_success()); println!("Authentication response: status({})", status); let body = res.text().await.unwrap(); @@ -237,3 +381,371 @@ fn _create_auth_challenge_response( .expires_time(now + 60) .finalize() } + +async fn _build_ping_message<'sr>( + to_did: &str, + my_did: String, + signed: bool, + expect_response: bool, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> (String, TrustPingSent) { + let now = _get_time_now(); + + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/trust-ping/2.0/ping".to_owned(), + json!({"response_requested": expect_response}), + ) + .to(to_did.to_owned()); + let from_did = if !signed { + // Can support anonymous pings + None + } else { + msg = msg.from(my_did.clone()); + Some(my_did.clone()) + }; + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + let mut msg_info = TrustPingSent { + message_id: msg.id.clone(), + message_hash: "".to_string(), + bytes: 0, + response: None, + }; + let (msg, _) = msg + .pack_encrypted( + to_did, + from_did.as_deref(), + from_did.as_deref(), + did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg_info.message_hash = digest(&msg).to_string(); + msg_info.bytes = msg.len() as u32; + + (msg, msg_info) +} + +async fn _build_status_request_message<'sr>( + mediator_did: &str, + recipient_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> String { + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/messagepickup/3.0/status-request".to_owned(), + json!({}), + ) + .header("return_route".into(), Value::String("all".into())); + + msg = msg.body(json!({"recipient_did": recipient_did })); + + let to_did = mediator_did; + + msg = msg.to(to_did.to_owned()); + + msg = msg.from(MY_DID.into()); + let now = _get_time_now(); + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + let (msg, _) = msg + .pack_encrypted( + &to_did, + Some(MY_DID), + Some(MY_DID), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg +} + +async fn _validate_status_reply( + status_reply: SendMessageResponse, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) where + S: SecretsResolver + Send, +{ + if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = + status_reply + { + let (message, _) = Message::unpack_string( + &message, + &did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + let status: MessagePickupStatusReply = + serde_json::from_value(message.body.clone()).unwrap(); + assert!(!status.live_delivery); + assert!(status.longest_waited_seconds.unwrap() > 0); + assert!(status.message_count == 1); + assert!(status.recipient_did == MY_DID); + assert!(status.total_bytes > 0); + } +} + +async fn _build_delivery_request_message<'sr>( + mediator_did: &str, + recipient_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> String { + let body = MessagePickupDeliveryRequest { + recipient_did: recipient_did, + limit: 10, + }; + + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/messagepickup/3.0/delivery-request".to_owned(), + serde_json::to_value(body).unwrap(), + ) + .header("return_route".into(), Value::String("all".into())); + + let to_did = mediator_did; + msg = msg.to(to_did.to_owned()); + + msg = msg.from(MY_DID.to_owned()); + let now = _get_time_now(); + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + let msg_id = msg.id.clone(); + + // Pack the message + let (msg, _) = msg + .pack_encrypted( + &to_did, + Some(MY_DID), + Some(MY_DID), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg +} + +async fn _handle_delivery( + message: &Message, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) -> Vec<(Message, UnpackMetadata)> +where + S: SecretsResolver + Send, +{ + let mut response: Vec<(Message, UnpackMetadata)> = Vec::new(); + + if let Some(attachments) = &message.attachments { + for attachment in attachments { + match &attachment.data { + AttachmentData::Base64 { value } => { + let decoded = match BASE64_URL_SAFE_NO_PAD.decode(value.base64.clone()) { + Ok(decoded) => match String::from_utf8(decoded) { + Ok(decoded) => decoded, + Err(e) => { + assert!(false, "{:?}", e); + "".into() + } + }, + Err(e) => { + assert!(false, "{:?}", e); + continue; + } + }; + let mut envelope = + match MetaEnvelope::new(&decoded, &did_resolver, secrets_resolver).await { + Ok(envelope) => envelope, + Err(e) => { + assert!(false, "{:?}", e); + continue; + } + }; + + match Message::unpack( + &mut envelope, + did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + { + Ok((mut m, u)) => { + if let Some(attachment_id) = &attachment.id { + m.id = attachment_id.to_string(); + } + response.push((m, u)) + } + Err(e) => { + assert!(false, "{:?}", e); + continue; + } + }; + } + _ => { + assert!(false); + continue; + } + }; + } + } + + response +} + +async fn _validate_message_delivery( + message_delivery: SendMessageResponse, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, + pong_msg_id: &str, +) -> Vec +where + S: SecretsResolver + Send, +{ + if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = + message_delivery + { + let (message, _) = Message::unpack_string( + &message, + &did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + + let messages = _handle_delivery(&message, did_resolver, secrets_resolver).await; + let mut to_delete_ids: Vec = Vec::new(); + + assert_eq!(messages.first().unwrap().0.id, pong_msg_id); + + for (message, _) in messages { + to_delete_ids.push(message.id.clone()); + } + to_delete_ids + } else { + vec![] + } +} + +async fn _build_message_received_message<'sr>( + mediator_did: &str, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), + to_delete_list: Vec, +) -> String { + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/messagepickup/3.0/messages-received".to_owned(), + json!({"message_id_list": to_delete_list}), + ) + .header("return_route".into(), Value::String("all".into())); + + let to_did = mediator_did; + msg = msg.to(to_did.to_owned()); + + msg = msg.from(MY_DID.into()); + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + + // Pack the message + let (msg, _) = msg + .pack_encrypted( + &to_did, + Some(MY_DID), + Some(MY_DID), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg +} + +async fn _validate_message_received_status_reply( + status_reply: SendMessageResponse, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) where + S: SecretsResolver + Send, +{ + if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = + status_reply + { + let (message, _) = Message::unpack_string( + &message, + &did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + let status: MessagePickupStatusReply = + serde_json::from_value(message.body.clone()).unwrap(); + assert!(!status.live_delivery); + assert!(status.longest_waited_seconds.is_none()); + assert!(status.message_count == 0); + assert!(status.recipient_did == MY_DID); + assert!(status.total_bytes == 0); + } +} + +async fn _send_inbound_message( + client: Client, + tokens: AuthorizationResponse, + message: &str, + return_response: bool, + expected_status_code: u16, +) -> SendMessageResponse +where + T: GenericDataStruct, +{ + let msg = message.to_owned(); + + let res = client + .post(format!("{}/inbound", MEDIATOR_API)) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", tokens.access_token)) + .body(msg) + .send() + .await + .unwrap(); + + let status = res.status(); + println!("API response: status({})", status); + assert_eq!(status, expected_status_code); + + let body = res.text().await.unwrap(); + + let http_response: Option = if return_response { + let r: SuccessResponse = serde_json::from_str(&body).unwrap(); + r.data + } else { + None + }; + + SendMessageResponse::RestAPI(http_response) +} + +fn _get_time_now() -> u64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() +} From a58945eea4b331cf3a901f296dc3c0e18ee0c41b Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Mon, 7 Oct 2024 14:03:43 +0200 Subject: [PATCH 03/18] test: add integration tests to /outbound endpoint --- .../tests/integration_test.rs | 370 +++++++++++++++--- 1 file changed, 312 insertions(+), 58 deletions(-) diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index 5d97d2a..781aa45 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -1,6 +1,6 @@ use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, DIDCacheClient}; use affinidi_messaging_didcomm::{ - envelope::MetaEnvelope, secrets::SecretsResolver, AttachmentData, Message, + envelope::MetaEnvelope, secrets::SecretsResolver, Attachment, AttachmentData, Message, PackEncryptedOptions, UnpackMetadata, UnpackOptions, }; use affinidi_messaging_mediator::{resolvers::affinidi_secrets::AffinidiSecrets, server::start}; @@ -10,7 +10,7 @@ use affinidi_messaging_sdk::{ errors::ATMError, messages::{ sending::InboundMessageResponse, AuthenticationChallenge, AuthorizationResponse, - GenericDataStruct, SuccessResponse, + GenericDataStruct, GetMessagesRequest, GetMessagesResponse, SuccessResponse, }, protocols::{ message_pickup::{MessagePickupDeliveryRequest, MessagePickupStatusReply}, @@ -29,6 +29,7 @@ use uuid::Uuid; const MY_DID: &str = "did:peer:2.Vz6MkgWJfVmPELozq6aCycK3CpxHN8Upphn3WSuQkWY6iqsjF.EzQ3shfb7vwQaTJqFkt8nRfo7Nu98tmeYpdDfWgrqQitDaqXRz"; const MEDIATOR_API: &str = "https://localhost:7037/mediator/v1"; +const BOB_DID: &str = "did:peer:2.Vz6Mkihn2R3M8nY62EFJ7MAVXu7YxsTnuS5iAhmn3qKJbkdFf.EzQ3shpZRBUtewwzYiueXgDqs1bvGNkSyGoRgsbZJXt3TTb9jD.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzcvIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXSwicm91dGluZ19rZXlzIjpbXX0sImlkIjpudWxsfQ"; #[tokio::test] async fn test_mediator_server() { @@ -45,7 +46,7 @@ async fn test_mediator_server() { .unwrap(); // Signing and verification key - let v1 = json!({ + let my_v1 = json!({ "crv": "Ed25519", "d": "LLWCf83n8VsUYq31zlZRe0NNMCcn1N4Dh85dGpIqSFw", "kty": "OKP", @@ -53,7 +54,7 @@ async fn test_mediator_server() { }); // Encryption key - let e1 = json!({ + let my_e1 = json!({ "crv": "secp256k1", "d": "oi-dXG4EqfNODFPjv2vkieoLdbQZH9k6dwPDV8HDoms", "kty": "EC", @@ -61,53 +62,72 @@ async fn test_mediator_server() { "y": "PpYqybOwMsm64vftt-7gBCQPIUbglMmyy_6rloSSAPk" }); + let bob_v1 = json!({ + "crv": "Ed25519", + "d": "FZMJijqdcp7PCQShgtFj6Ud3vjZY7jFZBVvahziaMMM", + "kty": "OKP", + "x": "PybG95kyeSfGRebp4T7hzA7JQuysc6mZ97nM2ety6Vo" + }); + let bob_e1 = json!({ + "crv": "secp256k1", + "d": "ai7B5fgT3pCBHec0I4Y1xXpSyrEHlTy0hivSlddWHZE", + "kty": "EC", + "x": "k2FhEi8WMxr4Ztr4u2xjKzDESqVnGg_WKrN1820wPeA", + "y": "fq0DnZ_duPWyeFK0k93bAzjNJVVHEjHFRlGOJXKDS18" + }); + let did_resolver = DIDCacheClient::new(ClientConfigBuilder::default().build()) .await .unwrap(); - let secrets_resolver = AffinidiSecrets::new(vec![ - secret_from_str(&format!("{}#key-2", MY_DID), &e1), - secret_from_str(&format!("{}#key-1", MY_DID), &v1), + let my_secrets_resolver = AffinidiSecrets::new(vec![ + secret_from_str(&format!("{}#key-1", MY_DID), &my_v1), + secret_from_str(&format!("{}#key-2", MY_DID), &my_e1), + ]); + let bob_secrets_resolver = AffinidiSecrets::new(vec![ + secret_from_str(&format!("{}#key-1", BOB_DID), &bob_v1), + secret_from_str(&format!("{}#key-2", BOB_DID), &bob_e1), ]); - // Set up the HTTPS client - let mut client = ClientBuilder::new() - .use_rustls_tls() - .https_only(true) - .user_agent("Affinidi Trusted Messaging"); - - for cert in config.get_ssl_certificates() { - client = - client.add_root_certificate(Certificate::from_der(cert.to_vec().as_slice()).unwrap()); - } - - let client = match client.build() { - Ok(client) => client, - Err(e) => return assert!(false, "{:?}", e), - }; + let client = init_client(config.clone()); let mediator_did = _well_known(client.clone()).await; // Start Authentication - let authentication_challenge = _authenticate_challenge(client.clone()).await; - - let auth_response_msg = _create_auth_challenge_response( - &authentication_challenge, + let my_authentication_challenge = _authenticate_challenge(client.clone(), MY_DID).await; + let bob_authentication_challenge = _authenticate_challenge(client.clone(), BOB_DID).await; + println!("Auth ch: {:#?}", my_authentication_challenge); + println!("Auth ch: {:#?}", bob_authentication_challenge); + // /authenticate/challenge + let my_auth_response_msg = + _create_auth_challenge_response(&my_authentication_challenge, MY_DID, &mediator_did); + + let bob_auth_response_msg = + _create_auth_challenge_response(&bob_authentication_challenge, BOB_DID, &mediator_did); + + // /authenticate + let my_authentication_response = _authenticate( + client.clone(), + my_auth_response_msg, MY_DID, &mediator_did, - "authenticate", - ); - let authentication_response = _authenticate( + &did_resolver, + &my_secrets_resolver, + ) + .await; + let bob_authentication_response = _authenticate( client.clone(), - auth_response_msg, - MY_DID, + bob_auth_response_msg, + BOB_DID, &mediator_did, &did_resolver, - &secrets_resolver, + &bob_secrets_resolver, ) .await; - assert!(!authentication_response.access_token.is_empty()); - assert!(!authentication_response.refresh_token.is_empty()); + assert!(!my_authentication_response.access_token.is_empty()); + assert!(!my_authentication_response.refresh_token.is_empty()); + assert!(!bob_authentication_response.access_token.is_empty()); + assert!(!bob_authentication_response.refresh_token.is_empty()); // POST /inbound // MessageType=TrustPing @@ -118,13 +138,13 @@ async fn test_mediator_server() { true, true, &did_resolver, - &secrets_resolver, + &my_secrets_resolver, ) .await; let signed_ping_res: SendMessageResponse = _send_inbound_message( client.clone(), - authentication_response.clone(), + my_authentication_response.clone(), &signed_ping_msg, true, 200, @@ -154,12 +174,12 @@ async fn test_mediator_server() { false, false, &did_resolver, - &secrets_resolver, + &my_secrets_resolver, ) .await; let _anon_ping_res: SendMessageResponse = _send_inbound_message( client.clone(), - authentication_response.clone(), + my_authentication_response.clone(), &anon_ping_msg, false, 500, @@ -171,30 +191,30 @@ async fn test_mediator_server() { &mediator_did, MY_DID.into(), &did_resolver, - &secrets_resolver, + &my_secrets_resolver, ) .await; let status_reply: SendMessageResponse = _send_inbound_message( client.clone(), - authentication_response.clone(), + my_authentication_response.clone(), &status_request_msg, false, 200, ) .await; - _validate_status_reply(status_reply, &did_resolver, &secrets_resolver).await; + _validate_status_reply(status_reply, &did_resolver, &my_secrets_resolver).await; // MessageType=MessagePickupDeliveryRequest let delivery_request_msg = _build_delivery_request_message( &mediator_did, MY_DID.into(), &did_resolver, - &secrets_resolver, + &my_secrets_resolver, ) .await; let message_delivery: SendMessageResponse = _send_inbound_message( client.clone(), - authentication_response.clone(), + my_authentication_response.clone(), &delivery_request_msg, true, 200, @@ -203,7 +223,7 @@ async fn test_mediator_server() { let message_received_ids = _validate_message_delivery( message_delivery, &did_resolver, - &secrets_resolver, + &my_secrets_resolver, &pong_msg_id, ) .await; @@ -212,14 +232,14 @@ async fn test_mediator_server() { let message_received_msg = _build_message_received_message( &mediator_did, &did_resolver, - &secrets_resolver, + &my_secrets_resolver, message_received_ids.clone(), ) .await; let message_received_status_reply: SendMessageResponse = _send_inbound_message( client.clone(), - authentication_response.clone(), + my_authentication_response.clone(), &message_received_msg, true, 200, @@ -228,9 +248,73 @@ async fn test_mediator_server() { _validate_message_received_status_reply( message_received_status_reply, &did_resolver, - &secrets_resolver, + &my_secrets_resolver, + ) + .await; + + // MessageType=ForwardRequest + let forward_request_msg = _build_forward_request_message( + &mediator_did, + MY_DID.into(), + BOB_DID.into(), + &did_resolver, + &bob_secrets_resolver, + ) + .await; + + let forward_request_response: SendMessageResponse = + _send_inbound_message( + client.clone(), + my_authentication_response.clone(), + &forward_request_msg, + true, + 200, + ) + .await; + + let forwarded_msg_id = _validate_forward_request_response(forward_request_response).await; + // /outbound + // delete messages: FALSE + let get_message_no_delete_request = GetMessagesRequest { + message_ids: vec![forwarded_msg_id.clone()], + delete: false, + }; + let msg_list = _outbound_message( + client.clone(), + &get_message_no_delete_request, + my_authentication_response.clone(), + 200, + false, + ) + .await; + + _validate_get_message_response(msg_list, MY_DID, &did_resolver, &my_secrets_resolver).await; + + // delete messages: TRUE + let get_message_delete_request = GetMessagesRequest { + message_ids: vec![forwarded_msg_id.clone()], + delete: true, + }; + let msg_list = _outbound_message( + client.clone(), + &get_message_delete_request, + my_authentication_response.clone(), + 200, + false, ) - .await + .await; + + _validate_get_message_response(msg_list, MY_DID, &did_resolver, &my_secrets_resolver).await; + + // get message should return not found + let msg_list = _outbound_message( + client.clone(), + &get_message_delete_request, + my_authentication_response.clone(), + 200, + true, + ) + .await; } async fn _start_mediator_server() { @@ -238,6 +322,28 @@ async fn _start_mediator_server() { println!("Server running"); } +fn init_client(config: Config<'_>) -> Client { + // Set up the HTTPS client + let mut client = ClientBuilder::new() + .use_rustls_tls() + .https_only(true) + .user_agent("Affinidi Trusted Messaging"); + + for cert in config.get_ssl_certificates() { + client = + client.add_root_certificate(Certificate::from_der(cert.to_vec().as_slice()).unwrap()); + } + + let client = match client.build() { + Ok(client) => client, + Err(e) => { + assert!(false, "{:?}", e); + panic!(); + } + }; + client +} + async fn _well_known(client: Client) -> String { let well_known_did_atm_api = format!("{}/.well-known/did", MEDIATOR_API); @@ -266,11 +372,11 @@ async fn _well_known(client: Client) -> String { did } -async fn _authenticate_challenge(client: Client) -> AuthenticationChallenge { +async fn _authenticate_challenge(client: Client, did: &str) -> AuthenticationChallenge { let res = client .post(format!("{}/authenticate/challenge", MEDIATOR_API)) .header("Content-Type", "application/json") - .body(format!("{{\"did\": \"{}\"}}", MY_DID).to_string()) + .body(format!("{{\"did\": \"{}\"}}", did).to_string()) .send() .await .unwrap(); @@ -341,11 +447,12 @@ async fn _authenticate<'sr>( .unwrap(); let status = res.status(); - assert!(status.is_success()); println!("Authentication response: status({})", status); let body = res.text().await.unwrap(); + assert!(status.is_success()); + if !status.is_success() { println!("Failed to get authentication response. Body: {:?}", body); panic!("Failed to get authentication response"); @@ -363,16 +470,12 @@ fn _create_auth_challenge_response( body: &AuthenticationChallenge, my_did: &str, atm_did: &str, - protocol_message: &str, ) -> Message { - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); + let now = _get_time_now(); Message::build( Uuid::new_v4().into(), - format!("https://affinidi.com/atm/1.0/{}", protocol_message), + "https://affinidi.com/atm/1.0/authenticate".to_owned(), json!(body), ) .to(atm_did.to_owned()) @@ -519,7 +622,6 @@ async fn _build_delivery_request_message<'sr>( msg = msg.from(MY_DID.to_owned()); let now = _get_time_now(); let msg = msg.created_time(now).expires_time(now + 300).finalize(); - let msg_id = msg.id.clone(); // Pack the message let (msg, _) = msg @@ -698,6 +800,7 @@ async fn _validate_message_received_status_reply( .unwrap(); let status: MessagePickupStatusReply = serde_json::from_value(message.body.clone()).unwrap(); + assert!(!status.live_delivery); assert!(status.longest_waited_seconds.is_none()); assert!(status.message_count == 0); @@ -706,6 +809,94 @@ async fn _validate_message_received_status_reply( } } +async fn _build_forward_request_message<'sr>( + mediator_did: &str, + recipient_did: String, + actor_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> String { + let now = _get_time_now(); + + let recipient_did = recipient_did; + + let msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/routing/2.0/forward".to_owned(), + json!({ "next": recipient_did }), + ) + .to(mediator_did.to_owned()) + .from(actor_did.clone()) + .attachment( + Attachment::json(json!({ "message": "plaintext attachment, mediator can read this" })) + .finalize(), + ) + .attachment( + Attachment::base64(String::from( + "ciphertext and iv which is encrypted by the recipient public key", + )) + .finalize(), + ); + + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + + // Pack the message + let (msg, _) = msg + .pack_encrypted( + &mediator_did.to_owned(), + Some(&actor_did.clone()), + Some(&actor_did.clone()), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + msg +} + +async fn _validate_forward_request_response( + forward_request_response: SendMessageResponse, +) -> String { + let msg_id = if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Stored(m))) = + forward_request_response + { + if let Some((_, msg_id)) = m.messages.first() { + Some(msg_id.to_owned()) + } else { + None + } + } else { + None + }; + + assert!(!msg_id.is_none()); + + msg_id.unwrap() +} + +async fn _validate_get_message_response( + list: GetMessagesResponse, + my_did: &str, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) where + S: SecretsResolver + Send, +{ + for msg in list.success { + assert_eq!(msg.to_address.unwrap(), digest(my_did)); + let _ = Message::unpack_string( + &msg.msg.unwrap(), + did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + println!("Msg id: {}", msg.msg_id); + } +} + async fn _send_inbound_message( client: Client, tokens: AuthorizationResponse, @@ -743,6 +934,69 @@ where SendMessageResponse::RestAPI(http_response) } +async fn _outbound_message( + client: Client, + messages: &GetMessagesRequest, + tokens: AuthorizationResponse, + expected_status_code: u16, + expecting_get_errors: bool, +) -> GetMessagesResponse { + let body = serde_json::to_string(messages) + .map_err(|e| { + ATMError::TransportError(format!("Could not serialize get message request: {:?}", e)) + }) + .unwrap(); + + let res = client + .post(format!("{}/outbound", MEDIATOR_API)) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", tokens.access_token)) + .body(body) + .send() + .await + .map_err(|e| { + ATMError::TransportError(format!("Could not send get_messages request: {:?}", e)) + }) + .unwrap(); + + let status = res.status(); + println!("API response: status({})", status); + assert_eq!(status, expected_status_code); + let body = res + .text() + .await + .map_err(|e| ATMError::TransportError(format!("Couldn't get body: {:?}", e))) + .unwrap(); + + let body = serde_json::from_str::>(&body) + .ok() + .unwrap(); + + let list = if let Some(list) = body.data { + list + } else { + panic!("No messages found") + }; + + if expecting_get_errors { + assert!(!list.get_errors.is_empty()) + } else { + assert!(list.get_errors.is_empty()) + } + + if !list.get_errors.is_empty() { + for (msg, err) in &list.get_errors { + println!("failed get: msg({}) error({})", msg, err); + } + } + if !list.delete_errors.is_empty() { + for (msg, err) in &list.delete_errors { + println!("failed delete: msg({}) error({})", msg, err); + } + } + list +} + fn _get_time_now() -> u64 { SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) From b9f47b410093ee6f1165ee80732ed4ce4a8aea4f Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Mon, 7 Oct 2024 17:04:43 +0200 Subject: [PATCH 04/18] feat: add integration tests for /list, /fetch and delete --- .../tests/integration_test.rs | 243 +++++++++++++++++- 1 file changed, 240 insertions(+), 3 deletions(-) diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index 781aa45..8698961 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -9,8 +9,10 @@ use affinidi_messaging_sdk::{ conversions::secret_from_str, errors::ATMError, messages::{ - sending::InboundMessageResponse, AuthenticationChallenge, AuthorizationResponse, - GenericDataStruct, GetMessagesRequest, GetMessagesResponse, SuccessResponse, + fetch::FetchOptions, sending::InboundMessageResponse, AuthenticationChallenge, + AuthorizationResponse, DeleteMessageRequest, DeleteMessageResponse, Folder, + GenericDataStruct, GetMessagesRequest, GetMessagesResponse, MessageList, + MessageListElement, SuccessResponse, }, protocols::{ message_pickup::{MessagePickupDeliveryRequest, MessagePickupStatusReply}, @@ -307,7 +309,7 @@ async fn test_mediator_server() { _validate_get_message_response(msg_list, MY_DID, &did_resolver, &my_secrets_resolver).await; // get message should return not found - let msg_list = _outbound_message( + let _msg_list = _outbound_message( client.clone(), &get_message_delete_request, my_authentication_response.clone(), @@ -315,6 +317,82 @@ async fn test_mediator_server() { true, ) .await; + + // Sending messages to list/fetch + for _ in 0..3 { + let (signed_ping_msg, _) = _build_ping_message( + &mediator_did, + MY_DID.into(), + true, + true, + &did_resolver, + &my_secrets_resolver, + ) + .await; + + let _: SendMessageResponse = _send_inbound_message( + client.clone(), + my_authentication_response.clone(), + &signed_ping_msg, + true, + 200, + ) + .await; + } + + // /list/:did_hash/:folder + // /list/:did_hash/Inbox + let msgs_list = list_messages( + client.clone(), + my_authentication_response.clone(), + 200, + MY_DID, + Folder::Inbox, + ) + .await; + _validate_list_messages(msgs_list, &mediator_did); + + // /list/:did_hash/Outbox + let msgs_list = list_messages( + client.clone(), + my_authentication_response.clone(), + 200, + MY_DID, + Folder::Outbox, + ) + .await; + assert_eq!(msgs_list.len(), 0); + + // /fetch + let messages = fetch_messages( + client.clone(), + my_authentication_response.clone(), + 200, + &FetchOptions { + limit: 10, + start_id: None, + delete_policy: affinidi_messaging_sdk::messages::FetchDeletePolicy::DoNotDelete, + }, + ) + .await; + assert_eq!(messages.success.len(), 3); + + let msg_ids: Vec = messages + .success + .iter() + .map(|msg| msg.msg_id.clone()) + .collect(); + + let deleted_msgs = _delete_messages( + client.clone(), + my_authentication_response.clone(), + 200, + &DeleteMessageRequest { + message_ids: msg_ids, + }, + ) + .await; + assert_eq!(deleted_msgs.success.len(), 3); } async fn _start_mediator_server() { @@ -997,6 +1075,165 @@ async fn _outbound_message( list } +fn _validate_list_messages(list: Vec, mediator_did: &str) { + assert_eq!(list.len(), 3); + + for msg in list { + assert_eq!(msg.from_address.unwrap(), mediator_did); + } +} + +async fn list_messages( + client: Client, + tokens: AuthorizationResponse, + expected_status_code: u16, + my_did: &str, + folder: Folder, +) -> Vec { + let res = client + .get(format!( + "{}/list/{}/{}", + MEDIATOR_API, + digest(my_did), + folder, + )) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", tokens.access_token)) + .send() + .await + .map_err(|e| { + ATMError::TransportError(format!("Could not send list_messages request: {:?}", e)) + }) + .unwrap(); + + let status = res.status(); + println!("API response: status({})", status); + assert_eq!(status, expected_status_code); + + let body = res + .text() + .await + .map_err(|e| ATMError::TransportError(format!("Couldn't get body: {:?}", e))) + .unwrap(); + + let body = serde_json::from_str::>(&body) + .ok() + .unwrap(); + + let list = if let Some(list) = body.data { + list + } else { + panic!("No messages found"); + }; + + list +} + +async fn fetch_messages( + client: Client, + tokens: AuthorizationResponse, + expected_status_code: u16, + options: &FetchOptions, +) -> GetMessagesResponse { + let body = serde_json::to_string(options) + .map_err(|e| { + ATMError::TransportError(format!( + "Could not serialize fetch_message() options: {:?}", + e + )) + }) + .unwrap(); + + let res = client + .post(format!("{}/fetch", MEDIATOR_API)) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", tokens.access_token)) + .body(body) + .send() + .await + .map_err(|e| { + ATMError::TransportError(format!("Could not send list_messages request: {:?}", e)) + }) + .unwrap(); + + let status = res.status(); + println!("API response: status({})", status); + assert_eq!(status, expected_status_code); + + let body = res + .text() + .await + .map_err(|e| ATMError::TransportError(format!("Couldn't get body: {:?}", e))) + .unwrap(); + + let body = serde_json::from_str::>(&body) + .ok() + .unwrap(); + + let list = if let Some(list) = body.data { + list + } else { + panic!("No messages found"); + }; + list +} + +async fn _delete_messages( + client: Client, + tokens: AuthorizationResponse, + expected_status_code: u16, + messages: &DeleteMessageRequest, +) -> DeleteMessageResponse { + let msg = serde_json::to_string(messages) + .map_err(|e| { + ATMError::TransportError(format!( + "Could not serialize delete message request: {:?}", + e + )) + }) + .unwrap(); + + let res = client + .delete(format!("{}/delete", MEDIATOR_API)) + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", tokens.access_token)) + .body(msg) + .send() + .await + .map_err(|e| { + ATMError::TransportError(format!("Could not send delete_messages request: {:?}", e)) + }) + .unwrap(); + + let status = res.status(); + println!("API response: status({})", status); + assert_eq!(status, expected_status_code); + + let body = res + .text() + .await + .map_err(|e| ATMError::TransportError(format!("Couldn't get body: {:?}", e))) + .unwrap(); + + let body = serde_json::from_str::>(&body) + .ok() + .unwrap(); + + let list = if let Some(list) = body.data { + list + } else { + panic!("No messages found"); + }; + + if !list.errors.is_empty() { + for (msg, err) in &list.errors { + println!("failed: msg({}) error({})", msg, err); + } + panic!("Failed to delete above messages") + } + + list +} fn _get_time_now() -> u64 { SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) From 897de823518c6eae29550f4179f1ff83bf1165e2 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Mon, 7 Oct 2024 18:02:36 +0200 Subject: [PATCH 05/18] chore: refactor tests --- Cargo.lock | 1 + affinidi-messaging-mediator/Cargo.toml | 1 + affinidi-messaging-mediator/tests/common.rs | 40 +++ .../tests/integration_test.rs | 317 ++---------------- .../tests/message_builders.rs | 252 ++++++++++++++ 5 files changed, 320 insertions(+), 291 deletions(-) create mode 100644 affinidi-messaging-mediator/tests/common.rs create mode 100644 affinidi-messaging-mediator/tests/message_builders.rs diff --git a/Cargo.lock b/Cargo.lock index 1f4678c..56a6534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,6 +143,7 @@ dependencies = [ "http 1.1.0", "itertools 0.13.0", "jsonwebtoken", + "lazy_static", "rand", "rcgen", "redis", diff --git a/affinidi-messaging-mediator/Cargo.toml b/affinidi-messaging-mediator/Cargo.toml index 9873f07..1a772f2 100644 --- a/affinidi-messaging-mediator/Cargo.toml +++ b/affinidi-messaging-mediator/Cargo.toml @@ -56,4 +56,5 @@ rcgen = { version = "0.13", default-features = false, features = [ "aws_lc_rs", "pem", ] } +lazy_static = "1.4.0" time = "0.3" diff --git a/affinidi-messaging-mediator/tests/common.rs b/affinidi-messaging-mediator/tests/common.rs new file mode 100644 index 0000000..6dfe19e --- /dev/null +++ b/affinidi-messaging-mediator/tests/common.rs @@ -0,0 +1,40 @@ +use lazy_static::lazy_static; +use serde_json::{json, Value}; + +pub const MY_DID: &str = "did:peer:2.Vz6MkgWJfVmPELozq6aCycK3CpxHN8Upphn3WSuQkWY6iqsjF.EzQ3shfb7vwQaTJqFkt8nRfo7Nu98tmeYpdDfWgrqQitDaqXRz"; +pub const MEDIATOR_API: &str = "https://localhost:7037/mediator/v1"; +pub const BOB_DID: &str = "did:peer:2.Vz6Mkihn2R3M8nY62EFJ7MAVXu7YxsTnuS5iAhmn3qKJbkdFf.EzQ3shpZRBUtewwzYiueXgDqs1bvGNkSyGoRgsbZJXt3TTb9jD.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzcvIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXSwicm91dGluZ19rZXlzIjpbXX0sImlkIjpudWxsfQ"; +lazy_static! { +// Signing and verification key +pub static ref MY_V1: Value = json!({ + "crv": "Ed25519", + "d": "LLWCf83n8VsUYq31zlZRe0NNMCcn1N4Dh85dGpIqSFw", + "kty": "OKP", + "x": "Hn8T4ZjjT0oJ6rjhqox8AykwC3GDFsJF6KkaYZExwQo" +}); + +// Encryption key +pub static ref MY_E1: Value = json!({ + "crv": "secp256k1", + "d": "oi-dXG4EqfNODFPjv2vkieoLdbQZH9k6dwPDV8HDoms", + "kty": "EC", + "x": "DhfaXbhwo0KkOiyA5V1K1RZx6Ikr86h_lX5GOwxjmjE", + "y": "PpYqybOwMsm64vftt-7gBCQPIUbglMmyy_6rloSSAPk" +}); + +pub static ref BOB_V1: Value = json!({ + "crv": "Ed25519", + "d": "FZMJijqdcp7PCQShgtFj6Ud3vjZY7jFZBVvahziaMMM", + "kty": "OKP", + "x": "PybG95kyeSfGRebp4T7hzA7JQuysc6mZ97nM2ety6Vo" +}); + +pub static ref BOB_E1: Value = json!({ + "crv": "secp256k1", + "d": "ai7B5fgT3pCBHec0I4Y1xXpSyrEHlTy0hivSlddWHZE", + "kty": "EC", + "x": "k2FhEi8WMxr4Ztr4u2xjKzDESqVnGg_WKrN1820wPeA", + "y": "fq0DnZ_duPWyeFK0k93bAzjNJVVHEjHFRlGOJXKDS18" +}); + +} diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index 8698961..b8ac08b 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -1,6 +1,6 @@ use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, DIDCacheClient}; use affinidi_messaging_didcomm::{ - envelope::MetaEnvelope, secrets::SecretsResolver, Attachment, AttachmentData, Message, + envelope::MetaEnvelope, secrets::SecretsResolver, AttachmentData, Message, PackEncryptedOptions, UnpackMetadata, UnpackOptions, }; use affinidi_messaging_mediator::{resolvers::affinidi_secrets::AffinidiSecrets, server::start}; @@ -14,24 +14,24 @@ use affinidi_messaging_sdk::{ GenericDataStruct, GetMessagesRequest, GetMessagesResponse, MessageList, MessageListElement, SuccessResponse, }, - protocols::{ - message_pickup::{MessagePickupDeliveryRequest, MessagePickupStatusReply}, - trust_ping::TrustPingSent, - }, + protocols::message_pickup::MessagePickupStatusReply, transports::SendMessageResponse, }; use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine}; +use common::{BOB_DID, BOB_E1, BOB_V1, MEDIATOR_API, MY_DID, MY_E1, MY_V1}; use core::panic; +use message_builders::{ + build_delivery_request_message, build_forward_request_message, build_message_received_message, + build_ping_message, build_status_request_message, create_auth_challenge_response, +}; use reqwest::{Certificate, Client, ClientBuilder}; -use serde_json::{json, Value}; +use serde_json::json; use sha256::digest; -use std::time::{Duration, SystemTime}; +use std::time::Duration; use tokio::time::sleep; -use uuid::Uuid; -const MY_DID: &str = "did:peer:2.Vz6MkgWJfVmPELozq6aCycK3CpxHN8Upphn3WSuQkWY6iqsjF.EzQ3shfb7vwQaTJqFkt8nRfo7Nu98tmeYpdDfWgrqQitDaqXRz"; -const MEDIATOR_API: &str = "https://localhost:7037/mediator/v1"; -const BOB_DID: &str = "did:peer:2.Vz6Mkihn2R3M8nY62EFJ7MAVXu7YxsTnuS5iAhmn3qKJbkdFf.EzQ3shpZRBUtewwzYiueXgDqs1bvGNkSyGoRgsbZJXt3TTb9jD.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzcvIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXSwicm91dGluZ19rZXlzIjpbXX0sImlkIjpudWxsfQ"; +mod common; +mod message_builders; #[tokio::test] async fn test_mediator_server() { @@ -47,47 +47,16 @@ async fn test_mediator_server() { .build() .unwrap(); - // Signing and verification key - let my_v1 = json!({ - "crv": "Ed25519", - "d": "LLWCf83n8VsUYq31zlZRe0NNMCcn1N4Dh85dGpIqSFw", - "kty": "OKP", - "x": "Hn8T4ZjjT0oJ6rjhqox8AykwC3GDFsJF6KkaYZExwQo" - }); - - // Encryption key - let my_e1 = json!({ - "crv": "secp256k1", - "d": "oi-dXG4EqfNODFPjv2vkieoLdbQZH9k6dwPDV8HDoms", - "kty": "EC", - "x": "DhfaXbhwo0KkOiyA5V1K1RZx6Ikr86h_lX5GOwxjmjE", - "y": "PpYqybOwMsm64vftt-7gBCQPIUbglMmyy_6rloSSAPk" - }); - - let bob_v1 = json!({ - "crv": "Ed25519", - "d": "FZMJijqdcp7PCQShgtFj6Ud3vjZY7jFZBVvahziaMMM", - "kty": "OKP", - "x": "PybG95kyeSfGRebp4T7hzA7JQuysc6mZ97nM2ety6Vo" - }); - let bob_e1 = json!({ - "crv": "secp256k1", - "d": "ai7B5fgT3pCBHec0I4Y1xXpSyrEHlTy0hivSlddWHZE", - "kty": "EC", - "x": "k2FhEi8WMxr4Ztr4u2xjKzDESqVnGg_WKrN1820wPeA", - "y": "fq0DnZ_duPWyeFK0k93bAzjNJVVHEjHFRlGOJXKDS18" - }); - let did_resolver = DIDCacheClient::new(ClientConfigBuilder::default().build()) .await .unwrap(); let my_secrets_resolver = AffinidiSecrets::new(vec![ - secret_from_str(&format!("{}#key-1", MY_DID), &my_v1), - secret_from_str(&format!("{}#key-2", MY_DID), &my_e1), + secret_from_str(&format!("{}#key-1", MY_DID), &MY_V1), + secret_from_str(&format!("{}#key-2", MY_DID), &MY_E1), ]); let bob_secrets_resolver = AffinidiSecrets::new(vec![ - secret_from_str(&format!("{}#key-1", BOB_DID), &bob_v1), - secret_from_str(&format!("{}#key-2", BOB_DID), &bob_e1), + secret_from_str(&format!("{}#key-1", BOB_DID), &BOB_V1), + secret_from_str(&format!("{}#key-2", BOB_DID), &BOB_E1), ]); let client = init_client(config.clone()); @@ -101,10 +70,10 @@ async fn test_mediator_server() { println!("Auth ch: {:#?}", bob_authentication_challenge); // /authenticate/challenge let my_auth_response_msg = - _create_auth_challenge_response(&my_authentication_challenge, MY_DID, &mediator_did); + create_auth_challenge_response(&my_authentication_challenge, MY_DID, &mediator_did); let bob_auth_response_msg = - _create_auth_challenge_response(&bob_authentication_challenge, BOB_DID, &mediator_did); + create_auth_challenge_response(&bob_authentication_challenge, BOB_DID, &mediator_did); // /authenticate let my_authentication_response = _authenticate( @@ -134,7 +103,7 @@ async fn test_mediator_server() { // POST /inbound // MessageType=TrustPing // Send signed ping and expecting response - let (signed_ping_msg, mut signed_ping_msg_info) = _build_ping_message( + let (signed_ping_msg, mut signed_ping_msg_info) = build_ping_message( &mediator_did, MY_DID.into(), true, @@ -170,7 +139,7 @@ async fn test_mediator_server() { assert!(!pong_msg_id.is_empty()); // Send anonymous ping - let (anon_ping_msg, _) = _build_ping_message( + let (anon_ping_msg, _) = build_ping_message( &mediator_did, MY_DID.into(), false, @@ -189,7 +158,7 @@ async fn test_mediator_server() { .await; // MessageType=MessagePickupStatusRequest - let status_request_msg = _build_status_request_message( + let status_request_msg = build_status_request_message( &mediator_did, MY_DID.into(), &did_resolver, @@ -207,7 +176,7 @@ async fn test_mediator_server() { _validate_status_reply(status_reply, &did_resolver, &my_secrets_resolver).await; // MessageType=MessagePickupDeliveryRequest - let delivery_request_msg = _build_delivery_request_message( + let delivery_request_msg = build_delivery_request_message( &mediator_did, MY_DID.into(), &did_resolver, @@ -231,8 +200,9 @@ async fn test_mediator_server() { .await; // MessageType=MessagePickupMessagesReceived - let message_received_msg = _build_message_received_message( + let message_received_msg = build_message_received_message( &mediator_did, + MY_DID.into(), &did_resolver, &my_secrets_resolver, message_received_ids.clone(), @@ -255,7 +225,7 @@ async fn test_mediator_server() { .await; // MessageType=ForwardRequest - let forward_request_msg = _build_forward_request_message( + let forward_request_msg = build_forward_request_message( &mediator_did, MY_DID.into(), BOB_DID.into(), @@ -275,6 +245,7 @@ async fn test_mediator_server() { .await; let forwarded_msg_id = _validate_forward_request_response(forward_request_response).await; + // /outbound // delete messages: FALSE let get_message_no_delete_request = GetMessagesRequest { @@ -320,7 +291,7 @@ async fn test_mediator_server() { // Sending messages to list/fetch for _ in 0..3 { - let (signed_ping_msg, _) = _build_ping_message( + let (signed_ping_msg, _) = build_ping_message( &mediator_did, MY_DID.into(), true, @@ -544,110 +515,6 @@ async fn _authenticate<'sr>( } } -fn _create_auth_challenge_response( - body: &AuthenticationChallenge, - my_did: &str, - atm_did: &str, -) -> Message { - let now = _get_time_now(); - - Message::build( - Uuid::new_v4().into(), - "https://affinidi.com/atm/1.0/authenticate".to_owned(), - json!(body), - ) - .to(atm_did.to_owned()) - .from(my_did.to_owned()) - .created_time(now) - .expires_time(now + 60) - .finalize() -} - -async fn _build_ping_message<'sr>( - to_did: &str, - my_did: String, - signed: bool, - expect_response: bool, - did_resolver: &DIDCacheClient, - secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), -) -> (String, TrustPingSent) { - let now = _get_time_now(); - - let mut msg = Message::build( - Uuid::new_v4().into(), - "https://didcomm.org/trust-ping/2.0/ping".to_owned(), - json!({"response_requested": expect_response}), - ) - .to(to_did.to_owned()); - let from_did = if !signed { - // Can support anonymous pings - None - } else { - msg = msg.from(my_did.clone()); - Some(my_did.clone()) - }; - let msg = msg.created_time(now).expires_time(now + 300).finalize(); - let mut msg_info = TrustPingSent { - message_id: msg.id.clone(), - message_hash: "".to_string(), - bytes: 0, - response: None, - }; - let (msg, _) = msg - .pack_encrypted( - to_did, - from_did.as_deref(), - from_did.as_deref(), - did_resolver, - secrets_resolver, - &PackEncryptedOptions::default(), - ) - .await - .unwrap(); - - msg_info.message_hash = digest(&msg).to_string(); - msg_info.bytes = msg.len() as u32; - - (msg, msg_info) -} - -async fn _build_status_request_message<'sr>( - mediator_did: &str, - recipient_did: String, - did_resolver: &DIDCacheClient, - secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), -) -> String { - let mut msg = Message::build( - Uuid::new_v4().into(), - "https://didcomm.org/messagepickup/3.0/status-request".to_owned(), - json!({}), - ) - .header("return_route".into(), Value::String("all".into())); - - msg = msg.body(json!({"recipient_did": recipient_did })); - - let to_did = mediator_did; - - msg = msg.to(to_did.to_owned()); - - msg = msg.from(MY_DID.into()); - let now = _get_time_now(); - let msg = msg.created_time(now).expires_time(now + 300).finalize(); - let (msg, _) = msg - .pack_encrypted( - &to_did, - Some(MY_DID), - Some(MY_DID), - &did_resolver, - secrets_resolver, - &PackEncryptedOptions::default(), - ) - .await - .unwrap(); - - msg -} - async fn _validate_status_reply( status_reply: SendMessageResponse, did_resolver: &DIDCacheClient, @@ -676,47 +543,6 @@ async fn _validate_status_reply( } } -async fn _build_delivery_request_message<'sr>( - mediator_did: &str, - recipient_did: String, - did_resolver: &DIDCacheClient, - secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), -) -> String { - let body = MessagePickupDeliveryRequest { - recipient_did: recipient_did, - limit: 10, - }; - - let mut msg = Message::build( - Uuid::new_v4().into(), - "https://didcomm.org/messagepickup/3.0/delivery-request".to_owned(), - serde_json::to_value(body).unwrap(), - ) - .header("return_route".into(), Value::String("all".into())); - - let to_did = mediator_did; - msg = msg.to(to_did.to_owned()); - - msg = msg.from(MY_DID.to_owned()); - let now = _get_time_now(); - let msg = msg.created_time(now).expires_time(now + 300).finalize(); - - // Pack the message - let (msg, _) = msg - .pack_encrypted( - &to_did, - Some(MY_DID), - Some(MY_DID), - &did_resolver, - secrets_resolver, - &PackEncryptedOptions::default(), - ) - .await - .unwrap(); - - msg -} - async fn _handle_delivery( message: &Message, did_resolver: &DIDCacheClient, @@ -819,45 +645,6 @@ where } } -async fn _build_message_received_message<'sr>( - mediator_did: &str, - did_resolver: &DIDCacheClient, - secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), - to_delete_list: Vec, -) -> String { - let mut msg = Message::build( - Uuid::new_v4().into(), - "https://didcomm.org/messagepickup/3.0/messages-received".to_owned(), - json!({"message_id_list": to_delete_list}), - ) - .header("return_route".into(), Value::String("all".into())); - - let to_did = mediator_did; - msg = msg.to(to_did.to_owned()); - - msg = msg.from(MY_DID.into()); - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); - let msg = msg.created_time(now).expires_time(now + 300).finalize(); - - // Pack the message - let (msg, _) = msg - .pack_encrypted( - &to_did, - Some(MY_DID), - Some(MY_DID), - &did_resolver, - secrets_resolver, - &PackEncryptedOptions::default(), - ) - .await - .unwrap(); - - msg -} - async fn _validate_message_received_status_reply( status_reply: SendMessageResponse, did_resolver: &DIDCacheClient, @@ -887,52 +674,6 @@ async fn _validate_message_received_status_reply( } } -async fn _build_forward_request_message<'sr>( - mediator_did: &str, - recipient_did: String, - actor_did: String, - did_resolver: &DIDCacheClient, - secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), -) -> String { - let now = _get_time_now(); - - let recipient_did = recipient_did; - - let msg = Message::build( - Uuid::new_v4().into(), - "https://didcomm.org/routing/2.0/forward".to_owned(), - json!({ "next": recipient_did }), - ) - .to(mediator_did.to_owned()) - .from(actor_did.clone()) - .attachment( - Attachment::json(json!({ "message": "plaintext attachment, mediator can read this" })) - .finalize(), - ) - .attachment( - Attachment::base64(String::from( - "ciphertext and iv which is encrypted by the recipient public key", - )) - .finalize(), - ); - - let msg = msg.created_time(now).expires_time(now + 300).finalize(); - - // Pack the message - let (msg, _) = msg - .pack_encrypted( - &mediator_did.to_owned(), - Some(&actor_did.clone()), - Some(&actor_did.clone()), - &did_resolver, - secrets_resolver, - &PackEncryptedOptions::default(), - ) - .await - .unwrap(); - msg -} - async fn _validate_forward_request_response( forward_request_response: SendMessageResponse, ) -> String { @@ -1234,9 +975,3 @@ async fn _delete_messages( list } -fn _get_time_now() -> u64 { - SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs() -} diff --git a/affinidi-messaging-mediator/tests/message_builders.rs b/affinidi-messaging-mediator/tests/message_builders.rs new file mode 100644 index 0000000..36e9cf0 --- /dev/null +++ b/affinidi-messaging-mediator/tests/message_builders.rs @@ -0,0 +1,252 @@ +use std::time::SystemTime; + +use affinidi_did_resolver_cache_sdk::DIDCacheClient; +use affinidi_messaging_didcomm::{ + secrets::SecretsResolver, Attachment, Message, PackEncryptedOptions, +}; +use affinidi_messaging_sdk::{ + messages::AuthenticationChallenge, + protocols::{message_pickup::MessagePickupDeliveryRequest, trust_ping::TrustPingSent}, +}; +use serde_json::{json, Value}; +use sha256::digest; +use uuid::Uuid; + +pub fn create_auth_challenge_response( + body: &AuthenticationChallenge, + my_did: &str, + atm_did: &str, +) -> Message { + let now = _get_time_now(); + + Message::build( + Uuid::new_v4().into(), + "https://affinidi.com/atm/1.0/authenticate".to_owned(), + json!(body), + ) + .to(atm_did.to_owned()) + .from(my_did.to_owned()) + .created_time(now) + .expires_time(now + 60) + .finalize() +} + +pub async fn build_ping_message<'sr>( + to_did: &str, + my_did: String, + signed: bool, + expect_response: bool, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> (String, TrustPingSent) { + let now = _get_time_now(); + + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/trust-ping/2.0/ping".to_owned(), + json!({"response_requested": expect_response}), + ) + .to(to_did.to_owned()); + + let from_did = if !signed { + // Can support anonymous pings + None + } else { + msg = msg.from(my_did.clone()); + Some(my_did.clone()) + }; + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + let mut msg_info = TrustPingSent { + message_id: msg.id.clone(), + message_hash: "".to_string(), + bytes: 0, + response: None, + }; + let (msg, _) = msg + .pack_encrypted( + to_did, + from_did.as_deref(), + from_did.as_deref(), + did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg_info.message_hash = digest(&msg).to_string(); + msg_info.bytes = msg.len() as u32; + + (msg, msg_info) +} + +pub async fn build_status_request_message<'sr>( + mediator_did: &str, + recipient_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> String { + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/messagepickup/3.0/status-request".to_owned(), + json!({}), + ) + .header("return_route".into(), Value::String("all".into())); + + msg = msg.body(json!({"recipient_did": recipient_did })); + + let to_did = mediator_did; + + msg = msg.to(to_did.to_owned()); + + msg = msg.from(recipient_did.clone().into()); + let now = _get_time_now(); + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + let (msg, _) = msg + .pack_encrypted( + &to_did, + Some(&recipient_did), + Some(&recipient_did), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg +} + +pub async fn build_delivery_request_message<'sr>( + mediator_did: &str, + recipient_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> String { + let body = MessagePickupDeliveryRequest { + recipient_did: recipient_did.clone(), + limit: 10, + }; + + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/messagepickup/3.0/delivery-request".to_owned(), + serde_json::to_value(body).unwrap(), + ) + .header("return_route".into(), Value::String("all".into())); + + let to_did = mediator_did; + msg = msg.to(to_did.to_owned()); + + msg = msg.from(recipient_did.clone().to_owned()); + let now = _get_time_now(); + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + + // Pack the message + let (msg, _) = msg + .pack_encrypted( + &to_did, + Some(&recipient_did), + Some(&recipient_did), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg +} + +pub async fn build_message_received_message<'sr>( + mediator_did: &str, + recipient_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), + to_delete_list: Vec, +) -> String { + let mut msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/messagepickup/3.0/messages-received".to_owned(), + json!({"message_id_list": to_delete_list}), + ) + .header("return_route".into(), Value::String("all".into())); + + let to_did = mediator_did; + msg = msg.to(to_did.to_owned()); + + msg = msg.from(recipient_did.clone().into()); + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + + // Pack the message + let (msg, _) = msg + .pack_encrypted( + &to_did, + Some(&recipient_did), + Some(&recipient_did), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + + msg +} + +pub async fn build_forward_request_message<'sr>( + mediator_did: &str, + recipient_did: String, + actor_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &'sr (dyn SecretsResolver + 'sr + Sync), +) -> String { + let now = _get_time_now(); + + let recipient_did = recipient_did; + + let msg = Message::build( + Uuid::new_v4().into(), + "https://didcomm.org/routing/2.0/forward".to_owned(), + json!({ "next": recipient_did }), + ) + .to(mediator_did.to_owned()) + .from(actor_did.clone()) + .attachment( + Attachment::json(json!({ "message": "plaintext attachment, mediator can read this" })) + .finalize(), + ) + .attachment( + Attachment::base64(String::from( + "ciphertext and iv which is encrypted by the recipient public key", + )) + .finalize(), + ); + + let msg = msg.created_time(now).expires_time(now + 300).finalize(); + + // Pack the message + let (msg, _) = msg + .pack_encrypted( + &mediator_did.to_owned(), + Some(&actor_did.clone()), + Some(&actor_did.clone()), + &did_resolver, + secrets_resolver, + &PackEncryptedOptions::default(), + ) + .await + .unwrap(); + msg +} + +fn _get_time_now() -> u64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() +} From 9dc3640504e1be72716420b331d280c37b73ebca Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Mon, 7 Oct 2024 18:19:11 +0200 Subject: [PATCH 06/18] chore: refactor validations --- .../tests/integration_test.rs | 248 ++---------------- .../tests/response_validations.rs | 223 ++++++++++++++++ 2 files changed, 246 insertions(+), 225 deletions(-) create mode 100644 affinidi-messaging-mediator/tests/response_validations.rs diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index b8ac08b..4078f45 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -1,8 +1,5 @@ use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, DIDCacheClient}; -use affinidi_messaging_didcomm::{ - envelope::MetaEnvelope, secrets::SecretsResolver, AttachmentData, Message, - PackEncryptedOptions, UnpackMetadata, UnpackOptions, -}; +use affinidi_messaging_didcomm::{secrets::SecretsResolver, Message, PackEncryptedOptions}; use affinidi_messaging_mediator::{resolvers::affinidi_secrets::AffinidiSecrets, server::start}; use affinidi_messaging_sdk::{ config::Config, @@ -14,10 +11,8 @@ use affinidi_messaging_sdk::{ GenericDataStruct, GetMessagesRequest, GetMessagesResponse, MessageList, MessageListElement, SuccessResponse, }, - protocols::message_pickup::MessagePickupStatusReply, transports::SendMessageResponse, }; -use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine}; use common::{BOB_DID, BOB_E1, BOB_V1, MEDIATOR_API, MY_DID, MY_E1, MY_V1}; use core::panic; use message_builders::{ @@ -25,13 +20,17 @@ use message_builders::{ build_ping_message, build_status_request_message, create_auth_challenge_response, }; use reqwest::{Certificate, Client, ClientBuilder}; -use serde_json::json; +use response_validations::{ + validate_forward_request_response, validate_get_message_response, validate_list_messages, + validate_message_delivery, validate_message_received_status_reply, validate_status_reply, +}; use sha256::digest; use std::time::Duration; use tokio::time::sleep; mod common; mod message_builders; +mod response_validations; #[tokio::test] async fn test_mediator_server() { @@ -173,7 +172,13 @@ async fn test_mediator_server() { 200, ) .await; - _validate_status_reply(status_reply, &did_resolver, &my_secrets_resolver).await; + validate_status_reply( + status_reply, + MY_DID.into(), + &did_resolver, + &my_secrets_resolver, + ) + .await; // MessageType=MessagePickupDeliveryRequest let delivery_request_msg = build_delivery_request_message( @@ -191,7 +196,7 @@ async fn test_mediator_server() { 200, ) .await; - let message_received_ids = _validate_message_delivery( + let message_received_ids = validate_message_delivery( message_delivery, &did_resolver, &my_secrets_resolver, @@ -217,8 +222,10 @@ async fn test_mediator_server() { 200, ) .await; - _validate_message_received_status_reply( + + validate_message_received_status_reply( message_received_status_reply, + MY_DID.into(), &did_resolver, &my_secrets_resolver, ) @@ -244,7 +251,7 @@ async fn test_mediator_server() { ) .await; - let forwarded_msg_id = _validate_forward_request_response(forward_request_response).await; + let forwarded_msg_id = validate_forward_request_response(forward_request_response).await; // /outbound // delete messages: FALSE @@ -261,7 +268,7 @@ async fn test_mediator_server() { ) .await; - _validate_get_message_response(msg_list, MY_DID, &did_resolver, &my_secrets_resolver).await; + validate_get_message_response(msg_list, MY_DID, &did_resolver, &my_secrets_resolver).await; // delete messages: TRUE let get_message_delete_request = GetMessagesRequest { @@ -277,7 +284,7 @@ async fn test_mediator_server() { ) .await; - _validate_get_message_response(msg_list, MY_DID, &did_resolver, &my_secrets_resolver).await; + validate_get_message_response(msg_list, MY_DID, &did_resolver, &my_secrets_resolver).await; // get message should return not found let _msg_list = _outbound_message( @@ -321,7 +328,7 @@ async fn test_mediator_server() { Folder::Inbox, ) .await; - _validate_list_messages(msgs_list, &mediator_did); + validate_list_messages(msgs_list, &mediator_did); // /list/:did_hash/Outbox let msgs_list = list_messages( @@ -335,7 +342,7 @@ async fn test_mediator_server() { assert_eq!(msgs_list.len(), 0); // /fetch - let messages = fetch_messages( + let messages = _fetch_messages( client.clone(), my_authentication_response.clone(), 200, @@ -515,207 +522,6 @@ async fn _authenticate<'sr>( } } -async fn _validate_status_reply( - status_reply: SendMessageResponse, - did_resolver: &DIDCacheClient, - secrets_resolver: &S, -) where - S: SecretsResolver + Send, -{ - if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = - status_reply - { - let (message, _) = Message::unpack_string( - &message, - &did_resolver, - secrets_resolver, - &UnpackOptions::default(), - ) - .await - .unwrap(); - let status: MessagePickupStatusReply = - serde_json::from_value(message.body.clone()).unwrap(); - assert!(!status.live_delivery); - assert!(status.longest_waited_seconds.unwrap() > 0); - assert!(status.message_count == 1); - assert!(status.recipient_did == MY_DID); - assert!(status.total_bytes > 0); - } -} - -async fn _handle_delivery( - message: &Message, - did_resolver: &DIDCacheClient, - secrets_resolver: &S, -) -> Vec<(Message, UnpackMetadata)> -where - S: SecretsResolver + Send, -{ - let mut response: Vec<(Message, UnpackMetadata)> = Vec::new(); - - if let Some(attachments) = &message.attachments { - for attachment in attachments { - match &attachment.data { - AttachmentData::Base64 { value } => { - let decoded = match BASE64_URL_SAFE_NO_PAD.decode(value.base64.clone()) { - Ok(decoded) => match String::from_utf8(decoded) { - Ok(decoded) => decoded, - Err(e) => { - assert!(false, "{:?}", e); - "".into() - } - }, - Err(e) => { - assert!(false, "{:?}", e); - continue; - } - }; - let mut envelope = - match MetaEnvelope::new(&decoded, &did_resolver, secrets_resolver).await { - Ok(envelope) => envelope, - Err(e) => { - assert!(false, "{:?}", e); - continue; - } - }; - - match Message::unpack( - &mut envelope, - did_resolver, - secrets_resolver, - &UnpackOptions::default(), - ) - .await - { - Ok((mut m, u)) => { - if let Some(attachment_id) = &attachment.id { - m.id = attachment_id.to_string(); - } - response.push((m, u)) - } - Err(e) => { - assert!(false, "{:?}", e); - continue; - } - }; - } - _ => { - assert!(false); - continue; - } - }; - } - } - - response -} - -async fn _validate_message_delivery( - message_delivery: SendMessageResponse, - did_resolver: &DIDCacheClient, - secrets_resolver: &S, - pong_msg_id: &str, -) -> Vec -where - S: SecretsResolver + Send, -{ - if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = - message_delivery - { - let (message, _) = Message::unpack_string( - &message, - &did_resolver, - secrets_resolver, - &UnpackOptions::default(), - ) - .await - .unwrap(); - - let messages = _handle_delivery(&message, did_resolver, secrets_resolver).await; - let mut to_delete_ids: Vec = Vec::new(); - - assert_eq!(messages.first().unwrap().0.id, pong_msg_id); - - for (message, _) in messages { - to_delete_ids.push(message.id.clone()); - } - to_delete_ids - } else { - vec![] - } -} - -async fn _validate_message_received_status_reply( - status_reply: SendMessageResponse, - did_resolver: &DIDCacheClient, - secrets_resolver: &S, -) where - S: SecretsResolver + Send, -{ - if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = - status_reply - { - let (message, _) = Message::unpack_string( - &message, - &did_resolver, - secrets_resolver, - &UnpackOptions::default(), - ) - .await - .unwrap(); - let status: MessagePickupStatusReply = - serde_json::from_value(message.body.clone()).unwrap(); - - assert!(!status.live_delivery); - assert!(status.longest_waited_seconds.is_none()); - assert!(status.message_count == 0); - assert!(status.recipient_did == MY_DID); - assert!(status.total_bytes == 0); - } -} - -async fn _validate_forward_request_response( - forward_request_response: SendMessageResponse, -) -> String { - let msg_id = if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Stored(m))) = - forward_request_response - { - if let Some((_, msg_id)) = m.messages.first() { - Some(msg_id.to_owned()) - } else { - None - } - } else { - None - }; - - assert!(!msg_id.is_none()); - - msg_id.unwrap() -} - -async fn _validate_get_message_response( - list: GetMessagesResponse, - my_did: &str, - did_resolver: &DIDCacheClient, - secrets_resolver: &S, -) where - S: SecretsResolver + Send, -{ - for msg in list.success { - assert_eq!(msg.to_address.unwrap(), digest(my_did)); - let _ = Message::unpack_string( - &msg.msg.unwrap(), - did_resolver, - secrets_resolver, - &UnpackOptions::default(), - ) - .await - .unwrap(); - println!("Msg id: {}", msg.msg_id); - } -} - async fn _send_inbound_message( client: Client, tokens: AuthorizationResponse, @@ -816,14 +622,6 @@ async fn _outbound_message( list } -fn _validate_list_messages(list: Vec, mediator_did: &str) { - assert_eq!(list.len(), 3); - - for msg in list { - assert_eq!(msg.from_address.unwrap(), mediator_did); - } -} - async fn list_messages( client: Client, tokens: AuthorizationResponse, @@ -870,7 +668,7 @@ async fn list_messages( list } -async fn fetch_messages( +async fn _fetch_messages( client: Client, tokens: AuthorizationResponse, expected_status_code: u16, diff --git a/affinidi-messaging-mediator/tests/response_validations.rs b/affinidi-messaging-mediator/tests/response_validations.rs new file mode 100644 index 0000000..0cb7a5c --- /dev/null +++ b/affinidi-messaging-mediator/tests/response_validations.rs @@ -0,0 +1,223 @@ +use affinidi_did_resolver_cache_sdk::DIDCacheClient; +use affinidi_messaging_didcomm::{ + envelope::MetaEnvelope, secrets::SecretsResolver, AttachmentData, Message, UnpackMetadata, + UnpackOptions, +}; +use affinidi_messaging_sdk::{ + messages::{sending::InboundMessageResponse, GetMessagesResponse, MessageListElement}, + protocols::message_pickup::MessagePickupStatusReply, + transports::SendMessageResponse, +}; +use base64::{prelude::BASE64_URL_SAFE_NO_PAD, Engine}; +use sha256::digest; + +pub async fn validate_status_reply( + status_reply: SendMessageResponse, + recipient_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) where + S: SecretsResolver + Send, +{ + if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = + status_reply + { + let (message, _) = Message::unpack_string( + &message, + &did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + let status: MessagePickupStatusReply = + serde_json::from_value(message.body.clone()).unwrap(); + assert!(!status.live_delivery); + assert!(status.longest_waited_seconds.unwrap() > 0); + assert!(status.message_count == 1); + assert!(status.recipient_did == recipient_did); + assert!(status.total_bytes > 0); + } +} + +pub async fn validate_message_delivery( + message_delivery: SendMessageResponse, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, + pong_msg_id: &str, +) -> Vec +where + S: SecretsResolver + Send, +{ + if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = + message_delivery + { + let (message, _) = Message::unpack_string( + &message, + &did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + + let messages = _handle_delivery(&message, did_resolver, secrets_resolver).await; + let mut to_delete_ids: Vec = Vec::new(); + + assert_eq!(messages.first().unwrap().0.id, pong_msg_id); + + for (message, _) in messages { + to_delete_ids.push(message.id.clone()); + } + to_delete_ids + } else { + vec![] + } +} + +async fn _handle_delivery( + message: &Message, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) -> Vec<(Message, UnpackMetadata)> +where + S: SecretsResolver + Send, +{ + let mut response: Vec<(Message, UnpackMetadata)> = Vec::new(); + + if let Some(attachments) = &message.attachments { + for attachment in attachments { + match &attachment.data { + AttachmentData::Base64 { value } => { + let decoded = match BASE64_URL_SAFE_NO_PAD.decode(value.base64.clone()) { + Ok(decoded) => match String::from_utf8(decoded) { + Ok(decoded) => decoded, + Err(e) => { + assert!(false, "{:?}", e); + "".into() + } + }, + Err(e) => { + assert!(false, "{:?}", e); + continue; + } + }; + let mut envelope = + match MetaEnvelope::new(&decoded, &did_resolver, secrets_resolver).await { + Ok(envelope) => envelope, + Err(e) => { + assert!(false, "{:?}", e); + continue; + } + }; + + match Message::unpack( + &mut envelope, + did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + { + Ok((mut m, u)) => { + if let Some(attachment_id) = &attachment.id { + m.id = attachment_id.to_string(); + } + response.push((m, u)) + } + Err(e) => { + assert!(false, "{:?}", e); + continue; + } + }; + } + _ => { + assert!(false); + continue; + } + }; + } + } + + response +} + +pub async fn validate_message_received_status_reply( + status_reply: SendMessageResponse, + recipient_did: String, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) where + S: SecretsResolver + Send, +{ + if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Ephemeral(message))) = + status_reply + { + let (message, _) = Message::unpack_string( + &message, + &did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + let status: MessagePickupStatusReply = + serde_json::from_value(message.body.clone()).unwrap(); + + assert!(!status.live_delivery); + assert!(status.longest_waited_seconds.is_none()); + assert!(status.message_count == 0); + assert!(status.recipient_did == recipient_did); + assert!(status.total_bytes == 0); + } +} + +pub async fn validate_forward_request_response( + forward_request_response: SendMessageResponse, +) -> String { + let msg_id = if let SendMessageResponse::RestAPI(Some(InboundMessageResponse::Stored(m))) = + forward_request_response + { + if let Some((_, msg_id)) = m.messages.first() { + Some(msg_id.to_owned()) + } else { + None + } + } else { + None + }; + + assert!(!msg_id.is_none()); + + msg_id.unwrap() +} + +pub async fn validate_get_message_response( + list: GetMessagesResponse, + my_did: &str, + did_resolver: &DIDCacheClient, + secrets_resolver: &S, +) where + S: SecretsResolver + Send, +{ + for msg in list.success { + assert_eq!(msg.to_address.unwrap(), digest(my_did)); + let _ = Message::unpack_string( + &msg.msg.unwrap(), + did_resolver, + secrets_resolver, + &UnpackOptions::default(), + ) + .await + .unwrap(); + println!("Msg id: {}", msg.msg_id); + } +} + +pub fn validate_list_messages(list: Vec, mediator_did: &str) { + assert_eq!(list.len(), 3); + + for msg in list { + assert_eq!(msg.from_address.unwrap(), mediator_did); + } +} From 0026bc6f81de9f0c0dd64ce7b65c4bb63e053835 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 8 Oct 2024 14:14:25 +0200 Subject: [PATCH 07/18] test: generate secrets on fly for integration tests and inject did to config --- affinidi-messaging-mediator/tests/common.rs | 3 + .../tests/integration_test.rs | 90 +++++++++++++++++-- .../tests/keys/client.chain | 21 +++++ 3 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 affinidi-messaging-mediator/tests/keys/client.chain diff --git a/affinidi-messaging-mediator/tests/common.rs b/affinidi-messaging-mediator/tests/common.rs index 6dfe19e..5681056 100644 --- a/affinidi-messaging-mediator/tests/common.rs +++ b/affinidi-messaging-mediator/tests/common.rs @@ -4,6 +4,9 @@ use serde_json::{json, Value}; pub const MY_DID: &str = "did:peer:2.Vz6MkgWJfVmPELozq6aCycK3CpxHN8Upphn3WSuQkWY6iqsjF.EzQ3shfb7vwQaTJqFkt8nRfo7Nu98tmeYpdDfWgrqQitDaqXRz"; pub const MEDIATOR_API: &str = "https://localhost:7037/mediator/v1"; pub const BOB_DID: &str = "did:peer:2.Vz6Mkihn2R3M8nY62EFJ7MAVXu7YxsTnuS5iAhmn3qKJbkdFf.EzQ3shpZRBUtewwzYiueXgDqs1bvGNkSyGoRgsbZJXt3TTb9jD.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vbG9jYWxob3N0OjcwMzcvIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXSwicm91dGluZ19rZXlzIjpbXX0sImlkIjpudWxsfQ"; +pub const SECRETS_PATH: &str = "../affinidi-messaging-mediator/conf/secrets.json"; +pub const CONFIG_PATH: &str = "../affinidi-messaging-mediator/conf/mediator.toml"; + lazy_static! { // Signing and verification key pub static ref MY_V1: Value = json!({ diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index 4078f45..cd3cada 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -1,5 +1,8 @@ use affinidi_did_resolver_cache_sdk::{config::ClientConfigBuilder, DIDCacheClient}; -use affinidi_messaging_didcomm::{secrets::SecretsResolver, Message, PackEncryptedOptions}; +use affinidi_messaging_didcomm::{ + secrets::{Secret, SecretsResolver}, + Message, PackEncryptedOptions, +}; use affinidi_messaging_mediator::{resolvers::affinidi_secrets::AffinidiSecrets, server::start}; use affinidi_messaging_sdk::{ config::Config, @@ -13,7 +16,9 @@ use affinidi_messaging_sdk::{ }, transports::SendMessageResponse, }; -use common::{BOB_DID, BOB_E1, BOB_V1, MEDIATOR_API, MY_DID, MY_E1, MY_V1}; +use common::{ + BOB_DID, BOB_E1, BOB_V1, CONFIG_PATH, MEDIATOR_API, MY_DID, MY_E1, MY_V1, SECRETS_PATH, +}; use core::panic; use message_builders::{ build_delivery_request_message, build_forward_request_message, build_message_received_message, @@ -25,7 +30,14 @@ use response_validations::{ validate_message_delivery, validate_message_received_status_reply, validate_status_reply, }; use sha256::digest; -use std::time::Duration; +use std::{ + fs::{self, File}, + io::{self, BufRead, BufReader}, + path::Path, + process::Command, + str, + time::Duration, +}; use tokio::time::sleep; mod common; @@ -34,6 +46,15 @@ mod response_validations; #[tokio::test] async fn test_mediator_server() { + // Generate secrets and did for mediator if not existing + if !fs::metadata(SECRETS_PATH).is_ok() { + println!("Generating secrets"); + _generate_secrets(); + let mediator_did = _get_did_from_secrets(SECRETS_PATH.into()); + _inject_did_into_config(CONFIG_PATH, &mediator_did); + println!("Secrets generated and did injected to mediator.toml"); + } + _start_mediator_server().await; // Allow some time for the server to start @@ -41,7 +62,7 @@ async fn test_mediator_server() { let config = Config::builder() .with_ssl_certificates(&mut vec![ - "../affinidi-messaging-mediator/conf/keys/client.chain".into(), + "../affinidi-messaging-mediator/tests/keys/client.chain".into(), ]) .build() .unwrap(); @@ -65,8 +86,7 @@ async fn test_mediator_server() { // Start Authentication let my_authentication_challenge = _authenticate_challenge(client.clone(), MY_DID).await; let bob_authentication_challenge = _authenticate_challenge(client.clone(), BOB_DID).await; - println!("Auth ch: {:#?}", my_authentication_challenge); - println!("Auth ch: {:#?}", bob_authentication_challenge); + // /authenticate/challenge let my_auth_response_msg = create_auth_challenge_response(&my_authentication_challenge, MY_DID, &mediator_did); @@ -507,7 +527,7 @@ async fn _authenticate<'sr>( let body = res.text().await.unwrap(); - assert!(status.is_success()); + assert!(status.is_success(), "Received status code: {}", status); if !status.is_success() { println!("Failed to get authentication response. Body: {:?}", body); @@ -773,3 +793,59 @@ async fn _delete_messages( list } + +fn _generate_secrets() { + let output = Command::new("cargo") + .args(&["run", "--example", "generate_secrets"]) + .output() + .expect("Failed to run example"); + assert!(output.status.success()); + let source_path = "../affinidi-messaging-mediator/conf/secrets.json-generated"; + + let _ = match fs::copy(source_path, SECRETS_PATH) { + Ok(_) => println!("Copied {} to {}", source_path, SECRETS_PATH), + Err(e) => panic!("Failed with error: {e:?}"), + }; +} + +fn _get_did_from_secrets(path: String) -> String { + let file = File::open(path).unwrap(); + let reader = BufReader::new(file); + + // Parse the JSON file + let config: Vec = serde_json::from_reader(reader).unwrap(); + let id_split: Vec<&str> = config.first().unwrap().id.split("#").collect(); + let did = *id_split.first().unwrap(); + did.into() +} + +fn _inject_did_into_config

(file_name: P, did: &str) +where + P: AsRef, +{ + let file = File::open(file_name.as_ref()) + .map_err(|err| { + panic!( + "{}", + format!( + "Could not open file({}). {}", + file_name.as_ref().display(), + err + ) + ); + }) + .unwrap(); + + let mut lines: Vec = Vec::new(); + for mut line in io::BufReader::new(file).lines().map_while(Result::ok) { + // Strip comments out + if line.starts_with("mediator_did =") { + let line_split: Vec<&str> = line.split("//").collect(); + let line_beginning = *line_split.first().unwrap(); + line = format!("{}{}{}{}", line_beginning, "//", did, "}\""); + } + lines.push(line); + } + let config_file = lines.join("\n"); + fs::write(file_name, config_file).expect("Failed to write to file"); +} diff --git a/affinidi-messaging-mediator/tests/keys/client.chain b/affinidi-messaging-mediator/tests/keys/client.chain new file mode 100644 index 0000000..5df667a --- /dev/null +++ b/affinidi-messaging-mediator/tests/keys/client.chain @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIBfzCCATGgAwIBAgIBAjAFBgMrZXAwHDEaMBgGA1UEAwwRQWZmaW5pZGkgRWRE +U0EgQ0EwIBcNNzUwMTAxMDAwMDAwWhgPNDA5NjAxMDEwMDAwMDBaMC4xLDAqBgNV +BAMMI0FmZmluaWRpIEVkRFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMCowBQYDK2Vw +AyEAn3TTHaMRU5pl9RpgA8rBHfK18hZADvsHBwgCH1RnXoejgYMwgYAwHwYDVR0j +BBgwFoAURrpKnqj4wRJgoUAYY3O52iKxX8IwDgYDVR0PAQH/BAQDAgH+MB0GA1Ud +JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUBGkLbHVPJObk/Hbr +6efJBXGUDpQwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQAEbot3Ied5wpJzZs6s +ref9XSJN1B+l/ICMTz9H5EHwSQCrzPqAUgHHtKSEb9WlhSYPROCIZ9TevK/SqD9X +kFMM +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIBbTCCAR+gAwIBAgIBATAFBgMrZXAwHDEaMBgGA1UEAwwRQWZmaW5pZGkgRWRE +U0EgQ0EwIBcNNzUwMTAxMDAwMDAwWhgPNDA5NjAxMDEwMDAwMDBaMBwxGjAYBgNV +BAMMEUFmZmluaWRpIEVkRFNBIENBMCowBQYDK2VwAyEALl0LU3jV2cACW5i7ZR1g +2u4n0cJkrs1u3OYn5Ate3/yjgYMwgYAwHwYDVR0jBBgwFoAURrpKnqj4wRJgoUAY +Y3O52iKxX8IwDgYDVR0PAQH/BAQDAgH+MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr +BgEFBQcDAjAdBgNVHQ4EFgQURrpKnqj4wRJgoUAYY3O52iKxX8IwDwYDVR0TAQH/ +BAUwAwEB/zAFBgMrZXADQQAx8Z6RS+5sk3yW2ZUMzq3MxdTTqmXUxU/HNbbNE4nJ +SDMlCKHIBFq2RshGu23uPHOeS+i9sLfo1J4whRX9NssE +-----END CERTIFICATE----- From eb91d9039b0f321f809a98e74254a7ef633ac2dd Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 8 Oct 2024 14:23:55 +0200 Subject: [PATCH 08/18] test: increase waiting time for server to spin up --- affinidi-messaging-mediator/tests/integration_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index cd3cada..0170f93 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -58,7 +58,7 @@ async fn test_mediator_server() { _start_mediator_server().await; // Allow some time for the server to start - sleep(Duration::from_millis(1000)).await; + sleep(Duration::from_millis(2000)).await; let config = Config::builder() .with_ssl_certificates(&mut vec![ From f274fe1aa216e5bd7fc39fb0e9395fdd954f10f1 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 8 Oct 2024 14:56:10 +0200 Subject: [PATCH 09/18] feat: test pipeline with redis --- .github/workflows/checks.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 45928f1..7909215 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -5,11 +5,11 @@ on: types: - opened - synchronize - + jobs: rust-pipeline: - uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@main + uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@77f989d893406492d4f9996ca0e17f37282006e7 secrets: inherit with: - auditIgnore: "RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373" + auditIgnore: 'RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373' coverage: 10 From e17682414c5a3800b7b36ecb4174d28565d9290b Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 8 Oct 2024 15:07:47 +0200 Subject: [PATCH 10/18] feat: trigger workflow on branch --- .github/workflows/checks.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 7909215..da3b67b 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -5,7 +5,9 @@ on: types: - opened - synchronize - + push: + branches: + - FTL-17136_integration_test jobs: rust-pipeline: uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@77f989d893406492d4f9996ca0e17f37282006e7 From af1a62bd2c52937edd28cdfdcc62ac9e6b8f9e4e Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 8 Oct 2024 15:36:35 +0200 Subject: [PATCH 11/18] feat: generate local certs on the fly --- .../tests/integration_test.rs | 13 ++++++++++-- .../tests/keys/client.chain | 21 ------------------- 2 files changed, 11 insertions(+), 23 deletions(-) delete mode 100644 affinidi-messaging-mediator/tests/keys/client.chain diff --git a/affinidi-messaging-mediator/tests/integration_test.rs b/affinidi-messaging-mediator/tests/integration_test.rs index 0170f93..db5988a 100644 --- a/affinidi-messaging-mediator/tests/integration_test.rs +++ b/affinidi-messaging-mediator/tests/integration_test.rs @@ -49,6 +49,7 @@ async fn test_mediator_server() { // Generate secrets and did for mediator if not existing if !fs::metadata(SECRETS_PATH).is_ok() { println!("Generating secrets"); + _generate_keys(); _generate_secrets(); let mediator_did = _get_did_from_secrets(SECRETS_PATH.into()); _inject_did_into_config(CONFIG_PATH, &mediator_did); @@ -62,7 +63,7 @@ async fn test_mediator_server() { let config = Config::builder() .with_ssl_certificates(&mut vec![ - "../affinidi-messaging-mediator/tests/keys/client.chain".into(), + "../affinidi-messaging-mediator/conf/keys/client.chain".into(), ]) .build() .unwrap(); @@ -798,7 +799,7 @@ fn _generate_secrets() { let output = Command::new("cargo") .args(&["run", "--example", "generate_secrets"]) .output() - .expect("Failed to run example"); + .expect("Failed to generate secrets"); assert!(output.status.success()); let source_path = "../affinidi-messaging-mediator/conf/secrets.json-generated"; @@ -808,6 +809,14 @@ fn _generate_secrets() { }; } +fn _generate_keys() { + let output = Command::new("cargo") + .args(&["run", "--example", "create_local_certs"]) + .output() + .expect("Failed to create local certs"); + assert!(output.status.success()); +} + fn _get_did_from_secrets(path: String) -> String { let file = File::open(path).unwrap(); let reader = BufReader::new(file); diff --git a/affinidi-messaging-mediator/tests/keys/client.chain b/affinidi-messaging-mediator/tests/keys/client.chain deleted file mode 100644 index 5df667a..0000000 --- a/affinidi-messaging-mediator/tests/keys/client.chain +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBfzCCATGgAwIBAgIBAjAFBgMrZXAwHDEaMBgGA1UEAwwRQWZmaW5pZGkgRWRE -U0EgQ0EwIBcNNzUwMTAxMDAwMDAwWhgPNDA5NjAxMDEwMDAwMDBaMC4xLDAqBgNV -BAMMI0FmZmluaWRpIEVkRFNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMCowBQYDK2Vw -AyEAn3TTHaMRU5pl9RpgA8rBHfK18hZADvsHBwgCH1RnXoejgYMwgYAwHwYDVR0j -BBgwFoAURrpKnqj4wRJgoUAYY3O52iKxX8IwDgYDVR0PAQH/BAQDAgH+MB0GA1Ud -JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUBGkLbHVPJObk/Hbr -6efJBXGUDpQwDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQAEbot3Ied5wpJzZs6s -ref9XSJN1B+l/ICMTz9H5EHwSQCrzPqAUgHHtKSEb9WlhSYPROCIZ9TevK/SqD9X -kFMM ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIBbTCCAR+gAwIBAgIBATAFBgMrZXAwHDEaMBgGA1UEAwwRQWZmaW5pZGkgRWRE -U0EgQ0EwIBcNNzUwMTAxMDAwMDAwWhgPNDA5NjAxMDEwMDAwMDBaMBwxGjAYBgNV -BAMMEUFmZmluaWRpIEVkRFNBIENBMCowBQYDK2VwAyEALl0LU3jV2cACW5i7ZR1g -2u4n0cJkrs1u3OYn5Ate3/yjgYMwgYAwHwYDVR0jBBgwFoAURrpKnqj4wRJgoUAY -Y3O52iKxX8IwDgYDVR0PAQH/BAQDAgH+MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr -BgEFBQcDAjAdBgNVHQ4EFgQURrpKnqj4wRJgoUAYY3O52iKxX8IwDwYDVR0TAQH/ -BAUwAwEB/zAFBgMrZXADQQAx8Z6RS+5sk3yW2ZUMzq3MxdTTqmXUxU/HNbbNE4nJ -SDMlCKHIBFq2RshGu23uPHOeS+i9sLfo1J4whRX9NssE ------END CERTIFICATE----- From 645583005dc921378e20a39a8c5faa98499e124a Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 8 Oct 2024 15:46:52 +0200 Subject: [PATCH 12/18] feat: revert workflow changes --- .github/workflows/checks.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index da3b67b..c8a081a 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -5,12 +5,9 @@ on: types: - opened - synchronize - push: - branches: - - FTL-17136_integration_test jobs: rust-pipeline: - uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@77f989d893406492d4f9996ca0e17f37282006e7 + uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@main secrets: inherit with: auditIgnore: 'RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373' From b7f97b3b784372ebe78838c6270f4ed13819bc16 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Tue, 8 Oct 2024 15:47:54 +0200 Subject: [PATCH 13/18] feat: formatting --- .github/workflows/checks.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index c8a081a..167629e 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -5,6 +5,7 @@ on: types: - opened - synchronize + jobs: rust-pipeline: uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@main From 21f99ee456abaa7da637a4b55b24e1d9530a302d Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Wed, 9 Oct 2024 09:25:04 +0200 Subject: [PATCH 14/18] chore: test redis service workflow --- .github/workflows/checks.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 167629e..29b936c 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -5,10 +5,12 @@ on: types: - opened - synchronize - + push: + branches: + - FTL-17136_integration_test jobs: rust-pipeline: - uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@main + uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@b60c4f85ab0de4f803e1093978bfd2f3765516f5 secrets: inherit with: auditIgnore: 'RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373' From 173b1d85d509ef79967272801409ea0df3e5d6e2 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Wed, 9 Oct 2024 09:41:21 +0200 Subject: [PATCH 15/18] fix: init redis for coverage check --- .github/workflows/checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 29b936c..2996240 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -10,7 +10,7 @@ on: - FTL-17136_integration_test jobs: rust-pipeline: - uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@b60c4f85ab0de4f803e1093978bfd2f3765516f5 + uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@bef6eb731c274f98ef2c3c1c79d551f58420c97d secrets: inherit with: auditIgnore: 'RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373' From 488d0c453b2768298f694e2e308a089c53d90a65 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Wed, 9 Oct 2024 10:40:50 +0200 Subject: [PATCH 16/18] feat: enable redis service explicitly in workflow --- .github/workflows/checks.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 2996240..3976572 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -10,8 +10,9 @@ on: - FTL-17136_integration_test jobs: rust-pipeline: - uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@bef6eb731c274f98ef2c3c1c79d551f58420c97d + uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@127f5c5a125a1586319a51fc50bf7066c83b86a6 secrets: inherit with: auditIgnore: 'RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373' coverage: 10 + useRedis: true From 241a3f94466c8d965d9c430e5dbde69bb08d9ec3 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Wed, 9 Oct 2024 10:42:43 +0200 Subject: [PATCH 17/18] feat: disable redis service --- .github/workflows/checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 3976572..40d6532 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -15,4 +15,4 @@ jobs: with: auditIgnore: 'RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373' coverage: 10 - useRedis: true + useRedis: false From 919f0285abb94f6343edab0364f8735451f47d18 Mon Sep 17 00:00:00 2001 From: YoussefAWasfy Date: Wed, 9 Oct 2024 10:46:25 +0200 Subject: [PATCH 18/18] feat: use main rust pipeline --- .github/workflows/checks.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 40d6532..bd14fe7 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -5,14 +5,12 @@ on: types: - opened - synchronize - push: - branches: - - FTL-17136_integration_test + jobs: rust-pipeline: - uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@127f5c5a125a1586319a51fc50bf7066c83b86a6 + uses: affinidi/pipeline-rust/.github/workflows/checks.yaml@main secrets: inherit with: auditIgnore: 'RUSTSEC-2022-0040,RUSTSEC-2023-0071,RUSTSEC-2024-0373' coverage: 10 - useRedis: false + useRedis: true