1use crate::{
2 database::ClientDatabaseStorage,
3 filesystem::ClientFileSystemStorage,
4 traits::{
5 private::Internal, ClientAccountStorage, ClientBaseStorage,
6 ClientDeviceStorage, ClientFolderStorage, ClientVaultStorage,
7 },
8 ClientEventLogStorage, Error, Result,
9};
10use async_trait::async_trait;
11use indexmap::IndexSet;
12use sos_backend::{
13 AccountEventLog, BackendTarget, DeviceEventLog, Folder, FolderEventLog,
14};
15use sos_core::{
16 device::TrustedDevice,
17 events::{
18 patch::{AccountDiff, CheckedPatch, DeviceDiff, FolderDiff},
19 Event, ReadEvent, WriteEvent,
20 },
21 AccountId, Paths, SecretId, VaultId,
22};
23use sos_login::Identity;
24use sos_reducers::FolderReducer;
25use sos_sync::{
26 CreateSet, ForceMerge, Merge, MergeOutcome, StorageEventLogs, SyncStorage,
27};
28use sos_vault::{Summary, Vault};
29use std::{
30 collections::{HashMap, HashSet},
31 sync::Arc,
32};
33use tokio::sync::RwLock;
34
35#[cfg(feature = "search")]
36use sos_search::AccountSearch;
37
38#[cfg(feature = "files")]
39use {
40 crate::files::ExternalFileManager, sos_backend::FileEventLog,
41 sos_core::events::patch::FileDiff,
42};
43
44use crate::sync::SyncImpl;
45
46pub enum ClientStorage {
48 FileSystem(SyncImpl<ClientFileSystemStorage>),
50 Database(SyncImpl<ClientDatabaseStorage>),
52}
53
54impl ClientStorage {
55 pub async fn new_unauthenticated(
57 target: BackendTarget,
58 account_id: &AccountId,
59 ) -> Result<Self> {
60 Ok(match &target {
61 BackendTarget::FileSystem(paths) => {
62 debug_assert!(!paths.is_server());
63 Self::FileSystem(SyncImpl::new(
64 ClientFileSystemStorage::new_unauthenticated(
65 target, account_id,
66 )
67 .await?,
68 ))
69 }
70 BackendTarget::Database(paths, _) => {
71 debug_assert!(!paths.is_server());
72 Self::Database(SyncImpl::new(
73 ClientDatabaseStorage::new_unauthenticated(
74 target, account_id,
75 )
76 .await?,
77 ))
78 }
79 })
80 }
81
82 pub async fn new_account(
89 target: BackendTarget,
90 account_id: &AccountId,
91 account_name: String,
92 ) -> Result<Self> {
93 Ok(match &target {
94 BackendTarget::FileSystem(paths) => {
95 debug_assert!(!paths.is_server());
96 Self::FileSystem(SyncImpl::new(
97 ClientFileSystemStorage::new_account(
98 target,
99 account_id,
100 account_name,
101 )
102 .await?,
103 ))
104 }
105 BackendTarget::Database(paths, _) => {
106 debug_assert!(!paths.is_server());
107 Self::Database(SyncImpl::new(
108 ClientDatabaseStorage::new_account(
109 target,
110 account_id,
111 account_name,
112 )
113 .await?,
114 ))
115 }
116 })
117 }
118
119 pub async fn rebuild_folder_vault(
129 target: BackendTarget,
130 account_id: &AccountId,
131 folder_id: &VaultId,
132 ) -> Result<Vec<u8>> {
133 let storage =
134 ClientStorage::new_unauthenticated(target, account_id).await?;
135
136 let event_log = FolderEventLog::new_folder(
137 storage.backend_target().clone(),
138 &account_id,
139 folder_id,
140 )
141 .await?;
142
143 let vault = FolderReducer::new()
144 .reduce(&event_log)
145 .await?
146 .build(true)
147 .await?;
148
149 storage.write_vault(&vault, Internal).await
150 }
151}
152
153impl ClientBaseStorage for ClientStorage {
154 fn account_id(&self) -> &AccountId {
155 match self {
156 ClientStorage::FileSystem(fs) => fs.account_id(),
157 ClientStorage::Database(db) => db.account_id(),
158 }
159 }
160
161 fn authenticated_user(&self) -> Option<&Identity> {
162 match self {
163 ClientStorage::FileSystem(fs) => fs.authenticated_user(),
164 ClientStorage::Database(db) => db.authenticated_user(),
165 }
166 }
167
168 fn authenticated_user_mut(&mut self) -> Option<&mut Identity> {
169 match self {
170 ClientStorage::FileSystem(fs) => fs.authenticated_user_mut(),
171 ClientStorage::Database(db) => db.authenticated_user_mut(),
172 }
173 }
174
175 fn paths(&self) -> Arc<Paths> {
176 match self {
177 ClientStorage::FileSystem(fs) => fs.paths(),
178 ClientStorage::Database(db) => db.paths(),
179 }
180 }
181
182 fn backend_target(&self) -> &BackendTarget {
183 match self {
184 ClientStorage::FileSystem(fs) => fs.backend_target(),
185 ClientStorage::Database(db) => db.backend_target(),
186 }
187 }
188
189 fn set_authenticated_user(
190 &mut self,
191 user: Option<Identity>,
192 token: Internal,
193 ) {
194 match self {
195 ClientStorage::FileSystem(fs) => {
196 fs.set_authenticated_user(user, token)
197 }
198 ClientStorage::Database(db) => {
199 db.set_authenticated_user(user, token)
200 }
201 }
202 }
203}
204
205#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
206#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
207impl ClientVaultStorage for ClientStorage {
208 async fn write_vault(
209 &self,
210 vault: &Vault,
211 token: Internal,
212 ) -> Result<Vec<u8>> {
213 match self {
214 ClientStorage::FileSystem(fs) => {
215 fs.write_vault(vault, token).await
216 }
217 ClientStorage::Database(db) => db.write_vault(vault, token).await,
218 }
219 }
220
221 async fn write_login_vault(
222 &self,
223 vault: &Vault,
224 token: Internal,
225 ) -> Result<Vec<u8>> {
226 match self {
227 ClientStorage::FileSystem(fs) => {
228 fs.write_login_vault(vault, token).await
229 }
230 ClientStorage::Database(db) => {
231 db.write_login_vault(vault, token).await
232 }
233 }
234 }
235
236 async fn remove_vault(
237 &self,
238 id: &VaultId,
239 token: Internal,
240 ) -> Result<()> {
241 match self {
242 ClientStorage::FileSystem(fs) => fs.remove_vault(id, token).await,
243 ClientStorage::Database(db) => db.remove_vault(id, token).await,
244 }
245 }
246
247 async fn read_vaults(&self, token: Internal) -> Result<Vec<Summary>> {
248 match self {
249 ClientStorage::FileSystem(fs) => fs.read_vaults(token).await,
250 ClientStorage::Database(db) => db.read_vaults(token).await,
251 }
252 }
253
254 fn summaries(&self, token: Internal) -> &Vec<Summary> {
255 match self {
256 ClientStorage::FileSystem(fs) => fs.summaries(token),
257 ClientStorage::Database(db) => db.summaries(token),
258 }
259 }
260
261 fn summaries_mut(&mut self, token: Internal) -> &mut Vec<Summary> {
262 match self {
263 ClientStorage::FileSystem(fs) => fs.summaries_mut(token),
264 ClientStorage::Database(db) => db.summaries_mut(token),
265 }
266 }
267}
268
269#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
270#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
271impl ClientFolderStorage for ClientStorage {
272 fn folders(&self) -> &HashMap<VaultId, Folder> {
273 match self {
274 ClientStorage::FileSystem(fs) => fs.folders(),
275 ClientStorage::Database(db) => db.folders(),
276 }
277 }
278
279 fn folders_mut(&mut self) -> &mut HashMap<VaultId, Folder> {
280 match self {
281 ClientStorage::FileSystem(fs) => fs.folders_mut(),
282 ClientStorage::Database(db) => db.folders_mut(),
283 }
284 }
285
286 async fn new_folder(
287 &self,
288 vault: &Vault,
289 token: Internal,
290 ) -> Result<Folder> {
291 match self {
292 ClientStorage::FileSystem(fs) => {
293 fs.new_folder(vault, token).await
294 }
295 ClientStorage::Database(db) => db.new_folder(vault, token).await,
296 }
297 }
298
299 async fn read_vault(&self, id: &VaultId) -> Result<Vault> {
300 match self {
301 ClientStorage::FileSystem(fs) => fs.read_vault(id).await,
302 ClientStorage::Database(db) => db.read_vault(id).await,
303 }
304 }
305
306 async fn read_login_vault(&self) -> Result<Vault> {
307 match self {
308 ClientStorage::FileSystem(fs) => fs.read_login_vault().await,
309 ClientStorage::Database(db) => db.read_login_vault().await,
310 }
311 }
312
313 fn current_folder(&self) -> Option<Summary> {
314 match self {
315 ClientStorage::FileSystem(fs) => fs.current_folder(),
316 ClientStorage::Database(db) => db.current_folder(),
317 }
318 }
319
320 fn open_folder(&self, folder_id: &VaultId) -> Result<ReadEvent> {
321 match self {
322 ClientStorage::FileSystem(fs) => fs.open_folder(folder_id),
323 ClientStorage::Database(db) => db.open_folder(folder_id),
324 }
325 }
326
327 fn close_folder(&self) {
328 match self {
329 ClientStorage::FileSystem(fs) => fs.close_folder(),
330 ClientStorage::Database(db) => db.close_folder(),
331 }
332 }
333}
334
335#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
336#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
337impl ClientDeviceStorage for ClientStorage {
338 fn devices(&self) -> &IndexSet<TrustedDevice> {
339 match self {
340 ClientStorage::FileSystem(fs) => fs.devices(),
341 ClientStorage::Database(db) => db.devices(),
342 }
343 }
344
345 fn set_devices(
346 &mut self,
347 devices: IndexSet<TrustedDevice>,
348 token: Internal,
349 ) {
350 match self {
351 ClientStorage::FileSystem(fs) => fs.set_devices(devices, token),
352 ClientStorage::Database(db) => db.set_devices(devices, token),
353 }
354 }
355
356 fn list_trusted_devices(&self) -> Vec<&TrustedDevice> {
357 match self {
358 ClientStorage::FileSystem(fs) => fs.list_trusted_devices(),
359 ClientStorage::Database(db) => db.list_trusted_devices(),
360 }
361 }
362}
363
364#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
365#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
366impl ClientAccountStorage for ClientStorage {
367 async fn import_account(
368 &mut self,
369 account_data: &CreateSet,
370 ) -> Result<()> {
371 match self {
372 ClientStorage::FileSystem(fs) => {
373 fs.import_account(account_data).await
374 }
375 ClientStorage::Database(db) => {
376 db.import_account(account_data).await
377 }
378 }
379 }
380
381 async fn list_secret_ids(
382 &self,
383 folder_id: &VaultId,
384 ) -> Result<Vec<SecretId>> {
385 self.guard_authenticated(Internal)?;
386
387 match self {
388 ClientStorage::FileSystem(fs) => {
389 fs.list_secret_ids(folder_id).await
390 }
391 ClientStorage::Database(db) => {
392 db.list_secret_ids(folder_id).await
393 }
394 }
395 }
396
397 async fn create_device_vault(
398 &mut self,
399 device_vault: &[u8],
400 ) -> Result<()> {
401 match self {
402 ClientStorage::FileSystem(fs) => {
403 fs.create_device_vault(device_vault).await
404 }
405 ClientStorage::Database(db) => {
406 db.create_device_vault(device_vault).await
407 }
408 }
409 }
410
411 async fn delete_account(&self) -> Result<Event> {
412 self.guard_authenticated(Internal)?;
413
414 match self {
415 ClientStorage::FileSystem(fs) => fs.delete_account().await,
416 ClientStorage::Database(db) => db.delete_account().await,
417 }
418 }
419
420 #[cfg(feature = "files")]
421 fn external_file_manager(&self) -> Option<&ExternalFileManager> {
422 match self {
423 ClientStorage::FileSystem(fs) => fs.external_file_manager(),
424 ClientStorage::Database(db) => db.external_file_manager(),
425 }
426 }
427
428 #[cfg(feature = "files")]
429 fn external_file_manager_mut(
430 &mut self,
431 ) -> Option<&mut ExternalFileManager> {
432 match self {
433 ClientStorage::FileSystem(fs) => fs.external_file_manager_mut(),
434 ClientStorage::Database(db) => db.external_file_manager_mut(),
435 }
436 }
437
438 #[cfg(feature = "files")]
439 fn set_external_file_manager(
440 &mut self,
441 file_manager: Option<ExternalFileManager>,
442 token: Internal,
443 ) {
444 match self {
445 ClientStorage::FileSystem(fs) => {
446 fs.set_external_file_manager(file_manager, token)
447 }
448 ClientStorage::Database(db) => {
449 db.set_external_file_manager(file_manager, token)
450 }
451 }
452 }
453
454 #[cfg(feature = "search")]
455 fn search_index(&self) -> Option<&AccountSearch> {
456 match self {
457 ClientStorage::FileSystem(fs) => fs.search_index(),
458 ClientStorage::Database(db) => db.search_index(),
459 }
460 }
461
462 #[cfg(feature = "search")]
463 fn search_index_mut(&mut self) -> Option<&mut AccountSearch> {
464 match self {
465 ClientStorage::FileSystem(fs) => fs.search_index_mut(),
466 ClientStorage::Database(db) => db.search_index_mut(),
467 }
468 }
469
470 #[cfg(feature = "search")]
471 fn set_search_index(
472 &mut self,
473 index: Option<AccountSearch>,
474 token: Internal,
475 ) {
476 match self {
477 ClientStorage::FileSystem(fs) => {
478 fs.set_search_index(index, token)
479 }
480 ClientStorage::Database(db) => db.set_search_index(index, token),
481 }
482 }
483}
484
485#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
486#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
487impl ClientEventLogStorage for ClientStorage {
488 async fn initialize_device_log(
489 &self,
490 device: TrustedDevice,
491 token: Internal,
492 ) -> Result<(DeviceEventLog, IndexSet<TrustedDevice>)> {
493 match self {
494 ClientStorage::FileSystem(fs) => {
495 fs.initialize_device_log(device, token).await
496 }
497 ClientStorage::Database(db) => {
498 db.initialize_device_log(device, token).await
499 }
500 }
501 }
502
503 #[cfg(feature = "files")]
504 async fn initialize_file_log(
505 &self,
506 token: Internal,
507 ) -> Result<FileEventLog> {
508 match self {
509 ClientStorage::FileSystem(fs) => {
510 fs.initialize_file_log(token).await
511 }
512 ClientStorage::Database(db) => {
513 db.initialize_file_log(token).await
514 }
515 }
516 }
517
518 fn set_identity_log(
519 &mut self,
520 log: Arc<RwLock<FolderEventLog>>,
521 token: Internal,
522 ) {
523 match self {
524 ClientStorage::FileSystem(fs) => fs.set_identity_log(log, token),
525 ClientStorage::Database(db) => db.set_identity_log(log, token),
526 }
527 }
528
529 fn set_device_log(
530 &mut self,
531 log: Arc<RwLock<DeviceEventLog>>,
532 token: Internal,
533 ) {
534 match self {
535 ClientStorage::FileSystem(fs) => fs.set_device_log(log, token),
536 ClientStorage::Database(db) => db.set_device_log(log, token),
537 }
538 }
539
540 #[cfg(feature = "files")]
541 fn set_file_log(
542 &mut self,
543 log: Arc<RwLock<FileEventLog>>,
544 token: Internal,
545 ) {
546 match self {
547 ClientStorage::FileSystem(fs) => fs.set_file_log(log, token),
548 ClientStorage::Database(db) => db.set_file_log(log, token),
549 }
550 }
551}
552
553#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
554#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
555impl StorageEventLogs for ClientStorage {
556 type Error = Error;
557
558 async fn identity_log(&self) -> Result<Arc<RwLock<FolderEventLog>>> {
559 match self {
560 ClientStorage::FileSystem(fs) => fs.identity_log().await,
561 ClientStorage::Database(db) => db.identity_log().await,
562 }
563 }
564
565 async fn account_log(&self) -> Result<Arc<RwLock<AccountEventLog>>> {
566 match self {
567 ClientStorage::FileSystem(fs) => fs.account_log().await,
568 ClientStorage::Database(db) => db.account_log().await,
569 }
570 }
571
572 async fn device_log(&self) -> Result<Arc<RwLock<DeviceEventLog>>> {
573 match self {
574 ClientStorage::FileSystem(fs) => fs.device_log().await,
575 ClientStorage::Database(db) => db.device_log().await,
576 }
577 }
578
579 #[cfg(feature = "files")]
580 async fn file_log(&self) -> Result<Arc<RwLock<FileEventLog>>> {
581 match self {
582 ClientStorage::FileSystem(fs) => fs.file_log().await,
583 ClientStorage::Database(db) => db.file_log().await,
584 }
585 }
586
587 async fn folder_details(&self) -> Result<IndexSet<Summary>> {
588 match self {
589 ClientStorage::FileSystem(fs) => fs.folder_details().await,
590 ClientStorage::Database(db) => db.folder_details().await,
591 }
592 }
593
594 async fn folder_log(
595 &self,
596 id: &VaultId,
597 ) -> Result<Arc<RwLock<FolderEventLog>>> {
598 match self {
599 ClientStorage::FileSystem(fs) => fs.folder_log(id).await,
600 ClientStorage::Database(db) => db.folder_log(id).await,
601 }
602 }
603}
604
605#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
606#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
607impl ForceMerge for ClientStorage {
608 async fn force_merge_identity(
609 &mut self,
610 diff: FolderDiff,
611 outcome: &mut MergeOutcome,
612 ) -> Result<()> {
613 match self {
614 ClientStorage::FileSystem(fs) => {
615 fs.force_merge_identity(diff, outcome).await
616 }
617 ClientStorage::Database(db) => {
618 db.force_merge_identity(diff, outcome).await
619 }
620 }
621 }
622
623 async fn force_merge_folder(
625 &mut self,
626 folder_id: &VaultId,
627 diff: FolderDiff,
628 outcome: &mut MergeOutcome,
629 ) -> Result<()> {
630 match self {
631 ClientStorage::FileSystem(fs) => {
632 fs.force_merge_folder(folder_id, diff, outcome).await
633 }
634 ClientStorage::Database(db) => {
635 db.force_merge_folder(folder_id, diff, outcome).await
636 }
637 }
638 }
639}
640
641#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
642#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
643impl Merge for ClientStorage {
644 async fn merge_identity(
645 &mut self,
646 diff: FolderDiff,
647 outcome: &mut MergeOutcome,
648 ) -> Result<CheckedPatch> {
649 match self {
650 ClientStorage::FileSystem(fs) => {
651 fs.merge_identity(diff, outcome).await
652 }
653 ClientStorage::Database(db) => {
654 db.merge_identity(diff, outcome).await
655 }
656 }
657 }
658
659 async fn merge_account(
660 &mut self,
661 diff: AccountDiff,
662 outcome: &mut MergeOutcome,
663 ) -> Result<(CheckedPatch, HashSet<VaultId>)> {
664 match self {
665 ClientStorage::FileSystem(fs) => {
666 fs.merge_account(diff, outcome).await
667 }
668 ClientStorage::Database(db) => {
669 db.merge_account(diff, outcome).await
670 }
671 }
672 }
673
674 async fn merge_device(
675 &mut self,
676 diff: DeviceDiff,
677 outcome: &mut MergeOutcome,
678 ) -> Result<CheckedPatch> {
679 match self {
680 ClientStorage::FileSystem(fs) => {
681 fs.merge_device(diff, outcome).await
682 }
683 ClientStorage::Database(db) => {
684 db.merge_device(diff, outcome).await
685 }
686 }
687 }
688
689 #[cfg(feature = "files")]
690 async fn merge_files(
691 &mut self,
692 diff: FileDiff,
693 outcome: &mut MergeOutcome,
694 ) -> Result<CheckedPatch> {
695 match self {
696 ClientStorage::FileSystem(fs) => {
697 fs.merge_files(diff, outcome).await
698 }
699 ClientStorage::Database(db) => {
700 db.merge_files(diff, outcome).await
701 }
702 }
703 }
704
705 async fn merge_folder(
706 &mut self,
707 folder_id: &VaultId,
708 diff: FolderDiff,
709 outcome: &mut MergeOutcome,
710 ) -> Result<(CheckedPatch, Vec<WriteEvent>)> {
711 match self {
712 ClientStorage::FileSystem(fs) => {
713 fs.merge_folder(folder_id, diff, outcome).await
714 }
715 ClientStorage::Database(db) => {
716 db.merge_folder(folder_id, diff, outcome).await
717 }
718 }
719 }
720}
721
722#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
723#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
724impl SyncStorage for ClientStorage {
725 fn is_client_storage(&self) -> bool {
726 true
727 }
728}