common/mount/
manifest.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::crypto::{PublicKey, Secret, SecretShare, SecretShareError};
7use crate::linked_data::{BlockEncoded, DagCborCodec, Link};
8use crate::version::Version;
9
10use super::principal::{Principal, PrincipalRole};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct Share {
14    principal: Principal,
15    share: SecretShare,
16}
17
18impl Share {
19    pub fn new(share: SecretShare, public_key: PublicKey) -> Self {
20        Self {
21            principal: Principal {
22                role: PrincipalRole::Owner,
23                identity: public_key,
24            },
25            share,
26        }
27    }
28
29    pub fn principal(&self) -> &Principal {
30        &self.principal
31    }
32
33    pub fn share(&self) -> &SecretShare {
34        &self.share
35    }
36}
37
38pub type Shares = BTreeMap<String, Share>;
39
40/**
41* BucketData
42* ==========
43* BucketData is the serializable metadata for a bucket.
44* It stores:
45*   - an identifier for the bucket (global and static)
46*   - a friendly name for the bucket (for display)
47*   - shares (access control and encryption keys for principals)
48*   - pins (optional pin set)
49*   - previous version link
50*   - version info
51*/
52#[allow(clippy::doc_overindented_list_items)]
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
54pub struct Manifest {
55    // Buckets have a global unique identifier
56    //  that clients should respect
57    id: Uuid,
58    // They also have a friendly name,
59    // buckets are identified via unique pairs
60    //  of <name, pk>
61    name: String,
62    // the set of principals that have access to the bucket
63    //  and their roles
64    // Using String as key for CBOR compatibility
65    shares: Shares,
66    // entry into the bucket
67    entry: Link,
68    // a pointer to a HashSeq block describing the pin set
69    //  for the bucket
70    pins: Link,
71    // and a point to the previous version of the bucket
72    previous: Option<Link>,
73    // the height of this manifest in the bucket's version chain
74    height: u64,
75    // specify the software version as a sanity check
76    version: Version,
77}
78
79impl BlockEncoded<DagCborCodec> for Manifest {}
80
81impl Manifest {
82    /// Create a new bucket with a name, owner, and share, and entry node link
83    pub fn new(
84        id: Uuid,
85        name: String,
86        owner: PublicKey,
87        share: SecretShare,
88        entry: Link,
89        pins: Link,
90        height: u64,
91    ) -> Self {
92        Manifest {
93            id,
94            name,
95            shares: BTreeMap::from([(
96                owner.to_hex(),
97                Share {
98                    principal: Principal {
99                        role: PrincipalRole::Owner,
100                        identity: owner,
101                    },
102                    share,
103                },
104            )]),
105            entry,
106            pins,
107            previous: None,
108            height,
109            version: Version::default(),
110        }
111    }
112
113    pub fn get_share(&self, public_key: &PublicKey) -> Option<&Share> {
114        self.shares.get(&public_key.to_hex())
115    }
116
117    pub fn add_share(
118        &mut self,
119        public_key: PublicKey,
120        secret: Secret,
121    ) -> Result<(), SecretShareError> {
122        let share = SecretShare::new(&secret, &public_key)?;
123        let bucket_share = Share::new(share, public_key);
124        self.shares.insert(public_key.to_hex(), bucket_share);
125        Ok(())
126    }
127
128    pub fn unset_shares(&mut self) {
129        self.shares.clear();
130    }
131
132    pub fn id(&self) -> &Uuid {
133        &self.id
134    }
135
136    pub fn name(&self) -> &str {
137        &self.name
138    }
139
140    pub fn shares(&self) -> &BTreeMap<String, Share> {
141        &self.shares
142    }
143
144    pub fn version(&self) -> &Version {
145        &self.version
146    }
147
148    pub fn entry(&self) -> &Link {
149        &self.entry
150    }
151
152    pub fn set_entry(&mut self, entry: Link) {
153        self.entry = entry;
154    }
155
156    pub fn pins(&self) -> &Link {
157        &self.pins
158    }
159
160    pub fn set_pins(&mut self, pins_link: Link) {
161        self.pins = pins_link;
162    }
163
164    pub fn set_previous(&mut self, previous: Link) {
165        self.previous = Some(previous);
166    }
167
168    pub fn previous(&self) -> &Option<Link> {
169        &self.previous
170    }
171
172    pub fn height(&self) -> u64 {
173        self.height
174    }
175
176    pub fn set_height(&mut self, height: u64) {
177        self.height = height;
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    #[allow(unused_imports)]
185    use crate::crypto::{PublicKey, Secret};
186
187    #[test]
188    fn test_share_serialize() {
189        use ipld_core::codec::Codec;
190        use serde_ipld_dagcbor::codec::DagCborCodec;
191
192        let share = SecretShare::default();
193
194        // Try to encode/decode just the Share
195        let encoded = DagCborCodec::encode_to_vec(&share).unwrap();
196        let decoded: SecretShare = DagCborCodec::decode_from_slice(&encoded).unwrap();
197
198        assert_eq!(share, decoded);
199    }
200
201    #[test]
202    fn test_principal_serialize() {
203        use ipld_core::codec::Codec;
204        use serde_ipld_dagcbor::codec::DagCborCodec;
205
206        let public_key = crate::crypto::SecretKey::generate().public();
207        let principal = Principal {
208            role: PrincipalRole::Owner,
209            identity: public_key,
210        };
211
212        // Try to encode/decode just the Principal
213        let encoded = DagCborCodec::encode_to_vec(&principal).unwrap();
214        let decoded: Principal = DagCborCodec::decode_from_slice(&encoded).unwrap();
215
216        assert_eq!(principal, decoded);
217    }
218}