core_crypto_keystore/connection/
mod.rs1pub mod platform {
18 cfg_if::cfg_if! {
19 if #[cfg(target_family = "wasm")] {
20 mod wasm;
21 pub use wasm::keystore_v_1_0_0;
22 pub use self::wasm::WasmConnection as KeystoreDatabaseConnection;
23 pub use wasm::storage;
24 pub use self::wasm::storage::WasmStorageTransaction as TransactionWrapper;
25 } else {
26 mod generic;
27 pub use self::generic::SqlCipherConnection as KeystoreDatabaseConnection;
28 pub use self::generic::TransactionWrapper;
29 }
30 }
31}
32
33pub use self::platform::*;
34use crate::entities::{Entity, EntityFindParams, StringEntityId};
35use std::ops::DerefMut;
36
37use crate::entities::{EntityTransactionExt, UniqueEntity};
38use crate::transaction::KeystoreTransaction;
39use crate::{CryptoKeystoreError, CryptoKeystoreResult};
40use async_lock::{Mutex, MutexGuard, Semaphore};
41use std::sync::Arc;
42
43pub const MAX_BLOB_LEN: usize = 1_000_000_000;
51
52#[cfg(not(target_family = "wasm"))]
53pub trait DatabaseConnectionRequirements: Sized + Send {}
55#[cfg(target_family = "wasm")]
56pub trait DatabaseConnectionRequirements: Sized {}
58
59#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
60#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
61pub trait DatabaseConnection<'a>: DatabaseConnectionRequirements {
62 type Connection: 'a;
63
64 async fn open(name: &str, key: &str) -> CryptoKeystoreResult<Self>;
65
66 async fn open_in_memory(name: &str, key: &str) -> CryptoKeystoreResult<Self>;
67
68 async fn close(self) -> CryptoKeystoreResult<()>;
69
70 async fn wipe(self) -> CryptoKeystoreResult<()> {
72 self.close().await
73 }
74
75 fn check_buffer_size(size: usize) -> CryptoKeystoreResult<()> {
76 #[cfg(not(target_family = "wasm"))]
77 if size > i32::MAX as usize {
78 return Err(CryptoKeystoreError::BlobTooBig);
79 }
80
81 if size >= MAX_BLOB_LEN {
82 return Err(CryptoKeystoreError::BlobTooBig);
83 }
84
85 Ok(())
86 }
87}
88
89#[derive(Debug, Clone)]
90pub struct Connection {
91 pub(crate) conn: Arc<Mutex<KeystoreDatabaseConnection>>,
92 pub(crate) transaction: Arc<Mutex<Option<KeystoreTransaction>>>,
93 transaction_semaphore: Arc<Semaphore>,
94}
95
96const ALLOWED_CONCURRENT_TRANSACTIONS_COUNT: usize = 1;
97
98#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
101#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
102pub trait FetchFromDatabase: Send + Sync {
103 async fn find<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
104 &self,
105 id: &[u8],
106 ) -> CryptoKeystoreResult<Option<E>>;
107
108 async fn find_unique<U: UniqueEntity<ConnectionType = KeystoreDatabaseConnection>>(
109 &self,
110 ) -> CryptoKeystoreResult<U>;
111
112 async fn find_all<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
113 &self,
114 params: EntityFindParams,
115 ) -> CryptoKeystoreResult<Vec<E>>;
116
117 async fn find_many<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
118 &self,
119 ids: &[Vec<u8>],
120 ) -> CryptoKeystoreResult<Vec<E>>;
121 async fn count<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(&self) -> CryptoKeystoreResult<usize>;
122}
123
124unsafe impl Send for Connection {}
126unsafe impl Sync for Connection {}
128
129impl Connection {
130 pub async fn open_with_key(name: impl AsRef<str>, key: impl AsRef<str>) -> CryptoKeystoreResult<Self> {
131 let conn = KeystoreDatabaseConnection::open(name.as_ref(), key.as_ref())
132 .await?
133 .into();
134 #[allow(clippy::arc_with_non_send_sync)] let conn = Arc::new(conn);
136 Ok(Self {
137 conn,
138 transaction: Default::default(),
139 transaction_semaphore: Arc::new(Semaphore::new(ALLOWED_CONCURRENT_TRANSACTIONS_COUNT)),
140 })
141 }
142
143 pub async fn open_in_memory_with_key(name: impl AsRef<str>, key: impl AsRef<str>) -> CryptoKeystoreResult<Self> {
144 let conn = KeystoreDatabaseConnection::open_in_memory(name.as_ref(), key.as_ref())
145 .await?
146 .into();
147 #[allow(clippy::arc_with_non_send_sync)] let conn = Arc::new(conn);
149 Ok(Self {
150 conn,
151 transaction: Default::default(),
152 transaction_semaphore: Arc::new(Semaphore::new(ALLOWED_CONCURRENT_TRANSACTIONS_COUNT)),
153 })
154 }
155
156 pub async fn borrow_conn(&self) -> CryptoKeystoreResult<MutexGuard<'_, KeystoreDatabaseConnection>> {
157 Ok(self.conn.lock().await)
158 }
159
160 pub async fn wipe(self) -> CryptoKeystoreResult<()> {
161 if self.transaction.lock().await.is_some() {
162 return Err(CryptoKeystoreError::TransactionInProgress {
163 attempted_operation: "wipe()".to_string(),
164 });
165 }
166 let conn: KeystoreDatabaseConnection = Arc::into_inner(self.conn).unwrap().into_inner();
167 conn.wipe().await?;
168 Ok(())
169 }
170
171 pub async fn close(self) -> CryptoKeystoreResult<()> {
172 if self.transaction.lock().await.is_some() {
173 return Err(CryptoKeystoreError::TransactionInProgress {
174 attempted_operation: "close()".to_string(),
175 });
176 }
177 let conn: KeystoreDatabaseConnection = Arc::into_inner(self.conn).unwrap().into_inner();
178 conn.close().await?;
179 Ok(())
180 }
181
182 pub async fn new_transaction(&self) -> CryptoKeystoreResult<()> {
184 let semaphore = self.transaction_semaphore.acquire_arc().await;
185 let mut transaction_guard = self.transaction.lock().await;
186 *transaction_guard = Some(KeystoreTransaction::new(semaphore).await?);
187 Ok(())
188 }
189
190 pub async fn commit_transaction(&self) -> CryptoKeystoreResult<()> {
191 let mut transaction_guard = self.transaction.lock().await;
192 let Some(transaction) = transaction_guard.as_ref() else {
193 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
194 };
195 transaction.commit(self).await?;
196 *transaction_guard = None;
197 Ok(())
198 }
199
200 pub async fn rollback_transaction(&self) -> CryptoKeystoreResult<()> {
201 let mut transaction_guard = self.transaction.lock().await;
202 if transaction_guard.is_none() {
203 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
204 };
205 *transaction_guard = None;
206 Ok(())
207 }
208
209 pub async fn child_groups<
210 E: Entity<ConnectionType = KeystoreDatabaseConnection> + crate::entities::PersistedMlsGroupExt + Sync,
211 >(
212 &self,
213 entity: E,
214 ) -> CryptoKeystoreResult<Vec<E>> {
215 let mut conn = self.conn.lock().await;
216 let persisted_records = entity.child_groups(conn.deref_mut()).await?;
217
218 let transaction_guard = self.transaction.lock().await;
219 let Some(transaction) = transaction_guard.as_ref() else {
220 return Ok(persisted_records);
221 };
222 transaction.child_groups(entity, persisted_records).await
223 }
224
225 pub async fn save<E: Entity<ConnectionType = KeystoreDatabaseConnection> + Sync + EntityTransactionExt>(
226 &self,
227 entity: E,
228 ) -> CryptoKeystoreResult<E> {
229 let transaction_guard = self.transaction.lock().await;
230 let Some(transaction) = transaction_guard.as_ref() else {
231 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
232 };
233 transaction.save_mut(entity).await
234 }
235
236 pub async fn remove<
237 E: Entity<ConnectionType = KeystoreDatabaseConnection> + EntityTransactionExt,
238 S: AsRef<[u8]>,
239 >(
240 &self,
241 id: S,
242 ) -> CryptoKeystoreResult<()> {
243 let transaction_guard = self.transaction.lock().await;
244 let Some(transaction) = transaction_guard.as_ref() else {
245 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
246 };
247 transaction.remove::<E, S>(id).await
248 }
249
250 pub async fn cred_delete_by_credential(&self, cred: Vec<u8>) -> CryptoKeystoreResult<()> {
251 let transaction_guard = self.transaction.lock().await;
252 let Some(transaction) = transaction_guard.as_ref() else {
253 return Err(CryptoKeystoreError::MutatingOperationWithoutTransaction);
254 };
255 transaction.cred_delete_by_credential(cred).await
256 }
257}
258
259#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
260#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
261impl FetchFromDatabase for Connection {
262 async fn find<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
263 &self,
264 id: &[u8],
265 ) -> CryptoKeystoreResult<Option<E>> {
266 if let Some(transaction) = self.transaction.lock().await.as_ref() {
268 if let Some(result) = transaction.find::<E>(id).await? {
270 return Ok(result);
272 }
273 }
274
275 let mut conn = self.conn.lock().await;
277 E::find_one(&mut conn, &id.into()).await
278 }
279
280 async fn find_unique<U: UniqueEntity>(&self) -> CryptoKeystoreResult<U> {
281 if let Some(transaction) = self.transaction.lock().await.as_ref() {
283 if let Some(result) = transaction.find_unique::<U>().await? {
285 return Ok(result);
287 }
288 }
289 let mut conn = self.conn.lock().await;
291 U::find_unique(&mut conn).await
292 }
293
294 async fn find_all<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
295 &self,
296 params: EntityFindParams,
297 ) -> CryptoKeystoreResult<Vec<E>> {
298 let mut conn = self.conn.lock().await;
299 let persisted_records = E::find_all(&mut conn, params.clone()).await?;
300
301 let transaction_guard = self.transaction.lock().await;
302 let Some(transaction) = transaction_guard.as_ref() else {
303 return Ok(persisted_records);
304 };
305 transaction.find_all(persisted_records, params).await
306 }
307
308 async fn find_many<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(
309 &self,
310 ids: &[Vec<u8>],
311 ) -> CryptoKeystoreResult<Vec<E>> {
312 let entity_ids: Vec<StringEntityId> = ids.iter().map(|id| id.as_slice().into()).collect();
313 let mut conn = self.conn.lock().await;
314 let persisted_records = E::find_many(&mut conn, &entity_ids).await?;
315
316 let transaction_guard = self.transaction.lock().await;
317 let Some(transaction) = transaction_guard.as_ref() else {
318 return Ok(persisted_records);
319 };
320 transaction.find_many(persisted_records, ids).await
321 }
322
323 async fn count<E: Entity<ConnectionType = KeystoreDatabaseConnection>>(&self) -> CryptoKeystoreResult<usize> {
324 if self.transaction.lock().await.is_some() {
325 return Ok(self.find_all::<E>(Default::default()).await?.len());
328 };
329 let mut conn = self.conn.lock().await;
330 E::count(&mut conn).await
331 }
332}