Skip to main content

lb_rs/model/
core_ops.rs

1use crate::model::access_info::{UserAccessInfo, UserAccessMode};
2use crate::model::api::METADATA_FEE;
3use crate::model::crypto::{AESKey, DecryptedDocument, EncryptedDocument};
4use crate::model::errors::{LbErrKind, LbResult};
5use crate::model::file::{File, Share, ShareMode};
6use crate::model::file_metadata::{FileType, Owner};
7use crate::model::lazy::LazyTree;
8use crate::model::meta::Meta;
9use crate::model::secret_filename::{HmacSha256, SecretFileName};
10use crate::model::staged::{StagedTree, StagedTreeLike};
11use crate::model::tree_like::{TreeLike, TreeLikeMut};
12use crate::model::{compression_service, symkey, validate};
13use crate::service::keychain::Keychain;
14use db_rs::LookupTable;
15use hmac::{Mac, NewMac};
16use libsecp256k1::PublicKey;
17use tracing::debug;
18use uuid::Uuid;
19
20use super::file_like::FileLike;
21use super::signed_meta::SignedMeta;
22
23pub type TreeWithOp<Staged> = LazyTree<StagedTree<Staged, Option<SignedMeta>>>;
24pub type TreeWithOps<Staged> = LazyTree<StagedTree<Staged, Vec<SignedMeta>>>;
25
26impl<T> LazyTree<T>
27where
28    T: TreeLike<F = SignedMeta>,
29{
30    /// convert FileMetadata into File. fields have been decrypted, public keys replaced with usernames, deleted files filtered out, etc.
31    pub fn decrypt(
32        &mut self, keychain: &Keychain, id: &Uuid, public_key_cache: &LookupTable<Owner, String>,
33    ) -> LbResult<File> {
34        let account = keychain.get_account()?;
35        let pk = keychain.get_pk()?;
36
37        let meta = self.find(id)?.clone();
38        let file_type = meta.file_type();
39        let last_modified = meta.timestamped_value.timestamp as u64;
40        let name = self.name_using_links(id, keychain)?;
41        let parent = self.parent_using_links(id)?;
42        let last_modified_by = public_key_cache
43            .get()
44            .get(&Owner(meta.public_key))
45            .cloned()
46            .unwrap_or_else(|| String::from("<unknown>"));
47
48        let owner = meta.owner();
49        let owner_username = public_key_cache
50            .get()
51            .get(&owner)
52            .cloned()
53            .unwrap_or_else(|| String::from("<unknown>"));
54
55        let id = *id;
56
57        let mut shares = Vec::new();
58        for user_access_key in meta.user_access_keys() {
59            if user_access_key.encrypted_by == user_access_key.encrypted_for {
60                continue;
61            }
62            let mode = match user_access_key.mode {
63                UserAccessMode::Read => ShareMode::Read,
64                UserAccessMode::Write => ShareMode::Write,
65                UserAccessMode::Owner => continue,
66            };
67            shares.push(Share {
68                mode,
69                shared_by: if user_access_key.encrypted_by == pk {
70                    account.username.clone()
71                } else {
72                    public_key_cache
73                        .get()
74                        .get(&Owner(user_access_key.encrypted_by))
75                        .cloned()
76                        .unwrap_or_else(|| String::from("<unknown>"))
77                },
78                shared_with: if user_access_key.encrypted_for == pk {
79                    account.username.clone()
80                } else {
81                    public_key_cache
82                        .get()
83                        .get(&Owner(user_access_key.encrypted_for))
84                        .cloned()
85                        .unwrap_or_else(|| String::from("<unknown>"))
86                },
87            });
88        }
89
90        Ok(File {
91            id,
92            parent,
93            name,
94            file_type,
95            last_modified,
96            last_modified_by,
97            owner: owner_username,
98            shares,
99            size_bytes: meta.timestamped_value.value.doc_size().unwrap_or_default() as u64
100                + METADATA_FEE,
101        })
102    }
103
104    /// convert FileMetadata into File. fields have been decrypted, public keys replaced with usernames, deleted files filtered out, etc.
105    pub fn decrypt_all<I>(
106        &mut self, keychain: &Keychain, ids: I, public_key_cache: &LookupTable<Owner, String>,
107        skip_invisible: bool,
108    ) -> LbResult<Vec<File>>
109    where
110        I: Iterator<Item = Uuid>,
111    {
112        let mut files: Vec<File> = Vec::new();
113
114        for id in ids {
115            if skip_invisible && self.is_invisible_id(id)? {
116                continue;
117            }
118
119            let finalized = self.decrypt(keychain, &id, public_key_cache)?;
120            files.push(finalized);
121        }
122
123        Ok(files)
124    }
125
126    pub fn is_invisible_id(&mut self, id: Uuid) -> LbResult<bool> {
127        Ok(self.find(&id)?.is_link()
128            || self.calculate_deleted(&id)?
129            || self.in_pending_share(&id)?)
130    }
131
132    pub fn create_op(
133        &mut self, id: Uuid, key: AESKey, parent: &Uuid, name: &str, file_type: FileType,
134        keychain: &Keychain,
135    ) -> LbResult<(SignedMeta, Uuid)> {
136        validate::file_name(name)?;
137
138        if self.maybe_find(parent).is_none() {
139            return Err(LbErrKind::FileParentNonexistent.into());
140        }
141        let parent_owner = self.find(parent)?.owner().0;
142        let parent_key = self.decrypt_key(parent, keychain)?;
143        let file = Meta::create(id, key, &parent_owner, *parent, &parent_key, name, file_type)?
144            .sign(keychain)?;
145        let id = *file.id();
146
147        debug!("new {:?} with id: {}", file_type, id);
148        Ok((file, id))
149    }
150
151    pub fn rename_op(
152        &mut self, id: &Uuid, name: &str, keychain: &Keychain,
153    ) -> LbResult<SignedMeta> {
154        let mut file = self.find(id)?.timestamped_value.value.clone();
155
156        validate::file_name(name)?;
157        if self.maybe_find(file.parent()).is_none() {
158            return Err(LbErrKind::InsufficientPermission.into());
159        }
160        let parent_key = self.decrypt_key(file.parent(), keychain)?;
161        let key = self.decrypt_key(id, keychain)?;
162        file.set_name(SecretFileName::from_str(name, &key, &parent_key)?);
163        let file = file.sign(keychain)?;
164
165        Ok(file)
166    }
167
168    pub fn move_op(
169        &mut self, id: &Uuid, new_parent: &Uuid, keychain: &Keychain,
170    ) -> LbResult<Vec<SignedMeta>> {
171        let mut file = self.find(id)?.timestamped_value.value.clone();
172        if self.maybe_find(new_parent).is_none() {
173            return Err(LbErrKind::FileParentNonexistent.into());
174        }
175        let key = self.decrypt_key(id, keychain)?;
176        let parent_key = self.decrypt_key(new_parent, keychain)?;
177        let owner = self.find(new_parent)?.owner();
178        file.set_owner(owner);
179        file.set_parent(*new_parent);
180        file.set_folder_access_keys(symkey::encrypt(&parent_key, &key)?);
181        file.set_name(SecretFileName::from_str(&self.name(id, keychain)?, &key, &parent_key)?);
182        let file = file.sign(keychain)?;
183
184        let mut result = vec![file];
185        for id in self.descendants(id)? {
186            if self.calculate_deleted(&id)? {
187                continue;
188            }
189            let mut descendant = self.find(&id)?.timestamped_value.value.clone();
190            descendant.set_owner(owner);
191            result.push(descendant.sign(keychain)?);
192        }
193
194        Ok(result)
195    }
196
197    pub fn delete_op(&self, id: &Uuid, keychain: &Keychain) -> LbResult<SignedMeta> {
198        let mut file = self.find(id)?.timestamped_value.value.clone();
199
200        file.set_deleted(true);
201        let file = file.sign(keychain)?;
202
203        Ok(file)
204    }
205
206    pub fn add_share_op(
207        &mut self, id: Uuid, sharee: Owner, mode: ShareMode, keychain: &Keychain,
208    ) -> LbResult<SignedMeta> {
209        let owner = Owner(keychain.get_pk()?);
210        let access_mode = match mode {
211            ShareMode::Write => UserAccessMode::Write,
212            ShareMode::Read => UserAccessMode::Read,
213        };
214        if self.calculate_deleted(&id)? {
215            return Err(LbErrKind::FileNonexistent.into());
216        }
217        let id =
218            if let FileType::Link { target } = self.find(&id)?.file_type() { target } else { id };
219        let mut file = self.find(&id)?.timestamped_value.value.clone();
220        validate::not_root(&file)?;
221        if mode == ShareMode::Write && file.owner().0 != owner.0 {
222            return Err(LbErrKind::InsufficientPermission.into());
223        }
224        // check for and remove duplicate shares
225        let mut found = false;
226        for user_access in file.user_access_keys() {
227            if user_access.encrypted_for == sharee.0 {
228                found = true;
229                if user_access.mode == access_mode && !user_access.deleted {
230                    return Err(LbErrKind::ShareAlreadyExists.into());
231                }
232            }
233        }
234        if found {
235            file.user_access_keys_mut()
236                .retain(|k| k.encrypted_for != sharee.0);
237        }
238        file.user_access_keys_mut().push(UserAccessInfo::encrypt(
239            keychain.get_account()?,
240            &owner.0,
241            &sharee.0,
242            &self.decrypt_key(&id, keychain)?,
243            access_mode,
244        )?);
245        let file = file.sign(keychain)?;
246
247        Ok(file)
248    }
249
250    pub fn delete_share_op(
251        &mut self, id: &Uuid, maybe_encrypted_for: Option<PublicKey>, keychain: &Keychain,
252    ) -> LbResult<Vec<SignedMeta>> {
253        let mut result = Vec::new();
254        let mut file = self.find(id)?.timestamped_value.value.clone();
255
256        let mut found = false;
257        for key in file.user_access_keys_mut() {
258            if let Some(encrypted_for) = maybe_encrypted_for {
259                if !key.deleted && key.encrypted_for == encrypted_for {
260                    found = true;
261                    key.deleted = true;
262                }
263            } else if !key.deleted {
264                found = true;
265                key.deleted = true;
266            }
267        }
268        if !found {
269            return Err(LbErrKind::ShareNonexistent.into());
270        }
271        result.push(file.sign(keychain)?);
272
273        // delete any links pointing to file
274        if let Some(encrypted_for) = maybe_encrypted_for {
275            if encrypted_for == keychain.get_pk()? {
276                if let Some(link) = self.linked_by(id)? {
277                    let mut link = self.find(&link)?.timestamped_value.value.clone();
278                    link.set_deleted(true);
279                    result.push(link.sign(keychain)?);
280                }
281            }
282        }
283
284        Ok(result)
285    }
286
287    pub fn decrypt_document(
288        &mut self, id: &Uuid, doc: &EncryptedDocument, keychain: &Keychain,
289    ) -> LbResult<DecryptedDocument> {
290        let key = self.decrypt_key(id, keychain)?;
291        let compressed = symkey::decrypt(&key, doc)?;
292        let doc = compression_service::decompress(&compressed)?;
293
294        Ok(doc)
295    }
296
297    pub fn update_document_op(
298        &mut self, id: &Uuid, document: &[u8], keychain: &Keychain,
299    ) -> LbResult<(SignedMeta, EncryptedDocument)> {
300        let id = match self.find(id)?.file_type() {
301            FileType::Document | FileType::Folder => *id,
302            FileType::Link { target } => target,
303        };
304        let mut file = self.find(&id)?.timestamped_value.value.clone();
305        validate::is_document(&file)?;
306        let key = self.decrypt_key(&id, keychain)?;
307        let hmac = {
308            let mut mac = HmacSha256::new_from_slice(&key)
309                .map_err(|err| LbErrKind::Unexpected(format!("hmac creation error: {err:?}")))?;
310            mac.update(document);
311            mac.finalize().into_bytes()
312        }
313        .into();
314        let document = compression_service::compress(document)?;
315        let document = symkey::encrypt(&key, &document)?;
316        file.set_hmac_and_size(Some(hmac), Some(document.value.len()));
317        let file = file.sign(keychain)?;
318
319        Ok((file, document))
320    }
321}
322
323impl<Base, Local, Staged> LazyTree<Staged>
324where
325    Staged: StagedTreeLike<Base = Base, Staged = Local, F = SignedMeta> + TreeLikeMut,
326    Base: TreeLike<F = Staged::F>,
327    Local: TreeLikeMut<F = Staged::F>,
328{
329    pub fn create_unvalidated(
330        &mut self, id: Uuid, key: AESKey, parent: &Uuid, name: &str, file_type: FileType,
331        keychain: &Keychain,
332    ) -> LbResult<Uuid> {
333        let (op, id) = self.create_op(id, key, parent, name, file_type, keychain)?;
334        self.stage_and_promote(Some(op))?;
335        Ok(id)
336    }
337
338    pub fn create(
339        &mut self, id: Uuid, key: AESKey, parent: &Uuid, name: &str, file_type: FileType,
340        keychain: &Keychain,
341    ) -> LbResult<Uuid> {
342        if self.calculate_deleted(parent)? {
343            return Err(LbErrKind::FileParentNonexistent.into());
344        }
345
346        let (op, id) = self.create_op(id, key, parent, name, file_type, keychain)?;
347        self.stage_validate_and_promote(Some(op), Owner(keychain.get_pk()?))?;
348        Ok(id)
349    }
350
351    pub fn rename_unvalidated(
352        &mut self, id: &Uuid, name: &str, keychain: &Keychain,
353    ) -> LbResult<()> {
354        let op = self.rename_op(id, name, keychain)?;
355        self.stage_and_promote(Some(op))?;
356        Ok(())
357    }
358
359    pub fn rename(&mut self, id: &Uuid, name: &str, keychain: &Keychain) -> LbResult<()> {
360        let op = self.rename_op(id, name, keychain)?;
361        self.stage_validate_and_promote(Some(op), Owner(keychain.get_pk()?))?;
362        Ok(())
363    }
364
365    pub fn move_unvalidated(
366        &mut self, id: &Uuid, new_parent: &Uuid, keychain: &Keychain,
367    ) -> LbResult<()> {
368        let op = self.move_op(id, new_parent, keychain)?;
369        self.stage_and_promote(op)?;
370        Ok(())
371    }
372
373    pub fn move_file(&mut self, id: &Uuid, new_parent: &Uuid, keychain: &Keychain) -> LbResult<()> {
374        if self.maybe_find(new_parent).is_none() || self.calculate_deleted(new_parent)? {
375            return Err(LbErrKind::FileParentNonexistent.into());
376        }
377        let op = self.move_op(id, new_parent, keychain)?;
378        self.stage_validate_and_promote(op, Owner(keychain.get_pk()?))?;
379        Ok(())
380    }
381
382    pub fn delete_unvalidated(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<()> {
383        let op = self.delete_op(id, keychain)?;
384        self.stage_and_promote(Some(op))?;
385        Ok(())
386    }
387
388    pub fn delete(&mut self, id: &Uuid, keychain: &Keychain) -> LbResult<()> {
389        let op = self.delete_op(id, keychain)?;
390        self.stage_validate_and_promote(Some(op), Owner(keychain.get_pk()?))?;
391        Ok(())
392    }
393
394    pub fn add_share_unvalidated(
395        &mut self, id: Uuid, sharee: Owner, mode: ShareMode, keychain: &Keychain,
396    ) -> LbResult<()> {
397        let op = self.add_share_op(id, sharee, mode, keychain)?;
398        self.stage_and_promote(Some(op))?;
399        Ok(())
400    }
401
402    pub fn add_share(
403        &mut self, id: Uuid, sharee: Owner, mode: ShareMode, keychain: &Keychain,
404    ) -> LbResult<()> {
405        let op = self.add_share_op(id, sharee, mode, keychain)?;
406        self.stage_validate_and_promote(Some(op), Owner(keychain.get_pk()?))?;
407        Ok(())
408    }
409
410    pub fn delete_share_unvalidated(
411        &mut self, id: &Uuid, maybe_encrypted_for: Option<PublicKey>, keychain: &Keychain,
412    ) -> LbResult<()> {
413        let op = self.delete_share_op(id, maybe_encrypted_for, keychain)?;
414        self.stage_and_promote(op)?;
415        Ok(())
416    }
417
418    pub fn delete_share(
419        &mut self, id: &Uuid, maybe_encrypted_for: Option<PublicKey>, keychain: &Keychain,
420    ) -> LbResult<()> {
421        let op = self.delete_share_op(id, maybe_encrypted_for, keychain)?;
422        self.stage_validate_and_promote(op, Owner(keychain.get_pk()?))?;
423        Ok(())
424    }
425
426    pub fn update_document_unvalidated(
427        &mut self, id: &Uuid, document: &[u8], keychain: &Keychain,
428    ) -> LbResult<EncryptedDocument> {
429        let (op, document) = self.update_document_op(id, document, keychain)?;
430        self.stage_and_promote(Some(op))?;
431        Ok(document)
432    }
433
434    pub fn update_document(
435        &mut self, id: &Uuid, document: &[u8], keychain: &Keychain,
436    ) -> LbResult<EncryptedDocument> {
437        let (op, document) = self.update_document_op(id, document, keychain)?;
438        self.stage_validate_and_promote(Some(op), Owner(keychain.get_pk()?))?;
439        Ok(document)
440    }
441}