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