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

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.as_str());
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.as_str());
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::new(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.into_iter().map(|(_, s)| ID::new(s)).collect();
75        }
76
77        Ok(TreeReference { root, tips })
78    }
79}
80
81impl From<DelegatedTreeRef> for Value {
82    fn from(dtref: DelegatedTreeRef) -> Value {
83        Value::Doc(Doc::from(dtref))
84    }
85}
86
87impl From<DelegatedTreeRef> for Doc {
88    fn from(dtref: DelegatedTreeRef) -> Doc {
89        let mut doc = Doc::atomic();
90        doc.set("permission_bounds", dtref.permission_bounds);
91        doc.set("tree", dtref.tree);
92        doc
93    }
94}
95
96impl TryFrom<&Doc> for DelegatedTreeRef {
97    type Error = crate::Error;
98
99    fn try_from(doc: &Doc) -> crate::Result<Self> {
100        let pb_doc = match doc.get("permission_bounds") {
101            Some(Value::Doc(d)) => d,
102            _ => {
103                return Err(CRDTError::ElementNotFound {
104                    key: "permission_bounds".to_string(),
105                }
106                .into());
107            }
108        };
109        let permission_bounds = PermissionBounds::try_from(pb_doc)?;
110
111        let tree_doc = match doc.get("tree") {
112            Some(Value::Doc(d)) => d,
113            _ => {
114                return Err(CRDTError::ElementNotFound {
115                    key: "tree".to_string(),
116                }
117                .into());
118            }
119        };
120        let tree = TreeReference::try_from(tree_doc)?;
121
122        Ok(DelegatedTreeRef {
123            permission_bounds,
124            tree,
125        })
126    }
127}