sos_client_storage/
secret_storage.rs

1//! Blanket implementation of secret storage trait.
2use crate::traits::private::Internal;
3use crate::{
4    AccessOptions, ClientAccountStorage, ClientBaseStorage,
5    ClientFolderStorage, ClientSecretStorage, Error, Result,
6    StorageChangeEvent,
7};
8use async_trait::async_trait;
9use sos_backend::StorageError;
10use sos_core::{
11    events::{ReadEvent, WriteEvent},
12    AuthenticationError, SecretId, VaultCommit, VaultId,
13};
14use sos_vault::secret::{Secret, SecretMeta, SecretRow};
15use sos_vault::Summary;
16
17#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
18#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
19impl<T> ClientSecretStorage for T
20where
21    T: ClientAccountStorage
22        + ClientFolderStorage
23        + ClientBaseStorage
24        + Send
25        + Sync,
26{
27    async fn create_secret(
28        &mut self,
29        secret_data: SecretRow,
30        #[allow(unused_mut, unused_variables)] mut options: AccessOptions,
31    ) -> Result<StorageChangeEvent> {
32        self.guard_authenticated(Internal)?;
33
34        let summary = if let Some(folder_id) = &options.folder {
35            self.find(|f| f.id() == folder_id)
36                .cloned()
37                .ok_or_else(|| StorageError::FolderNotFound(*folder_id))?
38        } else {
39            self.current_folder().ok_or(Error::NoOpenVault)?
40        };
41
42        #[cfg(feature = "search")]
43        let index_doc = if let Some(index) = self.search_index() {
44            let search = index.search();
45            let index = search.read().await;
46            Some(index.prepare(
47                summary.id(),
48                secret_data.id(),
49                secret_data.meta(),
50                secret_data.secret(),
51            ))
52        } else {
53            None
54        };
55
56        let event = {
57            let folder = self
58                .folders_mut()
59                .get_mut(summary.id())
60                .ok_or(StorageError::FolderNotFound(*summary.id()))?;
61            folder.create_secret(&secret_data).await?
62        };
63
64        #[cfg(feature = "files")]
65        let file_events = {
66            let (file_events, write_update) = self
67                .external_file_manager_mut()
68                .ok_or_else(|| AuthenticationError::NotAuthenticated)?
69                .create_files(
70                    &summary,
71                    secret_data,
72                    &mut options.file_progress,
73                )
74                .await?;
75
76            if let Some((id, secret_data)) = write_update {
77                // Update with new checksum(s)
78                self.write_secret(
79                    &summary,
80                    &id,
81                    secret_data,
82                    false,
83                    Internal,
84                )
85                .await?;
86            }
87
88            file_events
89        };
90
91        let result = StorageChangeEvent {
92            event,
93            #[cfg(feature = "files")]
94            file_events,
95        };
96
97        #[cfg(feature = "files")]
98        self.external_file_manager_mut()
99            .ok_or_else(|| AuthenticationError::NotAuthenticated)?
100            .append_file_mutation_events(&result.file_events)
101            .await?;
102
103        #[cfg(feature = "search")]
104        if let (Some(index), Some(index_doc)) =
105            (self.search_index(), index_doc)
106        {
107            let search = index.search();
108            let mut index = search.write().await;
109            index.commit(index_doc)
110        }
111
112        Ok(result)
113    }
114
115    async fn raw_secret(
116        &self,
117        folder_id: &VaultId,
118        secret_id: &SecretId,
119    ) -> Result<Option<(VaultCommit, ReadEvent)>> {
120        self.guard_authenticated(Internal)?;
121
122        let folder = self
123            .folders()
124            .get(folder_id)
125            .ok_or(StorageError::FolderNotFound(*folder_id))?;
126        Ok(folder.raw_secret(secret_id).await?)
127    }
128
129    async fn read_secret(
130        &self,
131        id: &SecretId,
132        options: &AccessOptions,
133    ) -> Result<(Summary, SecretMeta, Secret, ReadEvent)> {
134        self.guard_authenticated(Internal)?;
135
136        let summary = if let Some(folder_id) = &options.folder {
137            self.find(|f| f.id() == folder_id)
138                .cloned()
139                .ok_or_else(|| StorageError::FolderNotFound(*folder_id))?
140        } else {
141            self.current_folder().ok_or(Error::NoOpenVault)?
142        };
143
144        let folder = self
145            .folders()
146            .get(summary.id())
147            .ok_or(StorageError::FolderNotFound(*summary.id()))?;
148        let result = folder
149            .read_secret(id)
150            .await?
151            .ok_or(Error::SecretNotFound(*id))?;
152        Ok((summary, result.0, result.1, result.2))
153    }
154
155    async fn update_secret(
156        &mut self,
157        secret_id: &SecretId,
158        meta: SecretMeta,
159        secret: Option<Secret>,
160        #[allow(unused_mut, unused_variables)] mut options: AccessOptions,
161    ) -> Result<StorageChangeEvent> {
162        self.guard_authenticated(Internal)?;
163
164        let (folder, old_meta, old_secret, _) =
165            self.read_secret(secret_id, &options).await?;
166        let old_secret_data =
167            SecretRow::new(*secret_id, old_meta, old_secret);
168
169        let secret_data = if let Some(secret) = secret {
170            SecretRow::new(*secret_id, meta, secret)
171        } else {
172            let mut secret_data = old_secret_data.clone();
173            *secret_data.meta_mut() = meta;
174            secret_data
175        };
176
177        let event = self
178            .write_secret(
179                &folder,
180                secret_id,
181                secret_data.clone(),
182                true,
183                Internal,
184            )
185            .await?;
186
187        // Must update the files before moving so checksums are correct
188        #[cfg(feature = "files")]
189        let file_events = {
190            // let folder = self.current_folder().ok_or(Error::NoOpenVault)?;
191            let (file_events, write_update) = self
192                .external_file_manager_mut()
193                .ok_or_else(|| AuthenticationError::NotAuthenticated)?
194                .update_files(
195                    &folder,
196                    &folder,
197                    &old_secret_data,
198                    secret_data,
199                    &mut options.file_progress,
200                )
201                .await?;
202
203            if let Some((id, secret_data)) = write_update {
204                // Update with new checksum(s)
205                self.write_secret(&folder, &id, secret_data, false, Internal)
206                    .await?;
207            }
208
209            file_events
210        };
211
212        let result = StorageChangeEvent {
213            event,
214            #[cfg(feature = "files")]
215            file_events,
216        };
217
218        #[cfg(feature = "files")]
219        self.external_file_manager_mut()
220            .ok_or_else(|| AuthenticationError::NotAuthenticated)?
221            .append_file_mutation_events(&result.file_events)
222            .await?;
223
224        Ok(result)
225    }
226
227    async fn write_secret(
228        &mut self,
229        folder: &Summary,
230        id: &SecretId,
231        mut secret_data: SecretRow,
232        #[allow(unused_variables)] is_update: bool,
233        _: Internal,
234    ) -> Result<WriteEvent> {
235        // let summary = self.current_folder().ok_or(Error::NoOpenVault)?;
236
237        secret_data.meta_mut().touch();
238
239        #[cfg(feature = "search")]
240        let index_doc = if let Some(index) = self.search_index() {
241            let search = index.search();
242            let mut index = search.write().await;
243
244            if is_update {
245                // Must remove from the index before we
246                // prepare a new document otherwise the
247                // document would be stale as `prepare()`
248                // and `commit()` are for new documents
249                index.remove(folder.id(), id);
250            }
251
252            Some(index.prepare(
253                folder.id(),
254                id,
255                secret_data.meta(),
256                secret_data.secret(),
257            ))
258        } else {
259            None
260        };
261
262        let event = {
263            let folder = self
264                .folders_mut()
265                .get_mut(folder.id())
266                .ok_or(StorageError::FolderNotFound(*folder.id()))?;
267            let (_, meta, secret) = secret_data.into();
268            folder
269                .update_secret(id, meta, secret)
270                .await?
271                .ok_or(Error::SecretNotFound(*id))?
272        };
273
274        #[cfg(feature = "search")]
275        if let (Some(index), Some(index_doc)) =
276            (self.search_index(), index_doc)
277        {
278            let search = index.search();
279            let mut index = search.write().await;
280            index.commit(index_doc)
281        }
282
283        Ok(event)
284    }
285
286    async fn delete_secret(
287        &mut self,
288        secret_id: &SecretId,
289        #[allow(unused_mut, unused_variables)] mut options: AccessOptions,
290    ) -> Result<StorageChangeEvent> {
291        self.guard_authenticated(Internal)?;
292
293        #[cfg(feature = "files")]
294        let (folder, secret_data) = {
295            let (folder, meta, secret, _) =
296                self.read_secret(secret_id, &options).await?;
297            (folder, SecretRow::new(*secret_id, meta, secret))
298        };
299
300        let event = self.remove_secret(secret_id, &options).await?;
301
302        let result = StorageChangeEvent {
303            event,
304            // Must update the files before moving so
305            // checksums are correct
306            #[cfg(feature = "files")]
307            file_events: {
308                self.external_file_manager_mut()
309                    .ok_or_else(|| AuthenticationError::NotAuthenticated)?
310                    .delete_files(
311                        &folder,
312                        &secret_data,
313                        None,
314                        &mut options.file_progress,
315                    )
316                    .await?
317            },
318        };
319
320        #[cfg(feature = "files")]
321        self.external_file_manager_mut()
322            .ok_or_else(|| AuthenticationError::NotAuthenticated)?
323            .append_file_mutation_events(&result.file_events)
324            .await?;
325
326        Ok(result)
327    }
328
329    async fn remove_secret(
330        &mut self,
331        id: &SecretId,
332        options: &AccessOptions,
333    ) -> Result<WriteEvent> {
334        self.guard_authenticated(Internal)?;
335
336        let summary = if let Some(folder_id) = &options.folder {
337            self.find(|f| f.id() == folder_id)
338                .cloned()
339                .ok_or_else(|| StorageError::FolderNotFound(*folder_id))?
340        } else {
341            self.current_folder().ok_or(Error::NoOpenVault)?
342        };
343
344        let event = {
345            let folder = self
346                .folders_mut()
347                .get_mut(summary.id())
348                .ok_or(StorageError::FolderNotFound(*summary.id()))?;
349            folder
350                .delete_secret(id)
351                .await?
352                .ok_or(Error::SecretNotFound(*id))?
353        };
354
355        #[cfg(feature = "search")]
356        if let Some(index) = self.search_index() {
357            let search = index.search();
358            let mut writer = search.write().await;
359            writer.remove(summary.id(), id);
360        }
361
362        Ok(event)
363    }
364}