common/bucket/
manifest.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6use crate::crypto::{PublicKey, Secret, Share, ShareError};
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 BucketShare {
14    principal: Principal,
15    share: Share,
16}
17
18impl BucketShare {
19    pub fn new(share: Share, 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) -> &Share {
34        &self.share
35    }
36}
37
38pub type Shares = BTreeMap<String, BucketShare>;
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    // specify the software version as a sanity check
74    version: Version,
75}
76
77impl BlockEncoded<DagCborCodec> for Manifest {}
78
79impl Manifest {
80    /// Create a new bucket with a name, owner, and share, and entry node link
81    pub fn new(
82        id: Uuid,
83        name: String,
84        owner: PublicKey,
85        share: Share,
86        entry: Link,
87        pins: Link,
88    ) -> Self {
89        Manifest {
90            id,
91            name,
92            shares: BTreeMap::from([(
93                owner.to_hex(),
94                BucketShare {
95                    principal: Principal {
96                        role: PrincipalRole::Owner,
97                        identity: owner,
98                    },
99                    share,
100                },
101            )]),
102            entry,
103            pins,
104            previous: None,
105            version: Version::default(),
106        }
107    }
108
109    pub fn get_share(&self, public_key: &PublicKey) -> Option<&BucketShare> {
110        self.shares.get(&public_key.to_hex())
111    }
112
113    pub fn add_share(&mut self, public_key: PublicKey, secret: Secret) -> Result<(), ShareError> {
114        let share = Share::new(&secret, &public_key)?;
115        let bucket_share = BucketShare::new(share, public_key);
116        self.shares.insert(public_key.to_hex(), bucket_share);
117        Ok(())
118    }
119
120    pub fn unset_shares(&mut self) {
121        self.shares.clear();
122    }
123
124    pub fn id(&self) -> &Uuid {
125        &self.id
126    }
127
128    pub fn name(&self) -> &str {
129        &self.name
130    }
131
132    pub fn shares(&self) -> &BTreeMap<String, BucketShare> {
133        &self.shares
134    }
135
136    pub fn version(&self) -> &Version {
137        &self.version
138    }
139
140    pub fn entry(&self) -> &Link {
141        &self.entry
142    }
143
144    pub fn set_entry(&mut self, entry: Link) {
145        self.entry = entry;
146    }
147
148    pub fn pins(&self) -> &Link {
149        &self.pins
150    }
151
152    pub fn set_pins(&mut self, pins_link: Link) {
153        self.pins = pins_link;
154    }
155
156    pub fn set_previous(&mut self, previous: Link) {
157        self.previous = Some(previous);
158    }
159
160    pub fn previous(&self) -> &Option<Link> {
161        &self.previous
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    #[allow(unused_imports)]
169    use crate::crypto::{PublicKey, Secret};
170
171    #[test]
172    fn test_share_serialize() {
173        use ipld_core::codec::Codec;
174        use serde_ipld_dagcbor::codec::DagCborCodec;
175
176        let share = Share::default();
177
178        // Try to encode/decode just the Share
179        let encoded = DagCborCodec::encode_to_vec(&share).unwrap();
180        let decoded: Share = DagCborCodec::decode_from_slice(&encoded).unwrap();
181
182        assert_eq!(share, decoded);
183    }
184
185    #[test]
186    fn test_principal_serialize() {
187        use ipld_core::codec::Codec;
188        use serde_ipld_dagcbor::codec::DagCborCodec;
189
190        let public_key = crate::crypto::SecretKey::generate().public();
191        let principal = Principal {
192            role: PrincipalRole::Owner,
193            identity: public_key,
194        };
195
196        // Try to encode/decode just the Principal
197        let encoded = DagCborCodec::encode_to_vec(&principal).unwrap();
198        let decoded: Principal = DagCborCodec::decode_from_slice(&encoded).unwrap();
199
200        assert_eq!(principal, decoded);
201    }
202}