lb_rs/model/
server_ops.rs

1use super::errors::{DiffError, LbErrKind, LbResult};
2use crate::model::clock::get_time;
3use crate::model::file_like::FileLike;
4use crate::model::file_metadata::FileDiff;
5use crate::model::lazy::{LazyStaged1, LazyTree};
6use crate::model::server_file::{IntoServerFile, ServerFile};
7use crate::model::server_tree::ServerTree;
8use crate::model::signed_file::SignedFile;
9use crate::model::tree_like::TreeLike;
10
11type LazyServerStaged1<'a> = LazyStaged1<ServerTree<'a>, Vec<ServerFile>>;
12
13impl<'a> LazyTree<ServerTree<'a>> {
14    /// Validates a diff prior to staging it. Performs individual validations, then validations that
15    /// require a tree
16    pub fn stage_diff(self, changes: Vec<FileDiff<SignedFile>>) -> LbResult<LazyServerStaged1<'a>> {
17        // Check new.id == old.id
18        for change in &changes {
19            if let Some(old) = &change.old {
20                if old.id() != change.new.id() {
21                    return Err(LbErrKind::Diff(DiffError::DiffMalformed))?;
22                }
23            }
24        }
25
26        // Check for changes to digest
27        for change in &changes {
28            match &change.old {
29                Some(old) => {
30                    if old.timestamped_value.value.document_hmac
31                        != change.new.timestamped_value.value.document_hmac
32                    {
33                        return Err(LbErrKind::Diff(DiffError::HmacModificationInvalid))?;
34                    }
35                }
36                None => {
37                    if change.new.timestamped_value.value.document_hmac.is_some() {
38                        return Err(LbErrKind::Diff(DiffError::HmacModificationInvalid))?;
39                    }
40                }
41            }
42        }
43
44        // Check for race conditions
45        for change in &changes {
46            match &change.old {
47                Some(old) => {
48                    let current = &self
49                        .maybe_find(old.id())
50                        .ok_or(LbErrKind::Diff(DiffError::OldFileNotFound))?
51                        .file;
52                    if current != old {
53                        return Err(LbErrKind::Diff(DiffError::OldVersionIncorrect))?;
54                    }
55                }
56                None => {
57                    // if you're claiming this file is new, it must be globally unique
58                    if self.tree.files.maybe_find(change.new.id()).is_some() {
59                        return Err(LbErrKind::Diff(DiffError::OldVersionRequired))?;
60                    }
61                }
62            }
63        }
64
65        let now = get_time().0 as u64;
66        let changes = changes
67            .into_iter()
68            .map(|change| change.new.add_time(now))
69            .collect();
70
71        Ok(self.stage(changes))
72    }
73}