1use crate::model::ValidationFailure;
2use crate::model::access_info::UserAccessMode;
3use crate::model::file_like::FileLike;
4use crate::model::file_metadata::{Diff, FileDiff, FileType, Owner};
5use crate::model::filename::MAX_ENCRYPTED_FILENAME_LENGTH;
6use crate::model::lazy::LazyTree;
7use crate::model::staged::StagedTreeLike;
8use crate::model::tree_like::TreeLike;
9use std::collections::{HashMap, HashSet};
10
11use super::errors::{LbErrKind, LbResult};
12
13pub fn file_name(name: &str) -> LbResult<()> {
14 if name.is_empty() {
15 Err(LbErrKind::FileNameEmpty)?;
16 }
17 if name.contains('/') {
18 Err(LbErrKind::FileNameContainsSlash)?;
19 }
20 Ok(())
21}
22
23pub fn not_root<F: FileLike>(file: &F) -> LbResult<()> {
24 if file.is_root() { Err(LbErrKind::RootModificationInvalid)? } else { Ok(()) }
25}
26
27pub fn is_folder<F: FileLike>(file: &F) -> LbResult<()> {
28 if file.is_folder() {
29 Ok(())
30 } else {
31 Err(LbErrKind::Validation(ValidationFailure::NonFolderWithChildren(*file.id())))?
32 }
33}
34
35pub fn is_document<F: FileLike>(file: &F) -> LbResult<()> {
36 if file.is_document() { Ok(()) } else { Err(LbErrKind::FileNotDocument)? }
37}
38
39pub fn path(path: &str) -> LbResult<()> {
40 if path.contains("//") || path.is_empty() {
41 Err(LbErrKind::PathContainsEmptyFileName)?;
42 }
43
44 Ok(())
45}
46
47impl<T, Base, Local> LazyTree<T>
48where
49 T: StagedTreeLike<Base = Base, Staged = Local>,
50 Base: TreeLike<F = T::F>,
51 Local: TreeLike<F = T::F>,
52{
53 pub fn validate(&mut self, owner: Owner) -> LbResult<()> {
54 self.assert_no_root_changes()?;
56 self.assert_no_changes_to_deleted_files()?;
57 self.assert_all_filenames_size_limit()?;
58 self.assert_all_files_decryptable(owner)?;
59 self.assert_only_folders_have_children()?;
60 self.assert_all_files_same_owner_as_parent()?;
61
62 self.assert_no_cycles()?;
64 self.assert_no_path_conflicts()?;
65 self.assert_no_shared_links()?;
66 self.assert_no_duplicate_links()?;
67 self.assert_no_broken_links()?;
68 self.assert_no_owned_links()?;
69
70 self.assert_changes_authorized(owner)?;
72
73 Ok(())
74 }
75
76 pub fn assert_all_files_decryptable(&mut self, owner: Owner) -> LbResult<()> {
78 for file in self.ids().into_iter().filter_map(|id| self.maybe_find(&id)) {
79 if self.maybe_find_parent(file).is_none()
80 && !file
81 .user_access_keys()
82 .iter()
83 .any(|k| k.encrypted_for == owner.0)
84 {
85 Err(LbErrKind::Validation(ValidationFailure::Orphan(*file.id())))?;
86 }
87 }
88 Ok(())
89 }
90
91 pub fn assert_all_filenames_size_limit(&self) -> LbResult<()> {
92 for file in self.all_files()? {
93 if file.secret_name().encrypted_value.value.len() > MAX_ENCRYPTED_FILENAME_LENGTH {
94 return Err(LbErrKind::Validation(ValidationFailure::FileNameTooLong(*file.id())))?;
95 }
96 }
97 Ok(())
98 }
99
100 pub fn assert_only_folders_have_children(&self) -> LbResult<()> {
101 for file in self.all_files()? {
102 if let Some(parent) = self.maybe_find(file.parent()) {
103 if !parent.is_folder() {
104 Err(LbErrKind::Validation(ValidationFailure::NonFolderWithChildren(
105 *parent.id(),
106 )))?;
107 }
108 }
109 }
110 Ok(())
111 }
112
113 pub fn assert_all_files_same_owner_as_parent(&mut self) -> LbResult<()> {
116 for id in self.ids() {
117 if self.calculate_deleted(&id)? {
118 continue;
119 }
120 let file = self.find(&id)?;
121 if let Some(parent) = self.maybe_find(file.parent()) {
122 if parent.owner() != file.owner() {
123 Err(LbErrKind::Validation(ValidationFailure::FileWithDifferentOwnerParent(
124 *file.id(),
125 )))?;
126 }
127 }
128 }
129 Ok(())
130 }
131
132 pub fn assert_no_cycles(&mut self) -> LbResult<()> {
134 let mut owners_with_found_roots = HashSet::new();
135 let mut no_cycles_in_ancestors = HashSet::new();
136 for id in self.ids() {
137 let mut ancestors = HashSet::new();
138 let mut current_file = self.find(&id)?;
139 loop {
140 if no_cycles_in_ancestors.contains(current_file.id()) {
141 break;
142 } else if current_file.is_root() {
143 if owners_with_found_roots.insert(current_file.owner()) {
144 ancestors.insert(*current_file.id());
145 break;
146 } else {
147 Err(LbErrKind::Validation(ValidationFailure::Cycle(HashSet::from([id]))))?;
148 }
149 } else if ancestors.contains(current_file.parent()) {
150 Err(LbErrKind::Validation(ValidationFailure::Cycle(
151 self.ancestors(current_file.id())?,
152 )))?;
153 }
154 ancestors.insert(*current_file.id());
155 current_file = match self.maybe_find_parent(current_file) {
156 Some(file) => file,
157 None => {
158 if !current_file.user_access_keys().is_empty() {
159 break;
160 } else {
161 return Err(LbErrKind::FileParentNonexistent)?;
162 }
163 }
164 }
165 }
166 no_cycles_in_ancestors.extend(ancestors);
167 }
168 Ok(())
169 }
170
171 pub fn assert_no_path_conflicts(&mut self) -> LbResult<()> {
172 let mut id_by_name = HashMap::new();
173 for id in self.ids() {
174 if !self.calculate_deleted(&id)? {
175 let file = self.find(&id)?;
176 if file.is_root() || self.maybe_find(file.parent()).is_none() {
177 continue;
178 }
179 if let Some(conflicting) = id_by_name.remove(file.secret_name()) {
180 Err(LbErrKind::Validation(ValidationFailure::PathConflict(HashSet::from([
181 conflicting,
182 *file.id(),
183 ]))))?;
184 }
185 id_by_name.insert(file.secret_name().clone(), *file.id());
186 }
187 }
188 Ok(())
189 }
190
191 pub fn assert_no_shared_links(&self) -> LbResult<()> {
192 for link in self.ids() {
193 let meta = self.find(&link)?;
194 if let FileType::Link { target: _ } = meta.file_type() {
195 if meta.is_shared() {
196 Err(LbErrKind::Validation(ValidationFailure::SharedLink {
197 link,
198 shared_ancestor: link,
199 }))?;
200 }
201 for ancestor in self.ancestors(&link)? {
202 if self.find(&ancestor)?.is_shared() {
203 Err(LbErrKind::Validation(ValidationFailure::SharedLink {
204 link,
205 shared_ancestor: ancestor,
206 }))?;
207 }
208 }
209 }
210 }
211 Ok(())
212 }
213
214 pub fn assert_no_duplicate_links(&mut self) -> LbResult<()> {
215 let mut linked_targets = HashSet::new();
216 for link in self.ids() {
217 if self.calculate_deleted(&link)? {
218 continue;
219 }
220 if let FileType::Link { target } = self.find(&link)?.file_type() {
221 if !linked_targets.insert(target) {
222 Err(LbErrKind::Validation(ValidationFailure::DuplicateLink { target }))?;
223 }
224 }
225 }
226 Ok(())
227 }
228
229 pub fn assert_no_broken_links(&mut self) -> LbResult<()> {
235 for link in self.ids() {
236 if let FileType::Link { target } = self.find(&link)?.file_type() {
237 if !self.calculate_deleted(&link)? && self.maybe_find(&target).is_none() {
238 Err(LbErrKind::Validation(ValidationFailure::BrokenLink(link)))?;
239 }
240 }
241 }
242 Ok(())
243 }
244
245 pub fn assert_no_owned_links(&self) -> LbResult<()> {
246 for link in self.ids() {
247 if let FileType::Link { target } = self.find(&link)?.file_type() {
248 if let Some(target_owner) = self.maybe_find(&target).map(|f| f.owner()) {
249 if self.find(&link)?.owner() == target_owner {
250 Err(LbErrKind::Validation(ValidationFailure::OwnedLink(link)))?;
251 }
252 }
253 }
254 }
255 Ok(())
256 }
257
258 pub fn assert_no_root_changes(&mut self) -> LbResult<()> {
259 for id in self.tree.staged().ids() {
260 if let Some(base) = self.tree.base().maybe_find(&id) {
262 if base.is_root() {
263 Err(LbErrKind::RootModificationInvalid)?;
264 }
265 }
266 if self.find(&id)?.is_root() {
268 Err(LbErrKind::Validation(ValidationFailure::Cycle(
269 vec![id].into_iter().collect(),
270 )))?;
271 }
272 }
273 Ok(())
274 }
275
276 pub fn assert_no_changes_to_deleted_files(&mut self) -> LbResult<()> {
277 for id in self.tree.staged().ids() {
278 let mut base = self.tree.base().to_lazy();
280 if base.maybe_find(&id).is_some() && base.calculate_deleted(&id)? {
281 Err(LbErrKind::Validation(ValidationFailure::DeletedFileUpdated(id)))?;
282 }
283 if self.calculate_deleted(&id)? {
285 if let Some(base) = self.tree.base().maybe_find(&id) {
286 if FileDiff::edit(base.clone(), self.find(&id)?.clone())
287 .diff()
288 .iter()
289 .any(|d| d != &Diff::Deleted)
290 {
291 Err(LbErrKind::Validation(ValidationFailure::DeletedFileUpdated(id)))?;
292 }
293 }
294 }
295 }
296 Ok(())
297 }
298
299 pub fn assert_changes_authorized(&mut self, owner: Owner) -> LbResult<()> {
300 let new_files = {
319 let mut new_files = HashSet::new();
320 for id in self.tree.staged().ids() {
321 if self.tree.base().maybe_find(&id).is_none() {
322 new_files.insert(id);
323 }
324 }
325 new_files
326 };
327
328 for file_diff in self.diffs()? {
329 for field_diff in file_diff.diff() {
330 match field_diff {
331 Diff::New | Diff::Name | Diff::Deleted => {
332 let file =
334 if let Some(ref old) = file_diff.old { old } else { &file_diff.new };
335 if !new_files.contains(file.parent()) {
337 if let Some(parent) = self.maybe_find(file.parent()) {
339 if self.access_mode(owner, parent.id())?
340 < Some(UserAccessMode::Write)
341 {
342 Err(LbErrKind::InsufficientPermission)?;
344 }
345 } else {
346 Err(LbErrKind::InsufficientPermission)?;
348 }
349 }
350 }
351 Diff::Parent | Diff::Owner => {
352 {
354 let parent = if let Some(ref old) = file_diff.old {
355 old.parent()
356 } else {
357 return Err(LbErrKind::Unexpected(
358 "Non-New FileDiff with no old".to_string(),
359 )
360 .into());
361 };
362
363 if let Some(parent) = self.maybe_find(parent) {
365 if self.access_mode(owner, parent.id())?
366 < Some(UserAccessMode::Write)
367 {
368 Err(LbErrKind::InsufficientPermission)?;
370 }
371 } else {
372 Err(LbErrKind::InsufficientPermission)?;
374 }
375 }
376 {
378 let parent = file_diff.new.parent();
379
380 if !new_files.contains(parent) {
382 if let Some(parent) = self.maybe_find(parent) {
384 if self.access_mode(owner, parent.id())?
385 < Some(UserAccessMode::Write)
386 {
387 Err(LbErrKind::InsufficientPermission)?;
389 }
390 } else {
391 Err(LbErrKind::InsufficientPermission)?;
393 }
394 }
395 }
396 }
397 Diff::Hmac => {
398 if self.access_mode(owner, file_diff.id())? < Some(UserAccessMode::Write) {
400 Err(LbErrKind::InsufficientPermission)?;
401 }
402 }
403 Diff::UserKeys => {
404 let base_keys = {
406 if let Some(ref old) = file_diff.old {
407 let mut base_keys = HashMap::new();
408 for key in old.user_access_keys() {
409 base_keys.insert(
410 (Owner(key.encrypted_by), Owner(key.encrypted_for)),
411 (key.mode, key.deleted),
412 );
413 }
414 base_keys
415 } else {
416 return Err(LbErrKind::Unexpected(
417 "Non-New FileDiff with no old".to_string(),
418 ))?;
419 }
420 };
421 for key in file_diff.new.user_access_keys() {
422 if let Some((base_mode, base_deleted)) =
423 base_keys.get(&(Owner(key.encrypted_by), Owner(key.encrypted_for)))
424 {
425 let (staged_mode, staged_deleted) = (&key.mode, &key.deleted);
428 if *staged_deleted
430 && !*base_deleted
431 && self.access_mode(owner, file_diff.id())?
432 < Some(UserAccessMode::Write)
433 && owner.0 != key.encrypted_for
434 {
435 Err(LbErrKind::InsufficientPermission)?;
436 }
437 if staged_mode != base_mode
439 && self.access_mode(owner, file_diff.id())?
440 < Some(UserAccessMode::Write)
441 {
442 Err(LbErrKind::InsufficientPermission)?;
443 }
444 } else {
445 if self.access_mode(owner, file_diff.id())? < Some(key.mode) {
449 Err(LbErrKind::InsufficientPermission)?;
450 }
451 }
452 }
453 }
454 }
455 }
456 }
457
458 Ok(())
459 }
460
461 fn diffs(&self) -> LbResult<Vec<FileDiff<Base::F>>> {
462 let mut result = Vec::new();
463 for id in self.tree.staged().ids() {
464 let staged = self.tree.staged().find(&id)?;
465 if let Some(base) = self.tree.base().maybe_find(&id) {
466 result.push(FileDiff::edit(base.clone(), staged.clone()));
467 } else {
468 result.push(FileDiff::new(staged.clone()));
469 }
470 }
471 Ok(result)
472 }
473}