tap_node/validation/
mod.rs1use crate::storage::Storage;
11use async_trait::async_trait;
12use std::sync::Arc;
13use tap_msg::didcomm::PlainMessage;
14
15pub mod agent_validator;
16pub mod timestamp_validator;
17pub mod uniqueness_validator;
18
19#[derive(Debug, Clone)]
21pub enum ValidationResult {
22 Accept,
24 Reject(String),
26}
27
28#[async_trait]
30pub trait MessageValidator: Send + Sync {
31 async fn validate(&self, message: &PlainMessage) -> ValidationResult;
36}
37
38pub struct CompositeValidator {
40 validators: Vec<Box<dyn MessageValidator>>,
41}
42
43impl CompositeValidator {
44 pub fn new(validators: Vec<Box<dyn MessageValidator>>) -> Self {
46 Self { validators }
47 }
48}
49
50#[async_trait]
51impl MessageValidator for CompositeValidator {
52 async fn validate(&self, message: &PlainMessage) -> ValidationResult {
53 for validator in &self.validators {
54 match validator.validate(message).await {
55 ValidationResult::Accept => continue,
56 ValidationResult::Reject(reason) => return ValidationResult::Reject(reason),
57 }
58 }
59 ValidationResult::Accept
60 }
61}
62
63pub struct StandardValidatorConfig {
65 pub max_timestamp_drift_secs: i64,
67 pub storage: Arc<Storage>,
69}
70
71pub async fn create_standard_validator(config: StandardValidatorConfig) -> CompositeValidator {
76 let validators: Vec<Box<dyn MessageValidator>> = vec![
77 Box::new(timestamp_validator::TimestampValidator::new(
78 config.max_timestamp_drift_secs,
79 )),
80 Box::new(uniqueness_validator::UniquenessValidator::new(
81 config.storage.clone(),
82 )),
83 Box::new(agent_validator::AgentAuthorizationValidator::new(
84 config.storage.clone(),
85 )),
86 ];
87
88 CompositeValidator::new(validators)
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 struct AlwaysAcceptValidator;
96
97 #[async_trait]
98 impl MessageValidator for AlwaysAcceptValidator {
99 async fn validate(&self, _message: &PlainMessage) -> ValidationResult {
100 ValidationResult::Accept
101 }
102 }
103
104 struct AlwaysRejectValidator {
105 reason: String,
106 }
107
108 #[async_trait]
109 impl MessageValidator for AlwaysRejectValidator {
110 async fn validate(&self, _message: &PlainMessage) -> ValidationResult {
111 ValidationResult::Reject(self.reason.clone())
112 }
113 }
114
115 #[tokio::test]
116 async fn test_composite_validator_all_accept() {
117 let validators: Vec<Box<dyn MessageValidator>> = vec![
118 Box::new(AlwaysAcceptValidator),
119 Box::new(AlwaysAcceptValidator),
120 ];
121
122 let composite = CompositeValidator::new(validators);
123 let message = PlainMessage::new(
124 "test_msg_1".to_string(),
125 "test_type".to_string(),
126 serde_json::json!({}),
127 "did:example:sender".to_string(),
128 )
129 .with_recipient("did:example:receiver");
130
131 match composite.validate(&message).await {
132 ValidationResult::Accept => {} ValidationResult::Reject(reason) => panic!("Expected accept, got reject: {}", reason),
134 }
135 }
136
137 #[tokio::test]
138 async fn test_composite_validator_one_reject() {
139 let validators: Vec<Box<dyn MessageValidator>> = vec![
140 Box::new(AlwaysAcceptValidator),
141 Box::new(AlwaysRejectValidator {
142 reason: "Test rejection".to_string(),
143 }),
144 Box::new(AlwaysAcceptValidator),
145 ];
146
147 let composite = CompositeValidator::new(validators);
148 let message = PlainMessage::new(
149 "test_msg_1".to_string(),
150 "test_type".to_string(),
151 serde_json::json!({}),
152 "did:example:sender".to_string(),
153 )
154 .with_recipient("did:example:receiver");
155
156 match composite.validate(&message).await {
157 ValidationResult::Accept => panic!("Expected reject, got accept"),
158 ValidationResult::Reject(reason) => assert_eq!(reason, "Test rejection"),
159 }
160 }
161}