Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

eidetica/auth/types/
delegation.rs

1//! Delegation system types for authentication
2//!
3//! This module defines types related to tree delegation and reference management.
4
5use serde::{Deserialize, Serialize};
6
7use super::permissions::PermissionBounds;
8use crate::crdt::{CRDTError, Doc, doc::Value};
9use crate::entry::ID;
10
11/// Reference to a Merkle-DAG tree (for delegated trees)
12/// TODO: May standardize on this format across the codebase instead of just for delegation
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
14pub struct TreeReference {
15    /// Root entry ID of the referenced tree
16    pub root: ID,
17    /// Current tip entry IDs of the referenced tree
18    pub tips: Vec<ID>,
19}
20
21/// Delegated tree reference stored in main tree's _settings.auth
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
23pub struct DelegatedTreeRef {
24    /// Permission bounds for keys from this delegated tree
25    #[serde(rename = "permission-bounds")]
26    pub permission_bounds: PermissionBounds,
27    /// Reference to the delegated tree
28    pub tree: TreeReference,
29}
30
31// ==================== Doc Conversions ====================
32
33impl From<TreeReference> for Value {
34    fn from(tree_ref: TreeReference) -> Value {
35        Value::Doc(Doc::from(tree_ref))
36    }
37}
38
39impl From<TreeReference> for Doc {
40    fn from(tree_ref: TreeReference) -> Doc {
41        let mut doc = Doc::new();
42        doc.set("root", tree_ref.root.to_string());
43        let mut tips_doc = Doc::new();
44        for (i, tip) in tree_ref.tips.iter().enumerate() {
45            tips_doc.set(i.to_string(), tip.to_string());
46        }
47        doc.set("tips", tips_doc);
48        doc
49    }
50}
51
52impl TryFrom<&Doc> for TreeReference {
53    type Error = crate::Error;
54
55    fn try_from(doc: &Doc) -> crate::Result<Self> {
56        let root_str = doc
57            .get_as::<&str>("root")
58            .ok_or_else(|| CRDTError::ElementNotFound {
59                key: "root".to_string(),
60            })?;
61        let root = ID::parse(root_str)?;
62
63        let mut tips = Vec::new();
64        if let Some(Value::Doc(tips_doc)) = doc.get("tips") {
65            let mut entries: Vec<(usize, &str)> = tips_doc
66                .iter()
67                .filter_map(|(k, v)| {
68                    let idx: usize = k.parse().ok()?;
69                    let s = v.as_text()?;
70                    Some((idx, s))
71                })
72                .collect();
73            entries.sort_by_key(|(idx, _)| *idx);
74            tips = entries
75                .into_iter()
76                .map(|(_, s)| ID::parse(s))
77                .collect::<crate::Result<Vec<_>>>()?;
78        }
79
80        Ok(TreeReference { root, tips })
81    }
82}
83
84impl From<DelegatedTreeRef> for Value {
85    fn from(dtref: DelegatedTreeRef) -> Value {
86        Value::Doc(Doc::from(dtref))
87    }
88}
89
90impl From<DelegatedTreeRef> for Doc {
91    fn from(dtref: DelegatedTreeRef) -> Doc {
92        let mut doc = Doc::atomic();
93        doc.set("permission_bounds", dtref.permission_bounds);
94        doc.set("tree", dtref.tree);
95        doc
96    }
97}
98
99impl TryFrom<&Doc> for DelegatedTreeRef {
100    type Error = crate::Error;
101
102    fn try_from(doc: &Doc) -> crate::Result<Self> {
103        let pb_doc = match doc.get("permission_bounds") {
104            Some(Value::Doc(d)) => d,
105            _ => {
106                return Err(CRDTError::ElementNotFound {
107                    key: "permission_bounds".to_string(),
108                }
109                .into());
110            }
111        };
112        let permission_bounds = PermissionBounds::try_from(pb_doc)?;
113
114        let tree_doc = match doc.get("tree") {
115            Some(Value::Doc(d)) => d,
116            _ => {
117                return Err(CRDTError::ElementNotFound {
118                    key: "tree".to_string(),
119                }
120                .into());
121            }
122        };
123        let tree = TreeReference::try_from(tree_doc)?;
124
125        Ok(DelegatedTreeRef {
126            permission_bounds,
127            tree,
128        })
129    }
130}