sos_backend/
folder.rs

1//! Folder combines an access point with an event log.
2use crate::{AccessPoint, BackendTarget, Error, FolderEventLog, Result};
3use sos_core::{
4    commit::{CommitHash, CommitState},
5    crypto::AccessKey,
6    encode,
7    events::{EventLog, EventLogType, EventRecord, ReadEvent, WriteEvent},
8    AccountId, VaultFlags, VaultId,
9};
10use sos_core::{constants::EVENT_LOG_EXT, decode, VaultCommit};
11use sos_database::{
12    entity::{FolderEntity, FolderRecord, SecretRecord},
13    VaultDatabaseWriter,
14};
15use sos_filesystem::VaultFileWriter;
16use sos_reducers::FolderReducer;
17use sos_vault::{
18    secret::{Secret, SecretId, SecretMeta, SecretRow},
19    AccessPoint as VaultAccessPoint, EncryptedEntry, SecretAccess, Vault,
20    VaultMeta,
21};
22use sos_vfs as vfs;
23use std::{path::Path, sync::Arc};
24use tokio::sync::{Mutex, RwLock};
25
26/// Folder is a combined vault and event log.
27#[derive(Clone)]
28pub struct Folder {
29    access_point: Arc<Mutex<AccessPoint>>,
30    events: Arc<RwLock<FolderEventLog>>,
31}
32
33impl Folder {
34    /// Create a new folder.
35    ///
36    /// Changes to the in-memory vault are mirrored to storage.
37    pub async fn new(
38        target: BackendTarget,
39        account_id: &AccountId,
40        folder_id: &VaultId,
41    ) -> Result<Self> {
42        match target {
43            BackendTarget::FileSystem(paths) => {
44                Self::from_path(
45                    paths.with_account_id(account_id).vault_path(folder_id),
46                    account_id,
47                    EventLogType::Folder(*folder_id),
48                )
49                .await
50            }
51            BackendTarget::Database(paths, client) => {
52                let folder_id = *folder_id;
53                let folder_row = client
54                    .conn(move |conn| {
55                        let folder = FolderEntity::new(&conn);
56                        Ok(folder.find_one(&folder_id)?)
57                    })
58                    .await
59                    .map_err(sos_database::Error::from)?;
60                let folder_record =
61                    FolderRecord::from_row(folder_row).await?;
62                let mut vault = folder_record.into_vault()?;
63
64                let secrets = client
65                    .conn_and_then(move |conn| {
66                        let folder = FolderEntity::new(&conn);
67                        let secrets =
68                            folder.load_secrets(folder_record.row_id)?;
69                        Ok::<_, sos_database::Error>(secrets)
70                    })
71                    .await?;
72
73                for secret in secrets {
74                    let record = SecretRecord::from_row(secret).await?;
75                    let VaultCommit(commit, entry) = record.commit;
76                    vault
77                        .insert_secret(record.secret_id, commit, entry)
78                        .await?;
79                }
80
81                let mut event_log = FolderEventLog::new_folder(
82                    BackendTarget::Database(paths, client.clone()),
83                    &account_id,
84                    &folder_id,
85                )
86                .await?;
87
88                event_log.load_tree().await?;
89
90                if event_log.tree().len() == 0 {
91                    let buffer = encode(&vault).await?;
92                    let event = WriteEvent::CreateVault(buffer);
93                    event_log.apply(&[event]).await?;
94                }
95
96                let mirror =
97                    VaultDatabaseWriter::<Error>::new(client, folder_id);
98                let access_point = VaultAccessPoint::<Error>::new_mirror(
99                    vault,
100                    Box::new(mirror),
101                );
102
103                Ok(Self::init(AccessPoint::wrap(access_point), event_log))
104            }
105        }
106    }
107
108    /// Create a new folder using a vault and events.
109    pub async fn from_vault_event_log(
110        target: &BackendTarget,
111        vault: Vault,
112        event_log: FolderEventLog,
113    ) -> Result<Self> {
114        let access_point = match target {
115            BackendTarget::FileSystem(paths) => {
116                let path = paths.vault_path(vault.id());
117                let mirror = VaultFileWriter::<Error>::new(path);
118                VaultAccessPoint::<Error>::new_mirror(vault, Box::new(mirror))
119            }
120            BackendTarget::Database(_, client) => {
121                let mirror = VaultDatabaseWriter::<Error>::new(
122                    client.clone(),
123                    *vault.id(),
124                );
125                VaultAccessPoint::<Error>::new_mirror(vault, Box::new(mirror))
126            }
127        };
128        Ok(Self::init(AccessPoint::wrap(access_point), event_log))
129    }
130
131    /// Create a new folder from a vault on disc.
132    ///
133    /// Changes to the in-memory vault are mirrored to disc and
134    /// and if an event log does not exist it is created.
135    ///
136    /// If an event log exists the commit tree is loaded into memory.
137    pub async fn from_path(
138        path: impl AsRef<Path>,
139        account_id: &AccountId,
140        log_type: EventLogType,
141    ) -> Result<Self> {
142        let mut events_path = path.as_ref().to_owned();
143        events_path.set_extension(EVENT_LOG_EXT);
144
145        let mut event_log =
146            sos_filesystem::FolderEventLog::<Error>::new_folder(
147                events_path,
148                *account_id,
149                log_type,
150            )
151            .await?;
152        event_log.load_tree().await?;
153        let needs_init = event_log.tree().root().is_none();
154
155        let vault = if needs_init {
156            let vault = if vfs::try_exists(path.as_ref()).await? {
157                // For the client-side we must split the events
158                // out but keep the existing vault data (not the head-only)
159                // version so that the event log here will match what the
160                // server will have when an account is first synced
161                let buffer = vfs::read(path.as_ref()).await?;
162                let vault: Vault = decode(&buffer).await?;
163                vault
164            } else {
165                // If it doesn't exist on disc use a default vault
166                Default::default()
167            };
168
169            let (_, events) =
170                FolderReducer::split::<Error>(vault.clone()).await?;
171            event_log.apply(events.as_slice()).await?;
172
173            vault
174        } else {
175            let buffer = vfs::read(path.as_ref()).await?;
176            let vault: Vault = decode(&buffer).await?;
177            vault
178        };
179
180        let mirror = VaultFileWriter::<Error>::new(path.as_ref());
181        let access_point =
182            VaultAccessPoint::<Error>::new_mirror(vault, Box::new(mirror));
183
184        Ok(Self::init(
185            AccessPoint::wrap(access_point),
186            FolderEventLog::FileSystem(event_log),
187        ))
188    }
189
190    /// Create a new folder.
191    fn init(access_point: AccessPoint, events: FolderEventLog) -> Self {
192        Self {
193            access_point: Arc::new(Mutex::new(access_point)),
194            events: Arc::new(RwLock::new(events)),
195        }
196    }
197
198    /// Folder identifier.
199    pub async fn id(&self) -> VaultId {
200        let access_point = self.access_point.lock().await;
201        *access_point.id()
202    }
203
204    /// Access point for this folder.
205    pub fn access_point(&self) -> Arc<Mutex<AccessPoint>> {
206        self.access_point.clone()
207    }
208
209    /// Clone of the event log.
210    pub fn event_log(&self) -> Arc<RwLock<FolderEventLog>> {
211        Arc::clone(&self.events)
212    }
213
214    /// Unlock using the folder access key.
215    pub async fn unlock(
216        &mut self,
217        key: &AccessKey,
218    ) -> crate::Result<VaultMeta> {
219        let mut access_point = self.access_point.lock().await;
220        Ok(access_point.unlock(key).await?)
221    }
222
223    /// Lock the folder.
224    pub async fn lock(&mut self) {
225        let mut access_point = self.access_point.lock().await;
226        access_point.lock();
227    }
228
229    /// Create a secret.
230    pub async fn create_secret(
231        &mut self,
232        secret_data: &SecretRow,
233    ) -> crate::Result<WriteEvent> {
234        let mut access_point = self.access_point.lock().await;
235        let event = access_point.create_secret(secret_data).await?;
236        let mut events = self.events.write().await;
237        events.apply(&[event.clone()]).await?;
238        Ok(event)
239    }
240
241    /// Get a secret and it's meta data.
242    pub async fn read_secret(
243        &self,
244        id: &SecretId,
245    ) -> crate::Result<Option<(SecretMeta, Secret, ReadEvent)>> {
246        let access_point = self.access_point.lock().await;
247        Ok(access_point.read_secret(id).await?)
248    }
249
250    /// Read the encrypted contents of a secret.
251    pub async fn raw_secret(
252        &self,
253        id: &SecretId,
254    ) -> crate::Result<Option<(VaultCommit, ReadEvent)>> {
255        let access_point = self.access_point.lock().await;
256        Ok(access_point.raw_secret(id).await?)
257    }
258
259    /// Update a secret.
260    pub async fn update_secret(
261        &mut self,
262        id: &SecretId,
263        secret_meta: SecretMeta,
264        secret: Secret,
265    ) -> crate::Result<Option<WriteEvent>> {
266        let mut access_point = self.access_point.lock().await;
267        if let Some(event) =
268            access_point.update_secret(id, secret_meta, secret).await?
269        {
270            let mut events = self.events.write().await;
271            events.apply(&[event.clone()]).await?;
272            Ok(Some(event))
273        } else {
274            Ok(None)
275        }
276    }
277
278    /// Delete a secret and it's meta data.
279    pub async fn delete_secret(
280        &mut self,
281        id: &SecretId,
282    ) -> Result<Option<WriteEvent>> {
283        let mut access_point = self.access_point.lock().await;
284        if let Some(event) = access_point.delete_secret(id).await? {
285            let mut events = self.events.write().await;
286            events.apply(&[event.clone()]).await?;
287            Ok(Some(event))
288        } else {
289            Ok(None)
290        }
291    }
292
293    /// Set the name of the folder.
294    pub async fn rename_folder(
295        &mut self,
296        name: impl AsRef<str>,
297    ) -> Result<WriteEvent> {
298        let mut access_point = self.access_point.lock().await;
299        access_point
300            .set_vault_name(name.as_ref().to_owned())
301            .await?;
302        let event = WriteEvent::SetVaultName(name.as_ref().to_owned());
303        let mut events = self.events.write().await;
304        events.apply(&[event.clone()]).await?;
305        Ok(event)
306    }
307
308    /// Set the folder flags.
309    pub async fn update_folder_flags(
310        &mut self,
311        flags: VaultFlags,
312    ) -> Result<WriteEvent> {
313        let mut access_point = self.access_point.lock().await;
314        access_point.set_vault_flags(flags.clone()).await?;
315        let event = WriteEvent::SetVaultFlags(flags);
316        let mut events = self.events.write().await;
317        events.apply(&[event.clone()]).await?;
318        Ok(event)
319    }
320
321    /// Description of this folder.
322    pub async fn description(&self) -> Result<String> {
323        let access_point = self.access_point.lock().await;
324        let meta = access_point.vault_meta().await?;
325        Ok(meta.description().to_owned())
326    }
327
328    /// Set the description of this folder.
329    pub async fn set_description(
330        &mut self,
331        description: impl AsRef<str>,
332    ) -> Result<WriteEvent> {
333        let mut meta = {
334            let access_point = self.access_point.lock().await;
335            access_point.vault_meta().await?
336        };
337        meta.set_description(description.as_ref().to_owned());
338        self.set_meta(&meta).await
339    }
340
341    /// Set the folder meta data.
342    pub async fn set_meta(&mut self, meta: &VaultMeta) -> Result<WriteEvent> {
343        let mut access_point = self.access_point.lock().await;
344        let event = access_point.set_vault_meta(meta).await?;
345        let mut events = self.events.write().await;
346        events.apply(&[event.clone()]).await?;
347        Ok(event)
348    }
349
350    /// Folder commit state.
351    pub async fn commit_state(&self) -> Result<CommitState> {
352        let event_log = self.events.read().await;
353        Ok(event_log.tree().commit_state()?)
354    }
355
356    /// Folder root commit hash.
357    pub async fn root_hash(&self) -> Result<CommitHash> {
358        let event_log = self.events.read().await;
359        Ok(event_log
360            .tree()
361            .root()
362            .ok_or(sos_core::Error::NoRootCommit)?)
363    }
364
365    /// Apply events to the event log.
366    pub async fn apply(&mut self, events: &[WriteEvent]) -> Result<()> {
367        let mut event_log = self.events.write().await;
368        event_log.apply(events).await?;
369        Ok(())
370    }
371
372    /// Apply event records to the event log.
373    pub async fn apply_records(
374        &mut self,
375        records: Vec<EventRecord>,
376    ) -> Result<()> {
377        let mut event_log = self.events.write().await;
378        event_log.apply_records(records).await?;
379        Ok(())
380    }
381
382    /// Clear events from the event log.
383    pub async fn clear(&mut self) -> Result<()> {
384        let mut event_log = self.events.write().await;
385        event_log.clear().await?;
386        Ok(())
387    }
388}
389
390impl From<Folder> for Vault {
391    fn from(value: Folder) -> Self {
392        let mutex = Arc::into_inner(value.access_point).unwrap();
393        let access_point = mutex.into_inner();
394        access_point.into()
395    }
396}