1use async_trait::async_trait;
3use ipfs_api_backend_hyper::IpfsClient;
4use serde_json::Value;
5use ssi::did::{RelativeDIDURL, ServiceEndpoint, VerificationMethod, VerificationMethodMap};
6use ssi::did_resolve::DocumentMetadata;
7use ssi::one_or_many::OneOrMany;
8use ssi::{
9 did::Document,
10 did_resolve::{DIDResolver, ResolutionInputMetadata, ResolutionMetadata},
11};
12use std::collections::HashSet;
13use std::marker::PhantomData;
14use std::str::FromStr;
15use trustchain_core::resolver::{ResolverError, TrustchainResolver};
16
17use crate::utils::{decode_ipfs_content, query_ipfs};
18use crate::{FullClient, LightClient};
19use crate::{CONTROLLER_KEY, SERVICE_TYPE_IPFS_KEY};
20
21pub struct HTTPTrustchainResolver<T: DIDResolver + Sync + Send, U = FullClient> {
24 pub wrapped_resolver: T,
25 pub ipfs_client: Option<IpfsClient>,
26 _marker: PhantomData<U>,
27}
28
29impl<T: DIDResolver + Sync + Send> HTTPTrustchainResolver<T, FullClient> {
30 pub fn new(resolver: T) -> Self {
32 Self {
33 wrapped_resolver: resolver,
34 ipfs_client: Some(IpfsClient::default()),
35 _marker: PhantomData,
36 }
37 }
38 fn ipfs_client(&self) -> &IpfsClient {
39 self.ipfs_client.as_ref().unwrap()
40 }
41}
42
43impl<T: DIDResolver + Sync + Send> HTTPTrustchainResolver<T, LightClient> {
44 pub fn new(resolver: T) -> Self {
46 Self {
47 wrapped_resolver: resolver,
48 ipfs_client: None,
49 _marker: PhantomData,
50 }
51 }
52}
53
54#[async_trait]
55impl<T> DIDResolver for HTTPTrustchainResolver<T, FullClient>
56where
57 T: DIDResolver + Sync + Send,
58{
59 async fn resolve(
60 &self,
61 did: &str,
62 input_metadata: &ResolutionInputMetadata,
63 ) -> (
64 ResolutionMetadata,
65 Option<Document>,
66 Option<DocumentMetadata>,
67 ) {
68 self.trustchain_resolve(did, input_metadata).await
69 }
70}
71
72#[async_trait]
73impl<T> DIDResolver for HTTPTrustchainResolver<T, LightClient>
74where
75 T: DIDResolver + Sync + Send,
76{
77 async fn resolve(
78 &self,
79 did: &str,
80 input_metadata: &ResolutionInputMetadata,
81 ) -> (
82 ResolutionMetadata,
83 Option<Document>,
84 Option<DocumentMetadata>,
85 ) {
86 self.trustchain_resolve(did, input_metadata).await
87 }
88}
89
90#[async_trait]
91impl<T> TrustchainResolver for HTTPTrustchainResolver<T, FullClient>
92where
93 T: DIDResolver + Sync + Send,
94{
95 fn wrapped_resolver(&self) -> &dyn DIDResolver {
96 &self.wrapped_resolver
97 }
98
99 async fn extended_transform(
100 &self,
101 (res_meta, doc, doc_meta): (
102 ResolutionMetadata,
103 Option<Document>,
104 Option<DocumentMetadata>,
105 ),
106 ) -> (
107 ResolutionMetadata,
108 Option<Document>,
109 Option<DocumentMetadata>,
110 ) {
111 if let (Some(did_doc), Some(did_doc_meta)) = (doc, doc_meta) {
113 let tc_result =
115 transform_as_result(res_meta, did_doc, did_doc_meta, self.ipfs_client()).await;
116 match tc_result {
117 Ok((tc_res_meta, tc_doc, tc_doc_meta)) => {
119 (tc_res_meta, Some(tc_doc), Some(tc_doc_meta))
120 }
121 Err(err) => {
123 let res_meta = ResolutionMetadata {
124 error: Some(err.to_string()),
125 content_type: None,
126 property_set: None,
127 };
128 (res_meta, None, None)
129 }
130 }
131 } else {
132 (res_meta, None, None)
134 }
135 }
136}
137
138#[async_trait]
139impl<T> TrustchainResolver for HTTPTrustchainResolver<T, LightClient>
140where
141 T: DIDResolver + Sync + Send,
142{
143 fn wrapped_resolver(&self) -> &dyn DIDResolver {
144 &self.wrapped_resolver
145 }
146}
147
148async fn transform_as_result(
150 res_meta: ResolutionMetadata,
151 doc: Document,
152 doc_meta: DocumentMetadata,
153 ipfs_client: &IpfsClient,
154) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> {
155 Ok((res_meta, transform_doc(&doc, ipfs_client).await?, doc_meta))
156}
157
158async fn transform_doc(
159 doc: &Document,
160 ipfs_client: &IpfsClient,
161) -> Result<Document, ResolverError> {
162 let mut doc_clone = doc.clone();
164
165 let endpoints = ipfs_key_endpoints(doc);
166 if endpoints.is_empty() {
167 return Ok(doc_clone);
168 }
169
170 let mut verification_methods = match &doc.verification_method {
172 Some(x) => x.clone(),
173 None => vec![],
174 };
175
176 let verification_methods_ids: HashSet<String> = verification_methods
178 .iter()
179 .map(|vm| vm.get_id(&doc.id))
180 .collect();
181
182 for endpoint in endpoints {
184 let ipfs_file = query_ipfs(endpoint.as_str(), ipfs_client)
186 .await
187 .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?;
188
189 let mut json = decode_ipfs_content(&ipfs_file, false)
190 .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?;
191
192 json.as_object_mut()
195 .ok_or(ResolverError::FailedToConvertToTrustchain(String::from(
196 "Unsupported document verification_method, use Vec<VerificationMethod::VerificationMethodMap>.",
197 )))?
198 .insert(
199 CONTROLLER_KEY.to_owned(),
200 serde_json::Value::String(doc.id.to_owned()),
201 );
202
203 let mut new_vm_map: VerificationMethodMap = serde_json::from_str(&json.to_string())
205 .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?;
206
207 if !new_vm_map.id.starts_with('#') {
209 new_vm_map.id.insert(0, '#');
210 }
211 let relative_did_url: &str = new_vm_map.id.as_ref();
213 let relative_did_url_vm = VerificationMethod::RelativeDIDURL(
214 RelativeDIDURL::from_str(relative_did_url)
215 .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?,
216 );
217
218 if verification_methods_ids.contains(&relative_did_url_vm.get_id(&doc.id)) {
220 continue;
221 }
222
223 if let Some(extra_properties) = new_vm_map.property_set.as_mut() {
225 if let Some(purposes) = extra_properties.remove("purposes") {
226 let purposes_vec = purposes
227 .as_array()
228 .ok_or(ResolverError::FailedToConvertToTrustchain(String::from(
229 "Expected public key 'purposes' to be a JSON Array.",
230 )))?
231 .to_vec();
232
233 if purposes_vec.contains(&Value::String("authentication".to_string())) {
236 if let Some(authentication) = &doc.authentication {
237 let mut new_authentication = authentication.to_owned();
238 new_authentication.push(relative_did_url_vm.clone());
239 doc_clone.authentication = Some(new_authentication);
240 } else {
241 doc_clone.authentication = Some(vec![relative_did_url_vm.clone()])
242 }
243 }
244 if purposes_vec.contains(&Value::String("assertionMethod".to_string())) {
245 if let Some(assertion_method) = &doc.assertion_method {
246 let mut new_assertion_method = assertion_method.to_owned();
247 new_assertion_method.push(relative_did_url_vm.clone());
248 doc_clone.assertion_method = Some(new_assertion_method);
249 } else {
250 doc_clone.assertion_method = Some(vec![relative_did_url_vm.clone()])
251 }
252 }
253 if purposes_vec.contains(&Value::String("keyAgreement".to_string())) {
254 if let Some(key_agreement) = &doc.key_agreement {
255 let mut new_key_agreement = key_agreement.to_owned();
256 new_key_agreement.push(relative_did_url_vm.clone());
257 doc_clone.key_agreement = Some(new_key_agreement);
258 } else {
259 doc_clone.key_agreement = Some(vec![relative_did_url_vm.clone()])
260 }
261 }
262 if purposes_vec.contains(&Value::String("capabilityInvocation".to_string())) {
263 if let Some(capability_invocation) = &doc.capability_invocation {
264 let mut new_capability_invocation = capability_invocation.to_owned();
265 new_capability_invocation.push(relative_did_url_vm.clone());
266 doc_clone.capability_invocation = Some(new_capability_invocation);
267 } else {
268 doc_clone.capability_invocation = Some(vec![relative_did_url_vm.clone()])
269 }
270 }
271 if purposes_vec.contains(&Value::String("capabilityDelegation".to_string())) {
272 if let Some(capability_delegation) = &doc.capability_delegation {
273 let mut new_capability_delegation = capability_delegation.to_owned();
274 new_capability_delegation.push(relative_did_url_vm.clone());
275 doc_clone.capability_delegation = Some(new_capability_delegation);
276 } else {
277 doc_clone.capability_delegation = Some(vec![relative_did_url_vm.clone()])
278 }
279 }
280 }
281 }
282
283 verification_methods.push(VerificationMethod::Map(new_vm_map));
284 }
285
286 doc_clone.verification_method = Some(verification_methods.to_owned());
288 Ok(doc_clone)
289}
290
291fn ipfs_key_endpoints(doc: &Document) -> Vec<String> {
292 let services = &doc.service;
293 if services.is_none() {
294 return vec![];
295 }
296 services
297 .as_ref()
298 .unwrap()
299 .iter()
300 .filter(|s| s.type_.to_single().is_some())
301 .filter_map(|s| {
302 if s.type_.to_single().unwrap().eq(SERVICE_TYPE_IPFS_KEY) {
303 match s.service_endpoint {
304 Some(OneOrMany::One(ServiceEndpoint::URI(ref uri))) => Some(uri.to_owned()),
305 _ => None,
306 }
307 } else {
308 None
309 }
310 })
311 .collect()
312}
313
314#[cfg(test)]
315mod tests {
316 use crate::data::{TEST_DOCUMENT_IPFS_KEY, TEST_IPFS_KEY_VM_JSON};
317
318 use super::*;
319
320 #[test]
321 fn test_ipfs_key_endpoints() {
322 let doc: Document = serde_json::from_str(TEST_DOCUMENT_IPFS_KEY).unwrap();
323 let result = ipfs_key_endpoints(&doc);
324
325 assert_eq!(
326 vec!("QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD"),
327 result
328 );
329 }
330
331 #[tokio::test]
332 #[ignore = "Integration test requires IPFS"]
333 async fn test_transform_doc() {
334 let doc: Document = serde_json::from_str(TEST_DOCUMENT_IPFS_KEY).unwrap();
335 let ipfs_client = IpfsClient::default();
336 let result = transform_doc(&doc, &ipfs_client).await.unwrap();
337
338 assert!(result.verification_method.unwrap().into_iter().any(|vm| {
340 match vm {
341 VerificationMethod::Map(map) => {
342 map.id.eq("#YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4")
343 }
344 _ => false,
345 }
346 }));
347 }
348
349 #[test]
350 fn test_verification_method_deserialisation() {
351 let mut json: serde_json::Value = serde_json::from_str(TEST_IPFS_KEY_VM_JSON).unwrap();
352
353 json.as_object_mut()
354 .ok_or(ResolverError::FailedToConvertToTrustchain(String::from(
355 "Verification Method Map missing keys.",
356 )))
357 .unwrap()
358 .insert(
359 CONTROLLER_KEY.to_owned(),
360 serde_json::Value::String("did:ion:abc".to_owned()),
361 );
362
363 let _new_verification_method: ssi::did::VerificationMethod =
364 serde_json::from_str(&json.to_string()).unwrap();
365 }
366}