1use crate::error::{Error, Result};
10use crate::event::EventBus;
11use bytes::Bytes;
12use serde::Serialize;
13use serde_json::json;
14use std::convert::Infallible;
15use std::sync::Arc;
16use std::time::Instant;
17use tap_agent::did::{DIDGenerationOptions, KeyType, Service};
18use tap_agent::key_manager::KeyManager;
19use tap_agent::{AgentConfig, AgentKeyManager, TapAgent};
20use tap_node::TapNode;
21use tracing::{debug, error, info, warn};
22use warp::{self, hyper::StatusCode, reply::json, Reply};
23
24#[derive(Serialize)]
26struct HealthResponse {
27 status: String,
29 version: String,
31}
32
33pub async fn handle_health_check(
38 event_bus: Arc<EventBus>,
39) -> std::result::Result<impl Reply, Infallible> {
40 info!("Health check request received");
41
42 let start_time = Instant::now();
44
45 event_bus
47 .publish_request_received(
48 "GET".to_string(),
49 "/health".to_string(),
50 None, )
52 .await;
53
54 let response = HealthResponse {
56 status: "ok".to_string(),
57 version: env!("CARGO_PKG_VERSION").to_string(),
58 };
59
60 let json_response = json(&response);
62
63 let response_size = serde_json::to_string(&response)
65 .map(|s| s.len())
66 .unwrap_or(0);
67
68 let duration_ms = start_time.elapsed().as_millis() as u64;
70
71 event_bus
73 .publish_response_sent(StatusCode::OK, response_size, duration_ms)
74 .await;
75
76 Ok(json_response)
77}
78
79pub async fn handle_didcomm(
89 content_type: Option<String>,
90 body: Bytes,
91 node: Arc<TapNode>,
92 event_bus: Arc<EventBus>,
93) -> std::result::Result<impl Reply, Infallible> {
94 let start_time = Instant::now();
96
97 event_bus
99 .publish_request_received(
100 "POST".to_string(),
101 "/didcomm".to_string(),
102 None, )
104 .await;
105
106 if let Err(e) = validate_message_security(content_type.as_deref()) {
108 error!("Content-Type validation failed: {}", e);
109
110 let response = e.to_response();
111 let duration_ms = start_time.elapsed().as_millis() as u64;
112
113 event_bus
114 .publish_response_sent(e.status_code(), 200, duration_ms)
115 .await;
116
117 return Ok(response);
118 }
119
120 let message_str = match std::str::from_utf8(&body) {
122 Ok(s) => s,
123 Err(e) => {
124 error!("Failed to parse request body as UTF-8: {}", e);
125
126 let response =
127 json_error_response(StatusCode::BAD_REQUEST, "Invalid UTF-8 in request body");
128 let duration_ms = start_time.elapsed().as_millis() as u64;
129
130 event_bus
131 .publish_response_sent(StatusCode::BAD_REQUEST, 200, duration_ms)
132 .await;
133
134 return Ok(response);
135 }
136 };
137
138 let message_value: serde_json::Value = match serde_json::from_str(message_str) {
139 Ok(v) => v,
140 Err(e) => {
141 error!("Failed to parse message as JSON: {}", e);
142
143 let response =
144 json_error_response(StatusCode::BAD_REQUEST, "Invalid JSON in request body");
145 let duration_ms = start_time.elapsed().as_millis() as u64;
146
147 event_bus
148 .publish_response_sent(StatusCode::BAD_REQUEST, 200, duration_ms)
149 .await;
150
151 return Ok(response);
152 }
153 };
154
155 match node.receive_message(message_value).await {
157 Ok(_) => {
158 info!("DIDComm message processed successfully");
159
160 let response = json_success_response();
162 let response_size = 100; let duration_ms = start_time.elapsed().as_millis() as u64;
164
165 event_bus
167 .publish_response_sent(StatusCode::ACCEPTED, response_size, duration_ms)
168 .await;
169
170 Ok(response)
171 }
172 Err(e) => {
173 error!("Failed to process message: {}", e);
174
175 event_bus
177 .publish_message_error(
178 "node_error".to_string(),
179 e.to_string(),
180 None, )
182 .await;
183
184 let response =
186 json_error_response(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error");
187
188 let response_size = 200; let duration_ms = start_time.elapsed().as_millis() as u64;
191
192 event_bus
194 .publish_response_sent(
195 StatusCode::INTERNAL_SERVER_ERROR,
196 response_size,
197 duration_ms,
198 )
199 .await;
200
201 Ok(response)
203 }
204 }
205}
206
207fn validate_message_security(content_type: Option<&str>) -> Result<()> {
219 match content_type {
220 Some(ct) => {
221 let ct_lower = ct.to_lowercase();
223
224 if ct_lower.contains("application/didcomm-signed+json") {
226 debug!("Message security validation passed: signed message");
227 Ok(())
228 } else if ct_lower.contains("application/didcomm-encrypted+json") {
229 debug!("Message security validation passed: encrypted message");
230 Ok(())
231 } else if ct_lower.contains("application/didcomm-plain+json") {
232 Err(Error::Validation(
233 "Plain DIDComm messages are not allowed for security reasons. Only signed or encrypted messages are accepted.".to_string()
234 ))
235 } else {
236 Err(Error::Validation(
237 format!("Invalid Content-Type '{}'. Expected 'application/didcomm-signed+json' or 'application/didcomm-encrypted+json'.", ct)
238 ))
239 }
240 }
241 None => {
242 Err(Error::Validation(
243 "Missing Content-Type header. Expected 'application/didcomm-signed+json' or 'application/didcomm-encrypted+json'.".to_string()
244 ))
245 }
246 }
247}
248
249fn json_success_response() -> warp::reply::Response {
253 warp::reply::with_status(
254 json(&json!({
255 "status": "success",
256 "message": "Message received and processed"
257 })),
258 StatusCode::ACCEPTED,
259 )
260 .into_response()
261}
262
263fn json_error_response(status: StatusCode, message: &str) -> warp::reply::Response {
271 warp::reply::with_status(
272 json(&json!({
273 "status": "error",
274 "message": message
275 })),
276 status,
277 )
278 .into_response()
279}
280
281const MAX_DOMAIN_LENGTH: usize = 253;
283
284pub fn sanitize_domain(host: &str) -> Result<String> {
291 let trimmed = host.trim();
292
293 if trimmed.is_empty() {
294 return Err(Error::Validation("Empty domain name".to_string()));
295 }
296
297 let (domain, port) = if let Some(colon_idx) = trimmed.rfind(':') {
299 let potential_port = &trimmed[colon_idx + 1..];
300 if potential_port.chars().all(|c| c.is_ascii_digit()) && !potential_port.is_empty() {
302 (&trimmed[..colon_idx], Some(potential_port))
303 } else {
304 (trimmed, None)
305 }
306 } else {
307 (trimmed, None)
308 };
309
310 if domain.is_empty() {
311 return Err(Error::Validation("Empty domain name".to_string()));
312 }
313
314 if domain.len() > MAX_DOMAIN_LENGTH {
315 return Err(Error::Validation(format!(
316 "Domain name exceeds maximum length of {} characters",
317 MAX_DOMAIN_LENGTH
318 )));
319 }
320
321 for ch in domain.chars() {
323 if !ch.is_ascii_alphanumeric() && ch != '-' && ch != '.' {
324 return Err(Error::Validation(format!(
325 "Invalid character '{}' in domain name",
326 ch
327 )));
328 }
329 }
330
331 if domain.starts_with('.') || domain.ends_with('.') {
333 return Err(Error::Validation(
334 "Domain name must not start or end with a dot".to_string(),
335 ));
336 }
337
338 if domain.starts_with('-') || domain.ends_with('-') {
339 return Err(Error::Validation(
340 "Domain name must not start or end with a hyphen".to_string(),
341 ));
342 }
343
344 if domain.contains("..") {
346 return Err(Error::Validation(
347 "Domain name must not contain consecutive dots".to_string(),
348 ));
349 }
350
351 for label in domain.split('.') {
353 if label.is_empty() {
354 return Err(Error::Validation(
355 "Domain name contains an empty label".to_string(),
356 ));
357 }
358 if label.len() > 63 {
359 return Err(Error::Validation(
360 "Domain label exceeds maximum length of 63 characters".to_string(),
361 ));
362 }
363 if label.starts_with('-') || label.ends_with('-') {
364 return Err(Error::Validation(
365 "Domain label must not start or end with a hyphen".to_string(),
366 ));
367 }
368 }
369
370 if let Some(p) = port {
372 let port_num: u16 = p
373 .parse()
374 .map_err(|_| Error::Validation(format!("Invalid port number: {}", p)))?;
375 if port_num == 0 {
376 return Err(Error::Validation(
377 "Port number must not be zero".to_string(),
378 ));
379 }
380 }
381
382 let sanitized_domain = domain.to_lowercase();
384 match port {
385 Some(p) => Ok(format!("{}:{}", sanitized_domain, p)),
386 None => Ok(sanitized_domain),
387 }
388}
389
390pub fn domain_to_did_web(domain: &str) -> String {
395 let encoded = domain.replace(':', "%3A");
397 format!("did:web:{}", encoded)
398}
399
400fn did_doc_to_json_ld(doc: &tap_agent::did::DIDDoc) -> serde_json::Value {
406 use tap_agent::did::{VerificationMaterial, VerificationMethodType};
407
408 let verification_methods: Vec<serde_json::Value> = doc
409 .verification_method
410 .iter()
411 .map(|vm| {
412 let mut obj = json!({
413 "id": vm.id,
414 "type": match &vm.type_ {
415 VerificationMethodType::Ed25519VerificationKey2018 => "Ed25519VerificationKey2018",
416 VerificationMethodType::X25519KeyAgreementKey2019 => "X25519KeyAgreementKey2019",
417 VerificationMethodType::EcdsaSecp256k1VerificationKey2019 => "EcdsaSecp256k1VerificationKey2019",
418 VerificationMethodType::JsonWebKey2020 => "JsonWebKey2020",
419 },
420 "controller": vm.controller,
421 });
422
423 match &vm.verification_material {
424 VerificationMaterial::Base58 { public_key_base58 } => {
425 obj["publicKeyBase58"] = json!(public_key_base58);
426 }
427 VerificationMaterial::Multibase {
428 public_key_multibase,
429 } => {
430 obj["publicKeyMultibase"] = json!(public_key_multibase);
431 }
432 VerificationMaterial::JWK { public_key_jwk } => {
433 obj["publicKeyJwk"] = public_key_jwk.clone();
434 }
435 }
436
437 obj
438 })
439 .collect();
440
441 let services: Vec<serde_json::Value> = doc
442 .service
443 .iter()
444 .map(|svc| {
445 let mut obj = json!({
446 "id": svc.id,
447 "type": svc.type_,
448 "serviceEndpoint": svc.service_endpoint,
449 });
450 for (k, v) in &svc.properties {
452 obj[k] = v.clone();
453 }
454 obj
455 })
456 .collect();
457
458 let mut result = json!({
459 "@context": [
460 "https://www.w3.org/ns/did/v1",
461 "https://w3id.org/security/suites/ed25519-2018/v1"
462 ],
463 "id": doc.id,
464 "verificationMethod": verification_methods,
465 "authentication": doc.authentication,
466 });
467
468 if !doc.key_agreement.is_empty() {
469 result["keyAgreement"] = json!(doc.key_agreement);
470 }
471 if !doc.assertion_method.is_empty() {
472 result["assertionMethod"] = json!(doc.assertion_method);
473 }
474 if !doc.capability_invocation.is_empty() {
475 result["capabilityInvocation"] = json!(doc.capability_invocation);
476 }
477 if !doc.capability_delegation.is_empty() {
478 result["capabilityDelegation"] = json!(doc.capability_delegation);
479 }
480 if !services.is_empty() {
481 result["service"] = json!(services);
482 }
483
484 result
485}
486
487pub async fn handle_well_known_did(
496 host: Option<String>,
497 node: Arc<TapNode>,
498 event_bus: Arc<EventBus>,
499 max_agents: usize,
500) -> std::result::Result<impl Reply, Infallible> {
501 let start_time = Instant::now();
502
503 event_bus
504 .publish_request_received("GET".to_string(), "/.well-known/did.json".to_string(), None)
505 .await;
506
507 let domain = match host {
509 Some(h) => match sanitize_domain(&h) {
510 Ok(d) => d,
511 Err(e) => {
512 warn!("Invalid Host header for web DID: {}", e);
513 let response = json_error_response(StatusCode::BAD_REQUEST, "Invalid Host header");
514 let duration_ms = start_time.elapsed().as_millis() as u64;
515 event_bus
516 .publish_response_sent(StatusCode::BAD_REQUEST, 200, duration_ms)
517 .await;
518 return Ok(response);
519 }
520 },
521 None => {
522 let response = json_error_response(StatusCode::BAD_REQUEST, "Missing Host header");
523 let duration_ms = start_time.elapsed().as_millis() as u64;
524 event_bus
525 .publish_response_sent(StatusCode::BAD_REQUEST, 200, duration_ms)
526 .await;
527 return Ok(response);
528 }
529 };
530
531 let did_web = domain_to_did_web(&domain);
532 info!("Web DID document requested for: {}", did_web);
533
534 if node.agents().has_agent(&did_web) {
536 debug!("Found existing agent for {}", did_web);
537 match node.agents().get_agent(&did_web).await {
538 Ok(agent) => match agent.key_manager().get_generated_key(&did_web) {
539 Ok(generated_key) => {
540 let did_doc = did_doc_to_json_ld(&generated_key.did_doc);
541 let response =
542 warp::reply::with_status(warp::reply::json(&did_doc), StatusCode::OK)
543 .into_response();
544 let duration_ms = start_time.elapsed().as_millis() as u64;
545 event_bus
546 .publish_response_sent(StatusCode::OK, 500, duration_ms)
547 .await;
548 return Ok(response);
549 }
550 Err(e) => {
551 error!("Failed to get DID document for {}: {}", did_web, e);
552 let response = json_error_response(
553 StatusCode::INTERNAL_SERVER_ERROR,
554 "Failed to retrieve DID document",
555 );
556 let duration_ms = start_time.elapsed().as_millis() as u64;
557 event_bus
558 .publish_response_sent(StatusCode::INTERNAL_SERVER_ERROR, 200, duration_ms)
559 .await;
560 return Ok(response);
561 }
562 },
563 Err(e) => {
564 error!("Failed to get agent for {}: {}", did_web, e);
565 let response = json_error_response(
566 StatusCode::INTERNAL_SERVER_ERROR,
567 "Failed to retrieve agent",
568 );
569 let duration_ms = start_time.elapsed().as_millis() as u64;
570 event_bus
571 .publish_response_sent(StatusCode::INTERNAL_SERVER_ERROR, 200, duration_ms)
572 .await;
573 return Ok(response);
574 }
575 }
576 }
577
578 if node.agents().agent_count() >= max_agents {
580 warn!(
581 "Agent limit reached ({}/{}), refusing to create agent for {}",
582 node.agents().agent_count(),
583 max_agents,
584 did_web
585 );
586 let response = json_error_response(
587 StatusCode::SERVICE_UNAVAILABLE,
588 "Maximum number of agents reached",
589 );
590 let duration_ms = start_time.elapsed().as_millis() as u64;
591 event_bus
592 .publish_response_sent(StatusCode::SERVICE_UNAVAILABLE, 200, duration_ms)
593 .await;
594 return Ok(response);
595 }
596
597 info!("Creating new agent for {}", did_web);
599
600 let key_manager = AgentKeyManager::new();
601 let options = DIDGenerationOptions {
602 key_type: KeyType::Ed25519,
603 };
604
605 let encoded_domain = domain.replace(':', "%3A");
607 let generated_key = match key_manager.generate_web_did(&encoded_domain, options) {
608 Ok(k) => k,
609 Err(e) => {
610 error!("Failed to generate web DID for {}: {}", domain, e);
611 let response =
612 json_error_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to generate DID");
613 let duration_ms = start_time.elapsed().as_millis() as u64;
614 event_bus
615 .publish_response_sent(StatusCode::INTERNAL_SERVER_ERROR, 200, duration_ms)
616 .await;
617 return Ok(response);
618 }
619 };
620
621 let scheme = "https";
623 let didcomm_endpoint = format!("{}://{}/didcomm", scheme, domain);
624
625 let mut did_doc = generated_key.did_doc.clone();
627 did_doc.service.push(Service {
628 id: format!("{}#didcomm", did_web),
629 type_: "DIDCommMessaging".to_string(),
630 service_endpoint: didcomm_endpoint,
631 properties: Default::default(),
632 });
633
634 let config = AgentConfig::new(did_web.clone());
636 let agent = TapAgent::new(config, Arc::new(key_manager));
637
638 if let Err(e) = node.register_agent(Arc::new(agent)).await {
639 warn!("Failed to register new agent for {}: {}", did_web, e);
641 }
642
643 let json_doc = did_doc_to_json_ld(&did_doc);
644 let response =
645 warp::reply::with_status(warp::reply::json(&json_doc), StatusCode::OK).into_response();
646 let duration_ms = start_time.elapsed().as_millis() as u64;
647 event_bus
648 .publish_response_sent(StatusCode::OK, 500, duration_ms)
649 .await;
650
651 Ok(response)
652}
653
654#[cfg(test)]
655mod tests {
656 use super::*;
657 use serde_json::Value;
658 use tap_node::NodeConfig;
659 use warp::hyper::body::to_bytes;
660
661 #[tokio::test]
662 async fn test_health_check() {
663 let event_bus = Arc::new(crate::event::EventBus::new());
665
666 let response = handle_health_check(event_bus).await.unwrap();
668
669 let response_bytes = to_bytes(response.into_response().into_body())
671 .await
672 .unwrap();
673 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
674
675 assert_eq!(response_json["status"], "ok");
677 assert!(response_json["version"].is_string());
678 }
679
680 #[test]
681 fn test_validate_message_security() {
682 let result = validate_message_security(Some("application/didcomm-plain+json"));
684 assert!(result.is_err());
685 assert!(result
686 .unwrap_err()
687 .to_string()
688 .contains("Plain DIDComm messages are not allowed"));
689
690 let result = validate_message_security(Some("application/didcomm-signed+json"));
692 assert!(result.is_ok());
693
694 let result = validate_message_security(Some("application/didcomm-encrypted+json"));
696 assert!(result.is_ok());
697
698 let result =
700 validate_message_security(Some("application/didcomm-signed+json; charset=utf-8"));
701 assert!(result.is_ok());
702
703 let result = validate_message_security(Some("application/json"));
705 assert!(result.is_err());
706 assert!(result
707 .unwrap_err()
708 .to_string()
709 .contains("Invalid Content-Type"));
710
711 let result = validate_message_security(None);
713 assert!(result.is_err());
714 assert!(result
715 .unwrap_err()
716 .to_string()
717 .contains("Missing Content-Type header"));
718 }
719
720 #[tokio::test(flavor = "multi_thread")]
721 async fn test_handle_invalid_didcomm() {
722 let config = NodeConfig {
724 storage_path: None, ..Default::default()
726 };
727 let node = Arc::new(TapNode::new(config));
728
729 let event_bus = Arc::new(crate::event::EventBus::new());
731
732 let invalid_bytes = Bytes::from(vec![0xFF, 0xFF]);
734 let response = handle_didcomm(
735 Some("application/didcomm-signed+json".to_string()),
736 invalid_bytes,
737 node.clone(),
738 event_bus,
739 )
740 .await
741 .unwrap();
742
743 let response_bytes = to_bytes(response.into_response().into_body())
745 .await
746 .unwrap();
747 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
748
749 assert_eq!(response_json["status"], "error");
751 assert!(response_json["message"]
752 .as_str()
753 .unwrap()
754 .contains("Invalid UTF-8"));
755 }
756
757 #[tokio::test]
758 async fn test_json_error_response() {
759 let response = json_error_response(StatusCode::BAD_REQUEST, "Test error message");
761
762 let response_bytes = to_bytes(response.into_body()).await.unwrap();
764 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
765
766 assert_eq!(response_json["status"], "error");
768 assert_eq!(response_json["message"], "Test error message");
769 }
770
771 #[tokio::test]
772 async fn test_json_success_response() {
773 let response = json_success_response();
775
776 let response_bytes = to_bytes(response.into_body()).await.unwrap();
778 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
779
780 assert_eq!(response_json["status"], "success");
782 assert_eq!(response_json["message"], "Message received and processed");
783 }
784
785 #[test]
788 fn test_sanitize_domain_valid() {
789 assert_eq!(sanitize_domain("example.com").unwrap(), "example.com");
790 assert_eq!(
791 sanitize_domain("sub.example.com").unwrap(),
792 "sub.example.com"
793 );
794 assert_eq!(
795 sanitize_domain("example.com:8080").unwrap(),
796 "example.com:8080"
797 );
798 assert_eq!(sanitize_domain("EXAMPLE.COM").unwrap(), "example.com");
799 assert_eq!(
800 sanitize_domain("my-host.example.com").unwrap(),
801 "my-host.example.com"
802 );
803 }
804
805 #[test]
806 fn test_sanitize_domain_trims_whitespace() {
807 assert_eq!(sanitize_domain(" example.com ").unwrap(), "example.com");
808 }
809
810 #[test]
811 fn test_sanitize_domain_rejects_empty() {
812 assert!(sanitize_domain("").is_err());
813 assert!(sanitize_domain(" ").is_err());
814 }
815
816 #[test]
817 fn test_sanitize_domain_rejects_invalid_chars() {
818 assert!(sanitize_domain("example.com/path").is_err());
819 assert!(sanitize_domain("example.com\\path").is_err());
820 assert!(sanitize_domain("exam ple.com").is_err());
821 assert!(sanitize_domain("example.com?query").is_err());
822 assert!(sanitize_domain("<script>").is_err());
823 assert!(sanitize_domain("example.com#frag").is_err());
824 assert!(sanitize_domain("ex@mple.com").is_err());
825 }
826
827 #[test]
828 fn test_sanitize_domain_rejects_invalid_structure() {
829 assert!(sanitize_domain(".example.com").is_err());
830 assert!(sanitize_domain("example.com.").is_err());
831 assert!(sanitize_domain("example..com").is_err());
832 assert!(sanitize_domain("-example.com").is_err());
833 assert!(sanitize_domain("example.com-").is_err());
834 assert!(sanitize_domain("exam.-ple.com").is_err());
835 }
836
837 #[test]
838 fn test_sanitize_domain_rejects_port_zero() {
839 assert!(sanitize_domain("example.com:0").is_err());
840 }
841
842 #[test]
843 fn test_sanitize_domain_rejects_too_long() {
844 let long_label = "a".repeat(64);
845 let long_domain = format!("{}.com", long_label);
846 assert!(sanitize_domain(&long_domain).is_err());
847 }
848
849 #[test]
852 fn test_domain_to_did_web_simple() {
853 assert_eq!(domain_to_did_web("example.com"), "did:web:example.com");
854 }
855
856 #[test]
857 fn test_domain_to_did_web_with_port() {
858 assert_eq!(
859 domain_to_did_web("example.com:3000"),
860 "did:web:example.com%3A3000"
861 );
862 }
863
864 #[test]
865 fn test_domain_to_did_web_subdomain() {
866 assert_eq!(
867 domain_to_did_web("api.example.com"),
868 "did:web:api.example.com"
869 );
870 }
871
872 #[test]
875 fn test_did_doc_to_json_ld_has_context() {
876 use tap_agent::did::DIDDoc;
877 let doc = DIDDoc {
878 id: "did:web:example.com".to_string(),
879 verification_method: vec![],
880 authentication: vec![],
881 key_agreement: vec![],
882 assertion_method: vec![],
883 capability_invocation: vec![],
884 capability_delegation: vec![],
885 service: vec![],
886 };
887
888 let json = did_doc_to_json_ld(&doc);
889 assert!(json["@context"].is_array());
890 assert_eq!(json["id"], "did:web:example.com");
891 assert!(json["verificationMethod"].is_array());
892 assert!(json.get("assertionMethod").is_none());
894 assert!(json.get("capabilityInvocation").is_none());
895 assert!(json.get("capabilityDelegation").is_none());
896 assert!(json.get("service").is_none());
898 }
899
900 #[tokio::test(flavor = "multi_thread")]
903 async fn test_well_known_did_missing_host() {
904 let config = NodeConfig {
905 storage_path: None,
906 ..Default::default()
907 };
908 let node = Arc::new(TapNode::new(config));
909 let event_bus = Arc::new(crate::event::EventBus::new());
910
911 let response = handle_well_known_did(None, node, event_bus, 100)
912 .await
913 .unwrap();
914
915 let response_bytes = to_bytes(response.into_response().into_body())
916 .await
917 .unwrap();
918 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
919 assert_eq!(response_json["status"], "error");
920 assert!(response_json["message"]
921 .as_str()
922 .unwrap()
923 .contains("Missing Host header"));
924 }
925
926 #[tokio::test(flavor = "multi_thread")]
927 async fn test_well_known_did_invalid_host() {
928 let config = NodeConfig {
929 storage_path: None,
930 ..Default::default()
931 };
932 let node = Arc::new(TapNode::new(config));
933 let event_bus = Arc::new(crate::event::EventBus::new());
934
935 let response = handle_well_known_did(
936 Some("<script>alert(1)</script>".to_string()),
937 node,
938 event_bus,
939 100,
940 )
941 .await
942 .unwrap();
943
944 let response_bytes = to_bytes(response.into_response().into_body())
945 .await
946 .unwrap();
947 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
948 assert_eq!(response_json["status"], "error");
949 assert!(response_json["message"]
950 .as_str()
951 .unwrap()
952 .contains("Invalid Host header"));
953 }
954
955 #[tokio::test(flavor = "multi_thread")]
956 async fn test_well_known_did_creates_new_agent() {
957 let config = NodeConfig {
958 storage_path: None,
959 ..Default::default()
960 };
961 let node = Arc::new(TapNode::new(config));
962 let event_bus = Arc::new(crate::event::EventBus::new());
963
964 let response = handle_well_known_did(
965 Some("example.com".to_string()),
966 node.clone(),
967 event_bus,
968 100,
969 )
970 .await
971 .unwrap();
972
973 let response_bytes = to_bytes(response.into_response().into_body())
974 .await
975 .unwrap();
976 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
977
978 assert_eq!(response_json["id"], "did:web:example.com");
980 assert!(response_json["@context"].is_array());
981 assert!(response_json["verificationMethod"].is_array());
982 assert!(!response_json["verificationMethod"]
983 .as_array()
984 .unwrap()
985 .is_empty());
986
987 let services = response_json["service"].as_array().unwrap();
989 assert!(!services.is_empty());
990 assert_eq!(services[0]["type"], "DIDCommMessaging");
991 assert_eq!(
992 services[0]["serviceEndpoint"],
993 "https://example.com/didcomm"
994 );
995
996 assert!(node.agents().has_agent("did:web:example.com"));
998 }
999
1000 #[tokio::test(flavor = "multi_thread")]
1001 async fn test_well_known_did_returns_existing_agent() {
1002 let config = NodeConfig {
1003 storage_path: None,
1004 ..Default::default()
1005 };
1006 let node = Arc::new(TapNode::new(config));
1007 let event_bus = Arc::new(crate::event::EventBus::new());
1008
1009 let key_manager = AgentKeyManager::new();
1011 let options = DIDGenerationOptions {
1012 key_type: KeyType::Ed25519,
1013 };
1014 let _generated = key_manager
1015 .generate_web_did("test.example.com", options)
1016 .unwrap();
1017 let agent_config = AgentConfig::new("did:web:test.example.com".to_string());
1018 let agent = TapAgent::new(agent_config, Arc::new(key_manager));
1019 node.register_agent(Arc::new(agent)).await.unwrap();
1020
1021 let response = handle_well_known_did(
1023 Some("test.example.com".to_string()),
1024 node.clone(),
1025 event_bus,
1026 100,
1027 )
1028 .await
1029 .unwrap();
1030
1031 let response_bytes = to_bytes(response.into_response().into_body())
1032 .await
1033 .unwrap();
1034 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
1035
1036 assert_eq!(response_json["id"], "did:web:test.example.com");
1037 assert!(response_json["@context"].is_array());
1038 }
1039
1040 #[tokio::test(flavor = "multi_thread")]
1041 async fn test_well_known_did_with_port() {
1042 let config = NodeConfig {
1043 storage_path: None,
1044 ..Default::default()
1045 };
1046 let node = Arc::new(TapNode::new(config));
1047 let event_bus = Arc::new(crate::event::EventBus::new());
1048
1049 let response = handle_well_known_did(
1050 Some("localhost:3000".to_string()),
1051 node.clone(),
1052 event_bus,
1053 100,
1054 )
1055 .await
1056 .unwrap();
1057
1058 let response_bytes = to_bytes(response.into_response().into_body())
1059 .await
1060 .unwrap();
1061 let response_json: Value = serde_json::from_slice(&response_bytes).unwrap();
1062
1063 assert_eq!(response_json["id"], "did:web:localhost%3A3000");
1064
1065 let services = response_json["service"].as_array().unwrap();
1067 assert_eq!(
1068 services[0]["serviceEndpoint"],
1069 "https://localhost:3000/didcomm"
1070 );
1071
1072 assert!(node.agents().has_agent("did:web:localhost%3A3000"));
1074 }
1075
1076 #[tokio::test(flavor = "multi_thread")]
1077 async fn test_well_known_did_idempotent() {
1078 let config = NodeConfig {
1079 storage_path: None,
1080 ..Default::default()
1081 };
1082 let node = Arc::new(TapNode::new(config));
1083 let event_bus = Arc::new(crate::event::EventBus::new());
1084
1085 let response1 = handle_well_known_did(
1087 Some("idempotent.example.com".to_string()),
1088 node.clone(),
1089 event_bus.clone(),
1090 100,
1091 )
1092 .await
1093 .unwrap();
1094 let bytes1 = to_bytes(response1.into_response().into_body())
1095 .await
1096 .unwrap();
1097 let json1: Value = serde_json::from_slice(&bytes1).unwrap();
1098
1099 let response2 = handle_well_known_did(
1101 Some("idempotent.example.com".to_string()),
1102 node.clone(),
1103 event_bus,
1104 100,
1105 )
1106 .await
1107 .unwrap();
1108 let bytes2 = to_bytes(response2.into_response().into_body())
1109 .await
1110 .unwrap();
1111 let json2: Value = serde_json::from_slice(&bytes2).unwrap();
1112
1113 assert_eq!(json1["id"], json2["id"]);
1115 assert_eq!(json1["verificationMethod"], json2["verificationMethod"]);
1116 }
1117}