sos_database/
vault_writer.rs

1//! Write vault changes to a database.
2use crate::{
3    entity::{FolderEntity, FolderRecord, SecretRecord, SecretRow},
4    Error,
5};
6use async_sqlite::Client;
7use async_trait::async_trait;
8use sos_core::{
9    commit::CommitHash,
10    crypto::AeadPack,
11    encode,
12    events::{ReadEvent, WriteEvent},
13    SecretId, VaultCommit, VaultEntry, VaultFlags, VaultId,
14};
15use sos_vault::{EncryptedEntry, Summary, Vault};
16use std::borrow::Cow;
17
18/// Write changes to a vault in the database.
19pub struct VaultDatabaseWriter<E>
20where
21    E: std::error::Error
22        + std::fmt::Debug
23        + From<sos_core::Error>
24        + From<sos_vault::Error>
25        + From<Error>
26        + Send
27        + Sync
28        + 'static,
29{
30    client: Client,
31    folder_id: VaultId,
32    marker: std::marker::PhantomData<E>,
33}
34
35impl<E> VaultDatabaseWriter<E>
36where
37    E: std::error::Error
38        + std::fmt::Debug
39        + From<sos_core::Error>
40        + From<sos_vault::Error>
41        + From<Error>
42        + Send
43        + Sync
44        + 'static,
45{
46    /// Create a new vault database writer.
47    pub fn new(client: Client, folder_id: VaultId) -> Self {
48        Self {
49            client,
50            folder_id,
51            marker: std::marker::PhantomData,
52        }
53    }
54}
55
56#[async_trait]
57impl<E> EncryptedEntry for VaultDatabaseWriter<E>
58where
59    E: std::error::Error
60        + std::fmt::Debug
61        + From<sos_core::Error>
62        + From<sos_vault::Error>
63        + From<Error>
64        + Send
65        + Sync
66        + 'static,
67{
68    type Error = E;
69
70    async fn summary(&self) -> Result<Summary, Self::Error> {
71        let folder_id = self.folder_id;
72        let row = self
73            .client
74            .conn(move |conn| {
75                let folder = FolderEntity::new(&conn);
76                let row = folder.find_optional(&folder_id)?;
77                Ok(row)
78            })
79            .await
80            .map_err(Error::from)?;
81        let row = row.ok_or(Error::DatabaseFolderNotFound(folder_id))?;
82        let record = FolderRecord::from_row(row).await?;
83        Ok(record.summary)
84    }
85
86    async fn vault_name(&self) -> Result<Cow<'_, str>, Self::Error> {
87        let summary = self.summary().await?;
88        Ok(Cow::Owned(summary.name().to_string()))
89    }
90
91    async fn set_vault_name(
92        &mut self,
93        name: String,
94    ) -> Result<WriteEvent, Self::Error> {
95        let folder_id = self.folder_id;
96        let folder_name = name.clone();
97        self.client
98            .conn_and_then(move |conn| {
99                let folder = FolderEntity::new(&conn);
100                folder.update_name(&folder_id, &folder_name)
101            })
102            .await?;
103        Ok(WriteEvent::SetVaultName(name))
104    }
105
106    async fn set_vault_flags(
107        &mut self,
108        flags: VaultFlags,
109    ) -> Result<WriteEvent, Self::Error> {
110        let folder_id = self.folder_id;
111        let folder_flags = flags.clone();
112        self.client
113            .conn_and_then(move |conn| {
114                let folder = FolderEntity::new(&conn);
115                folder.update_flags(&folder_id, &folder_flags)
116            })
117            .await?;
118        Ok(WriteEvent::SetVaultFlags(flags))
119    }
120
121    async fn set_vault_meta(
122        &mut self,
123        meta_data: AeadPack,
124    ) -> Result<WriteEvent, Self::Error> {
125        let folder_id = self.folder_id;
126        let folder_meta = encode(&meta_data).await?;
127        self.client
128            .conn_and_then(move |conn| {
129                let folder = FolderEntity::new(&conn);
130                folder.update_meta(&folder_id, folder_meta.as_slice())
131            })
132            .await?;
133        Ok(WriteEvent::SetVaultMeta(meta_data))
134    }
135
136    async fn create_secret(
137        &mut self,
138        commit: CommitHash,
139        secret: VaultEntry,
140    ) -> Result<WriteEvent, Self::Error> {
141        self.insert_secret(SecretId::new_v4(), commit, secret).await
142    }
143
144    async fn insert_secret(
145        &mut self,
146        secret_id: SecretId,
147        commit: CommitHash,
148        secret: VaultEntry,
149    ) -> Result<WriteEvent, Self::Error> {
150        let folder_id = self.folder_id;
151        let secret_row = SecretRow::new(&secret_id, &commit, &secret).await?;
152        self.client
153            .conn(move |conn| {
154                let folder = FolderEntity::new(&conn);
155                folder.insert_secret(&folder_id, &secret_row)?;
156                Ok(())
157            })
158            .await
159            .map_err(Error::from)?;
160        Ok(WriteEvent::CreateSecret(
161            secret_id,
162            VaultCommit(commit, secret),
163        ))
164    }
165
166    async fn read_secret<'a>(
167        &'a self,
168        secret_id: &SecretId,
169    ) -> Result<Option<(Cow<'a, VaultCommit>, ReadEvent)>, Self::Error> {
170        let folder_id = self.folder_id;
171        let folder_secret_id = *secret_id;
172        let secret_row = self
173            .client
174            .conn(move |conn| {
175                let folder = FolderEntity::new(&conn);
176                folder.find_secret(&folder_id, &folder_secret_id)
177            })
178            .await
179            .map_err(Error::from)?;
180
181        let event = ReadEvent::ReadSecret(*secret_id);
182        if let Some(row) = secret_row {
183            let record = SecretRecord::from_row(row).await?;
184            Ok(Some((Cow::Owned(record.commit), event)))
185        } else {
186            Ok(None)
187        }
188    }
189
190    async fn update_secret(
191        &mut self,
192        secret_id: &SecretId,
193        commit: CommitHash,
194        secret: VaultEntry,
195    ) -> Result<Option<WriteEvent>, Self::Error> {
196        let folder_id = self.folder_id;
197        let secret_row = SecretRow::new(secret_id, &commit, &secret).await?;
198        let updated = self
199            .client
200            .conn_and_then(move |conn| {
201                let folder = FolderEntity::new(&conn);
202                folder.update_secret(&folder_id, &secret_row)
203            })
204            .await?;
205        Ok(updated.then_some(WriteEvent::UpdateSecret(
206            *secret_id,
207            VaultCommit(commit, secret),
208        )))
209    }
210
211    async fn delete_secret(
212        &mut self,
213        secret_id: &SecretId,
214    ) -> Result<Option<WriteEvent>, Self::Error> {
215        let folder_id = self.folder_id;
216        let folder_secret_id = *secret_id;
217        let deleted = self
218            .client
219            .conn(move |conn| {
220                let folder = FolderEntity::new(&conn);
221                folder.delete_secret(&folder_id, &folder_secret_id)
222            })
223            .await
224            .map_err(Error::from)?;
225        Ok(deleted.then_some(WriteEvent::DeleteSecret(*secret_id)))
226    }
227
228    async fn replace_vault(
229        &mut self,
230        vault: &Vault,
231    ) -> Result<(), Self::Error> {
232        Ok(FolderEntity::replace_all_secrets(
233            self.client.clone(),
234            &self.folder_id,
235            vault,
236        )
237        .await?)
238    }
239}