did_toolkit/
registry.rs

1use crate::{
2    did::DID,
3    document::{Document, VerificationMethod},
4    url::URL,
5};
6use anyhow::anyhow;
7use either::Either;
8use std::{
9    collections::BTreeMap,
10    ops::{Index, IndexMut},
11    path::PathBuf,
12};
13use url::Url;
14
15/// Registry is a basic, in-memory [Document] registry that is able to load documents directly as well as
16/// cross-reference them in some ways. It can also optionally fetch remote documents and cache them
17/// as a part of its implementation. Documents can be loaded via the JSON or CBOR formats. JSON
18/// loading is provided by [serde_json] and CBOR is provided by [ciborium].
19///
20/// [Document] validity checks (via [Document::valid]) are not performed at loading time. [DID]
21/// keying is automatically performed based on the [Document] `id` property.
22///
23/// Accessing the registry is provided by a few methods in the implementation, but can also be
24/// indexed by [DID] reference or [usize]. Iterators are provided as ordered pairs via
25/// [Registry::iter]. The underlying storage is a [BTreeMap] and awareness of the performance
26/// characteristics of this implementation may be important for larger registries.
27///
28/// There are examples in the apporpriate part of this crate which go into loading documents from
29/// disk.
30///
31/// ```
32/// use did_toolkit::prelude::*;
33/// use either::Either;
34///
35/// let mut reg = Registry::default();
36/// let did = DID::parse("did:mymethod:alice").unwrap();
37/// let did2 = DID::parse("did:mymethod:bob").unwrap();
38/// let doc = Document{
39///   id: did.clone(),
40///   controller: Some(Controller(Either::Left(did2.clone()))),
41///   ..Default::default()
42/// };
43///
44/// reg.insert(doc.clone());
45///
46/// reg.insert(Document{
47///   id: did2.clone(),
48///   ..Default::default()
49/// });
50///
51/// assert!(reg.controls(&did, &did2).unwrap());
52/// assert_eq!(reg[0], doc);
53/// assert_eq!(reg[&did], doc);
54/// ```
55///
56#[derive(Default)]
57pub struct Registry {
58    r: BTreeMap<DID, Document>,
59    remote_cache: bool,
60}
61
62impl<'a> Index<&'a DID> for Registry {
63    type Output = Document;
64
65    fn index(&self, index: &'a DID) -> &Self::Output {
66        self.r.index(index)
67    }
68}
69
70impl<'a> IndexMut<&'a DID> for Registry {
71    fn index_mut(&mut self, index: &'a DID) -> &mut Document {
72        self.r.get_mut(index).unwrap()
73    }
74}
75
76impl Index<usize> for Registry {
77    type Output = Document;
78
79    fn index(&self, index: usize) -> &Self::Output {
80        self.r
81            .iter()
82            .nth(index)
83            .expect("invalid index dereferencing document in registry")
84            .1
85    }
86}
87
88impl IndexMut<usize> for Registry {
89    fn index_mut(&mut self, index: usize) -> &mut Document {
90        self.r
91            .iter_mut()
92            .nth(index)
93            .expect("invalid index dereferencing document in registry")
94            .1
95    }
96}
97
98impl Registry {
99    /// Create a [Registry] with the remote cache enabled. Use [Registry::default] for one that
100    /// does not use the remote cache.
101    pub fn new_with_remote_cache() -> Self {
102        Self {
103            r: BTreeMap::new(),
104            remote_cache: true,
105        }
106    }
107
108    /// Load a document from the filesystem as JSON.
109    pub fn load_document(&mut self, filename: PathBuf) -> Result<(), anyhow::Error> {
110        let mut file = std::fs::OpenOptions::new();
111        file.read(true);
112        let io = file.open(filename)?;
113        let doc: Document = serde_json::from_reader(io)?;
114        self.insert(doc)
115    }
116
117    /// Load a document from the filesystem as CBOR.
118    pub fn load_document_cbor(&mut self, filename: PathBuf) -> Result<(), anyhow::Error> {
119        let mut file = std::fs::OpenOptions::new();
120        file.read(true);
121        let io = file.open(filename)?;
122        let doc: Document = ciborium::de::from_reader(io)?;
123        self.insert(doc)
124    }
125
126    /// Get an iterator into the ordered pairs of the registry.
127    pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a DID, &'a Document)> + 'a {
128        self.r.iter()
129    }
130
131    /// Compute the size of the registry.
132    pub fn len(&self) -> usize {
133        self.r.len()
134    }
135
136    /// Insert a document into the registry. The registry will automatically be keyed by the
137    /// [Document]'s `id` property. Will fail if the document already exists.
138    pub fn insert(&mut self, doc: Document) -> Result<(), anyhow::Error> {
139        if self.r.contains_key(&doc.id) {
140            return Err(anyhow!("DID {} already exists in registry", doc.id));
141        }
142
143        self.r.insert(doc.id.clone(), doc);
144        Ok(())
145    }
146
147    /// Remove a document by [DID].
148    pub fn remove(&mut self, did: &DID) -> Option<Document> {
149        self.r.remove(did)
150    }
151
152    /// Retreive a document by [DID].
153    pub fn get(&self, did: &DID) -> Option<Document> {
154        self.r.get(did).cloned()
155    }
156
157    /// Retrieve a document by DID [URL].
158    pub fn follow(&self, url: URL) -> Option<Document> {
159        self.get(&url.to_did())
160    }
161
162    /// Looks up a [VerificationMethod] by [URL] for the [DID]. There must be a
163    /// [VerificationMethod] in the [DID]'s document, otherwise this will return [None].
164    pub fn verification_method_for_url(&self, did: &DID, url: URL) -> Option<VerificationMethod> {
165        if let Some(doc) = self.get(did) {
166            if let Some(vm) = doc.verification_method {
167                for method in vm {
168                    if url == method.id {
169                        return Some(method);
170                    }
171                }
172            }
173        }
174
175        None
176    }
177
178    /// For a given [DID], determine if another [DID] is designated as a controller. Follows the
179    /// rules specified in <https://www.w3.org/TR/did-core/#did-controller>. Will fail if either
180    /// [DID] is missing from the registry.
181    pub fn controls(&self, did: &DID, controller: &DID) -> Result<bool, anyhow::Error> {
182        if let Some(did_doc) = self.get(did) {
183            if did == controller {
184                return Ok(true);
185            }
186
187            if self.get(controller).is_some() {
188                if did_doc.controller.is_some() {
189                    match did_doc.controller.unwrap().0 {
190                        Either::Left(did) => return Ok(&did == controller),
191                        Either::Right(did_list) => {
192                            for did in did_list {
193                                if &did == controller {
194                                    return Ok(true);
195                                }
196                            }
197                        }
198                    }
199                } else {
200                    return Ok(false);
201                }
202            } else {
203                return Err(anyhow!("DID {} did not exist in the registry", did));
204            }
205        } else {
206            return Err(anyhow!("DID {} did not exist in the registry", did));
207        }
208
209        Ok(false)
210    }
211
212    /// For two given [DID]s, determine if they can be treated the same according to the rules for
213    /// the `alsoKnownAs` property, which you can read here:
214    /// <https://www.w3.org/TR/did-core/#also-known-as>
215    ///
216    /// Both [DID]s must exist in the registry, otherwise an error will be returned.
217    pub fn equivalent_to_did(&mut self, did: &DID, other: &DID) -> Result<bool, anyhow::Error> {
218        // there is probably a better way to represent this stew with Iterator methods, but I
219        // cannot be fucked to deal with that right now.
220        if let Some(doc) = self.get(did) {
221            if let Some(other_doc) = self.get(other) {
222                if let Some(this_aka) = doc.also_known_as {
223                    for this_aka_each in this_aka.0 {
224                        match this_aka_each.0 {
225                            Either::Left(this_did) => {
226                                if self.compare_aka(did, &this_did, &other_doc)? {
227                                    return Ok(true);
228                                }
229                            }
230                            Either::Right(url) => {
231                                let this_doc = self.cache_document(url)?;
232                                if self.compare_aka(did, &this_doc.id, &other_doc)? {
233                                    return Ok(true);
234                                }
235                            }
236                        }
237                    }
238                } else {
239                    return Ok(false);
240                }
241            } else {
242                return Err(anyhow!("DID {} did not exist in the registry", other));
243            }
244        } else {
245            return Err(anyhow!("DID {} did not exist in the registry", did));
246        }
247
248        Ok(false)
249    }
250
251    fn compare_aka(
252        &mut self,
253        did: &DID,
254        this_did: &DID,
255        other_doc: &Document,
256    ) -> Result<bool, anyhow::Error> {
257        if let Some(other_aka) = &other_doc.also_known_as {
258            for other_aka_each in &other_aka.0 {
259                let other_did = &match &other_aka_each.0 {
260                    Either::Left(other_did) => other_did.clone(),
261                    Either::Right(url) => self.cache_document(url.clone())?.id,
262                };
263
264                if other_did == did && this_did == &other_doc.id {
265                    return Ok(true);
266                }
267            }
268        }
269
270        Ok(false)
271    }
272
273    fn cache_document(&mut self, url: Url) -> Result<Document, anyhow::Error> {
274        if self.remote_cache {
275            let doc = reqwest::blocking::get(url)?.json::<Document>()?;
276            self.insert(doc.clone())?;
277            Ok(doc)
278        } else {
279            Err(anyhow!("Remote caching of documents is disabled"))
280        }
281    }
282}
283
284mod tests {
285    #[test]
286    fn test_basic() {
287        use super::Registry;
288        use crate::{did::DID, document::Document};
289
290        let mut reg: Registry = Default::default();
291        let did = DID::parse("did:testing:u:alice").unwrap();
292        let doc = Document {
293            id: did.clone(),
294            ..Default::default()
295        };
296
297        let did2 = DID::parse("did:testing:u:bob").unwrap();
298        let doc2 = Document {
299            id: did2.clone(),
300            ..Default::default()
301        };
302
303        let did3 = DID::parse("did:testing:u:charlie").unwrap();
304
305        assert!(reg.insert(doc.clone()).is_ok());
306        assert!(reg.insert(doc.clone()).is_err());
307        assert_eq!(reg.get(&did), Some(doc));
308        assert!(reg.insert(doc2.clone()).is_ok());
309        assert_eq!(reg.get(&did2), Some(doc2));
310        assert!(reg.get(&did3).is_none());
311        assert!(reg.remove(&did).is_some());
312        assert!(reg.get(&did).is_none());
313    }
314
315    #[test]
316    fn test_follow() {
317        use super::Registry;
318        use crate::{did::DID, document::Document, url::URL};
319
320        let mut reg: Registry = Default::default();
321        let did = DID::parse("did:testing:u:alice").unwrap();
322        let doc = Document {
323            id: did.clone(),
324            ..Default::default()
325        };
326
327        let did2 = DID::parse("did:testing:u:bob").unwrap();
328        let doc2 = Document {
329            id: did2.clone(),
330            ..Default::default()
331        };
332
333        assert!(reg.insert(doc.clone()).is_ok());
334        assert!(reg.insert(doc2.clone()).is_ok());
335
336        let url = URL::parse("did:testing:u:alice/path#fragment").unwrap();
337        let url2 = URL::parse("did:testing:u:bob/path#fragment").unwrap();
338        let url3 = URL::parse("did:testing:u:charlie/path#fragment").unwrap();
339
340        assert_eq!(reg.follow(url).unwrap(), doc);
341        assert_eq!(reg.follow(url2).unwrap(), doc2);
342        assert!(reg.follow(url3).is_none());
343    }
344
345    #[test]
346    fn test_controls() {
347        use super::Registry;
348        use crate::{
349            did::DID,
350            document::{Controller, Document},
351        };
352        use either::Either;
353        use std::collections::BTreeSet;
354
355        let mut reg: Registry = Default::default();
356        let did = DID::parse("did:testing:u:alice").unwrap();
357        let did2 = DID::parse("did:testing:u:bob").unwrap();
358        let did3 = DID::parse("did:testing:u:charlie").unwrap();
359
360        let doc = Document {
361            id: did.clone(),
362            controller: Some(Controller(Either::Left(did2.clone()))),
363            ..Default::default()
364        };
365
366        let doc2 = Document {
367            id: did2.clone(),
368            ..Default::default()
369        };
370
371        assert!(reg.insert(doc.clone()).is_ok());
372        assert!(reg.insert(doc2.clone()).is_ok());
373        assert!(reg.controls(&did, &did2).is_ok());
374        assert!(reg.controls(&did2, &did3).is_err());
375        assert!(reg.controls(&did, &did2).unwrap());
376        assert!(!reg.controls(&did2, &did).unwrap());
377
378        assert!(reg.remove(&did).is_some());
379        assert!(reg.remove(&did2).is_some());
380
381        let mut set = BTreeSet::new();
382        set.insert(did2.clone());
383
384        let doc = Document {
385            id: did.clone(),
386            controller: Some(Controller(Either::Right(set))),
387            ..Default::default()
388        };
389
390        let doc2 = Document {
391            id: did2.clone(),
392            ..Default::default()
393        };
394
395        let doc3 = Document {
396            id: did3.clone(),
397            controller: Some(Controller(Either::Left(did2.clone()))),
398            ..Default::default()
399        };
400
401        assert!(reg.insert(doc.clone()).is_ok());
402        assert!(reg.insert(doc2.clone()).is_ok());
403        assert!(reg.insert(doc3.clone()).is_ok());
404        assert!(reg.controls(&did, &did2).is_ok());
405        assert!(reg.controls(&did, &did2).unwrap());
406        assert!(!reg.controls(&did2, &did).unwrap());
407        assert!(!reg.controls(&did, &did3).unwrap());
408    }
409
410    #[test]
411    fn test_equivalent_to_did() {
412        use super::Registry;
413        use crate::{
414            did::DID,
415            document::{AlsoKnownAs, AlsoKnownAsEither, Document},
416        };
417        use either::Either;
418        use std::collections::BTreeSet;
419
420        let mut reg: Registry = Default::default();
421        let did = DID::parse("did:testing:u:alice").unwrap();
422        let did2 = DID::parse("did:testing:u:bob").unwrap();
423        let did3 = DID::parse("did:testing:u:charlie").unwrap();
424
425        let mut set = BTreeSet::new();
426        set.insert(AlsoKnownAsEither(Either::Left(did2.clone())));
427
428        let mut set2 = BTreeSet::new();
429        set2.insert(AlsoKnownAsEither(Either::Left(did.clone())));
430
431        let doc = Document {
432            id: did.clone(),
433            also_known_as: Some(AlsoKnownAs(set.clone())),
434            ..Default::default()
435        };
436
437        let doc2 = Document {
438            id: did2.clone(),
439            also_known_as: Some(AlsoKnownAs(set2)),
440            ..Default::default()
441        };
442
443        assert!(reg.insert(doc.clone()).is_ok());
444        assert!(reg.insert(doc2.clone()).is_ok());
445        assert!(reg.equivalent_to_did(&did, &did3).is_err());
446        assert!(reg.equivalent_to_did(&did, &did2).unwrap());
447        assert!(reg.equivalent_to_did(&did2, &did).unwrap());
448
449        let doc3 = Document {
450            id: did3.clone(),
451            ..Default::default()
452        };
453
454        assert!(reg.insert(doc3.clone()).is_ok());
455        assert!(!reg.equivalent_to_did(&did2, &did3).unwrap());
456        assert!(!reg.equivalent_to_did(&did, &did3).unwrap());
457
458        assert!(reg.remove(&did).is_some());
459        assert!(reg.remove(&did2).is_some());
460
461        let doc = Document {
462            id: did.clone(),
463            also_known_as: Some(AlsoKnownAs(set)),
464            ..Default::default()
465        };
466
467        let doc2 = Document {
468            id: did2.clone(),
469            ..Default::default()
470        };
471
472        assert!(reg.insert(doc).is_ok());
473        assert!(reg.insert(doc2).is_ok());
474        assert!(!reg.equivalent_to_did(&did, &did2).unwrap());
475    }
476}