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