dif_presentation_exchange/
input_evaluation.rs1use crate::InputDescriptor;
2use jsonpath_lib as jsonpath;
3use jsonschema::JSONSchema;
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum FieldQueryResult {
7 Some { value: serde_json::Value, path: String },
8 None,
9 Invalid,
10}
11
12impl FieldQueryResult {
13 pub fn is_valid(&self) -> bool {
14 !self.is_invalid()
15 }
16
17 pub fn is_invalid(&self) -> bool {
18 *self == FieldQueryResult::Invalid
19 }
20}
21
22pub fn evaluate_input(input_descriptor: &InputDescriptor, value: &serde_json::Value) -> bool {
26 let selector = &mut jsonpath::selector(value);
27
28 input_descriptor
29 .constraints()
30 .fields()
31 .as_ref()
32 .map(|fields| {
33 let results: Vec<FieldQueryResult> = fields
34 .iter()
35 .map(|field| {
36 let filter = field
37 .filter()
38 .as_ref()
39 .map(JSONSchema::compile)
40 .transpose()
41 .ok()
42 .flatten();
43
44 field
48 .path()
49 .iter()
50 .find_map(|path| {
52 selector(path).ok().and_then(|values| {
55 values.into_iter().find_map(|result| {
56 filter
59 .as_ref()
60 .map(|filter| filter.is_valid(result))
61 .unwrap_or(true)
62 .then(|| FieldQueryResult::Some {
64 value: result.to_owned(),
65 path: path.to_owned(),
66 })
67 })
69 })
70 })
71 .or_else(|| field.optional().and_then(|opt| opt.then(|| FieldQueryResult::None)))
77 .unwrap_or(FieldQueryResult::Invalid)
78 })
79 .collect();
80 results.iter().all(FieldQueryResult::is_valid)
81 })
82 .unwrap_or(false)
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::{
89 presentation_definition::{Constraints, Field},
90 InputDescriptor,
91 };
92 use serde::de::DeserializeOwned;
93 use std::{fs::File, path::Path};
94
95 fn json_example<T>(path: &str) -> T
96 where
97 T: DeserializeOwned,
98 {
99 let file_path = Path::new(path);
100 let file = File::open(file_path).expect("file does not exist");
101 serde_json::from_reader::<_, T>(file).expect("could not parse json")
102 }
103
104 fn input_descriptor(constraints: Constraints) -> InputDescriptor {
105 InputDescriptor {
106 id: "test_input_descriptor".to_string(),
107 name: None,
108 purpose: None,
109 format: None,
110 constraints,
111 schema: None,
112 }
113 }
114
115 #[test]
116 fn test_constraints() {
117 let credential = json_example::<serde_json::Value>("../oid4vp/tests/examples/credentials/jwt_vc.json");
118
119 assert!(!evaluate_input(&input_descriptor(Constraints::default()), &credential));
121
122 assert!(evaluate_input(
124 &input_descriptor(Constraints {
125 fields: Some(vec![Field {
126 path: vec!["$.vc.type".to_string()],
127 ..Default::default()
128 }]),
129 ..Default::default()
130 }),
131 &credential
132 ));
133
134 assert!(!evaluate_input(
136 &input_descriptor(Constraints {
137 fields: Some(vec![Field {
138 path: vec!["$.vc.foo".to_string()],
139 ..Default::default()
140 }]),
141 ..Default::default()
142 }),
143 &credential
144 ));
145
146 assert!(!evaluate_input(
148 &input_descriptor(Constraints {
149 fields: Some(vec![
150 Field {
151 path: vec!["$.vc.foo".to_string()],
152 ..Default::default()
153 },
154 Field {
155 path: vec!["$.vc.type".to_string()],
156 ..Default::default()
157 },
158 ]),
159 ..Default::default()
160 }),
161 &credential
162 ));
163
164 assert!(!evaluate_input(
166 &input_descriptor(Constraints {
167 fields: Some(vec![
168 Field {
169 path: vec!["$.vc.type".to_string()],
170 ..Default::default()
171 },
172 Field {
173 path: vec!["$.vc.foo".to_string()],
174 ..Default::default()
175 },
176 ]),
177 ..Default::default()
178 }),
179 &credential
180 ));
181
182 assert!(evaluate_input(
184 &input_descriptor(Constraints {
185 fields: Some(vec![
186 Field {
187 path: vec!["$.vc.type".to_string()],
188 ..Default::default()
189 },
190 Field {
191 path: vec!["$.vc.foo".to_string()],
192 optional: Some(true),
193 ..Default::default()
194 },
195 ]),
196 ..Default::default()
197 }),
198 &credential
199 ));
200 }
201
202 #[test]
203 fn test_field() {
204 let credential = json_example::<serde_json::Value>("../oid4vp/tests/examples/credentials/jwt_vc.json");
205
206 assert!(!evaluate_input(
208 &input_descriptor(Constraints {
209 fields: Some(vec![Field::default()]),
210 ..Default::default()
211 }),
212 &credential
213 ));
214
215 assert!(evaluate_input(
217 &input_descriptor(Constraints {
218 fields: Some(vec![Field {
219 path: vec!["$.vc.type".to_string()],
220 ..Default::default()
221 }]),
222 ..Default::default()
223 }),
224 &credential
225 ));
226
227 assert!(evaluate_input(
229 &input_descriptor(Constraints {
230 fields: Some(vec![Field {
231 path: vec!["$.vc.foo".to_string(), "$.vc.type".to_string()],
232 ..Default::default()
233 }]),
234 ..Default::default()
235 }),
236 &credential
237 ));
238
239 assert!(evaluate_input(
241 &input_descriptor(Constraints {
242 fields: Some(vec![Field {
243 path: vec!["$.vc.type".to_string(), "$.vc.foo".to_string()],
244 filter: Some(serde_json::json!({
245 "type": "array",
246 "contains": {
247 "const": "IDCredential"
248 }
249 })),
250 ..Default::default()
251 }]),
252 ..Default::default()
253 }),
254 &credential
255 ));
256
257 assert!(!evaluate_input(
259 &input_descriptor(Constraints {
260 fields: Some(vec![Field {
261 path: vec!["$.vc.type".to_string()],
262 filter: Some(serde_json::json!({
263 "type": "array",
264 "contains": {
265 "const": "Foo"
266 }
267 })),
268 ..Default::default()
269 }]),
270 ..Default::default()
271 }),
272 &credential
273 ));
274
275 assert!(evaluate_input(
277 &input_descriptor(Constraints {
278 fields: Some(vec![Field {
279 path: vec!["$.vc.type".to_string()],
280 filter: Some(serde_json::json!({
281 "type": "array",
282 "contains": {
283 "const": "Foo"
284 }
285 })),
286 optional: Some(true),
287 ..Default::default()
288 }]),
289 ..Default::default()
290 }),
291 &credential
292 ));
293
294 assert!(evaluate_input(
296 &input_descriptor(Constraints {
297 fields: Some(vec![Field {
298 path: vec!["$.vc.foo".to_string()],
299 optional: Some(true),
300 ..Default::default()
301 }]),
302 ..Default::default()
303 }),
304 &credential
305 ));
306
307 assert!(!evaluate_input(
309 &input_descriptor(Constraints {
310 fields: Some(vec![Field {
311 path: vec!["$.vc.foo".to_string()],
312 optional: Some(false),
313 ..Default::default()
314 }]),
315 ..Default::default()
316 }),
317 &credential
318 ));
319 }
320}