1use crate::{Error, Result};
2use async_sqlite::rusqlite::{
3 CachedStatement, Connection, Error as SqlError, OptionalExtension, Row,
4 Transaction,
5};
6use async_sqlite::Client;
7use sos_core::crypto::Seed;
8use sos_core::{
9 commit::CommitHash, crypto::AeadPack, decode, encode, SecretId,
10 UtcDateTime, VaultCommit, VaultEntry, VaultFlags, VaultId,
11};
12use sos_vault::{Summary, Vault};
13use sql_query_builder as sql;
14use std::collections::HashMap;
15use std::ops::Deref;
16use std::result::Result as StdResult;
17
18fn folder_select_columns(sql: sql::Select) -> sql::Select {
19 sql.select(
20 r#"
21 folders.folder_id,
22 folders.created_at,
23 folders.modified_at,
24 folders.identifier,
25 folders.name,
26 folders.salt,
27 folders.meta,
28 folders.seed,
29 folders.version,
30 folders.cipher,
31 folders.kdf,
32 folders.flags
33 "#,
34 )
35}
36
37fn secret_select_columns(sql: sql::Select) -> sql::Select {
38 sql.select(
39 r#"
40 secret_id,
41 created_at,
42 modified_at,
43 identifier,
44 commit_hash,
45 meta,
46 secret
47 "#,
48 )
49}
50
51#[doc(hidden)]
53#[derive(Debug, Default)]
54pub struct FolderRow {
55 pub row_id: i64,
56 created_at: String,
57 modified_at: String,
58 identifier: String,
59 name: String,
60 salt: Option<String>,
61 meta: Option<Vec<u8>>,
62 seed: Option<Vec<u8>>,
63 version: i64,
64 cipher: String,
65 kdf: String,
66 flags: Vec<u8>,
67}
68
69impl FolderRow {
70 pub async fn new_insert(vault: &Vault) -> Result<Self> {
72 let meta = if let Some(meta) = vault.header().meta() {
73 Some(encode(meta).await?)
74 } else {
75 None
76 };
77 let salt = vault.salt().cloned();
78 let seed = vault.seed().map(|s| s.as_ref().to_vec());
79 Self::new_insert_parts(vault.summary(), salt, meta, seed)
80 }
81
82 pub fn new_insert_parts(
84 summary: &Summary,
85 salt: Option<String>,
86 meta: Option<Vec<u8>>,
87 seed: Option<Vec<u8>>,
88 ) -> Result<Self> {
89 Ok(Self {
90 created_at: UtcDateTime::default().to_rfc3339()?,
91 modified_at: UtcDateTime::default().to_rfc3339()?,
92 identifier: summary.id().to_string(),
93 name: summary.name().to_string(),
94 salt,
95 meta,
96 seed,
97 version: *summary.version() as i64,
98 cipher: summary.cipher().to_string(),
99 kdf: summary.kdf().to_string(),
100 flags: summary.flags().bits().to_le_bytes().to_vec(),
101 ..Default::default()
102 })
103 }
104
105 pub async fn new_update(vault: &Vault) -> Result<Self> {
107 let summary = vault.summary();
108 let meta = if let Some(meta) = vault.header().meta() {
109 Some(encode(meta).await?)
110 } else {
111 None
112 };
113 let salt = vault.salt().cloned();
114 let seed = vault.seed().map(|s| s.as_ref().to_vec());
115 Ok(Self {
116 modified_at: UtcDateTime::default().to_rfc3339()?,
117 identifier: summary.id().to_string(),
118 name: summary.name().to_string(),
119 salt,
120 meta,
121 seed,
122 version: *summary.version() as i64,
123 cipher: summary.cipher().to_string(),
124 kdf: summary.kdf().to_string(),
125 flags: summary.flags().bits().to_le_bytes().to_vec(),
126 ..Default::default()
127 })
128 }
129}
130
131impl<'a> TryFrom<&Row<'a>> for FolderRow {
132 type Error = SqlError;
133 fn try_from(row: &Row<'a>) -> StdResult<Self, Self::Error> {
134 Ok(FolderRow {
135 row_id: row.get(0)?,
136 created_at: row.get(1)?,
137 modified_at: row.get(2)?,
138 identifier: row.get(3)?,
139 name: row.get(4)?,
140 salt: row.get(5)?,
141 meta: row.get(6)?,
142 seed: row.get(7)?,
143 version: row.get(8)?,
144 cipher: row.get(9)?,
145 kdf: row.get(10)?,
146 flags: row.get(11)?,
147 })
148 }
149}
150
151#[derive(Debug, Clone)]
153pub struct FolderRecord {
154 pub row_id: i64,
156 pub created_at: UtcDateTime,
158 pub modified_at: UtcDateTime,
160 pub salt: Option<String>,
162 pub meta: Option<AeadPack>,
164 pub seed: Option<Seed>,
166 pub summary: Summary,
168}
169
170impl FolderRecord {
171 pub async fn from_row(value: FolderRow) -> Result<Self> {
173 let created_at = UtcDateTime::parse_rfc3339(&value.created_at)?;
174 let modified_at = UtcDateTime::parse_rfc3339(&value.modified_at)?;
175 let folder_id: VaultId = value.identifier.parse()?;
176 let version: u16 = value.version.try_into()?;
177 let cipher = value.cipher.parse()?;
178 let kdf = value.kdf.parse()?;
179 let bytes: [u8; 8] = value.flags.as_slice().try_into()?;
180 let bits = u64::from_le_bytes(bytes);
181 let flags = VaultFlags::from_bits(bits)
182 .ok_or(sos_vault::Error::InvalidVaultFlags)?;
183
184 let salt = value.salt;
185
186 let meta = if let Some(meta) = &value.meta {
187 Some(decode(meta).await?)
188 } else {
189 None
190 };
191
192 let seed = if let Some(seed) = value.seed {
193 let seed: [u8; Seed::SIZE] = seed.as_slice().try_into()?;
194 Some(Seed(seed))
195 } else {
196 None
197 };
198
199 let summary =
200 Summary::new(version, folder_id, value.name, cipher, kdf, flags);
201
202 Ok(FolderRecord {
203 row_id: value.row_id,
204 created_at,
205 modified_at,
206 salt,
207 meta,
208 seed,
209 summary,
210 })
211 }
212
213 pub fn into_vault(&self) -> Result<Vault> {
215 let mut vault: Vault = self.summary.clone().into();
216 vault.header_mut().set_meta(self.meta.clone());
217 vault.header_mut().set_salt(self.salt.clone());
218 vault.header_mut().set_seed(self.seed);
219 Ok(vault)
220 }
221}
222
223#[doc(hidden)]
225#[derive(Debug, Default)]
226pub struct SecretRow {
227 pub row_id: i64,
228 created_at: String,
229 modified_at: String,
230 identifier: String,
231 commit: Vec<u8>,
232 meta: Vec<u8>,
233 secret: Vec<u8>,
234}
235
236impl SecretRow {
237 pub async fn new(
239 secret_id: &SecretId,
240 commit: &CommitHash,
241 entry: &VaultEntry,
242 ) -> Result<Self> {
243 let VaultEntry(meta, secret) = entry;
244 let meta = encode(meta).await?;
245 let secret = encode(secret).await?;
246 Ok(Self {
247 created_at: UtcDateTime::default().to_rfc3339()?,
248 modified_at: UtcDateTime::default().to_rfc3339()?,
249 identifier: secret_id.to_string(),
250 commit: commit.as_ref().to_vec(),
251 meta,
252 secret,
253 ..Default::default()
254 })
255 }
256
257 pub fn identifier(&self) -> &str {
259 &self.identifier
260 }
261
262 pub fn commit(&self) -> &[u8] {
264 &self.commit
265 }
266
267 pub fn meta_bytes(&self) -> &[u8] {
269 &self.meta
270 }
271
272 pub fn secret_bytes(&self) -> &[u8] {
274 &self.secret
275 }
276}
277
278impl<'a> TryFrom<&Row<'a>> for SecretRow {
279 type Error = SqlError;
280 fn try_from(row: &Row<'a>) -> StdResult<Self, Self::Error> {
281 Ok(SecretRow {
282 row_id: row.get(0)?,
283 created_at: row.get(1)?,
284 modified_at: row.get(2)?,
285 identifier: row.get(3)?,
286 commit: row.get(4)?,
287 meta: row.get(5)?,
288 secret: row.get(6)?,
289 })
290 }
291}
292
293#[doc(hidden)]
295#[derive(Debug)]
296pub struct SecretRecord {
297 pub row_id: i64,
298 pub created_at: UtcDateTime,
299 pub modified_at: UtcDateTime,
300 pub secret_id: VaultId,
301 pub commit: VaultCommit,
302}
303
304impl SecretRecord {
305 pub async fn from_row(value: SecretRow) -> Result<Self> {
307 let created_at = UtcDateTime::parse_rfc3339(&value.created_at)?;
308 let modified_at = UtcDateTime::parse_rfc3339(&value.modified_at)?;
309 let secret_id: SecretId = value.identifier.parse()?;
310 let commit_hash = CommitHash(value.commit.as_slice().try_into()?);
311 let meta: AeadPack = decode(&value.meta).await?;
312 let secret: AeadPack = decode(&value.secret).await?;
313 let commit = VaultCommit(commit_hash, VaultEntry(meta, secret));
314
315 Ok(SecretRecord {
316 row_id: value.row_id,
317 created_at,
318 modified_at,
319 secret_id,
320 commit,
321 })
322 }
323}
324
325pub struct FolderEntity<'conn, C>
327where
328 C: Deref<Target = Connection>,
329{
330 conn: &'conn C,
331}
332
333impl<'conn> FolderEntity<'conn, Box<Connection>> {
334 pub fn find_all_secrets_query() -> sql::Select {
336 secret_select_columns(sql::Select::new())
337 .from("folder_secrets")
338 .where_clause("folder_id=?1")
339 }
340
341 pub async fn compute_folder_vault(
343 client: &Client,
344 folder_id: &VaultId,
345 ) -> Result<Vault> {
346 let folder_id = *folder_id;
347
348 let (folder_row, secret_rows) = client
349 .conn_and_then(move |conn| {
350 let folder_entity = FolderEntity::new(&conn);
351 let folder_row = folder_entity.find_one(&folder_id)?;
352 let secret_rows =
353 folder_entity.load_secrets(folder_row.row_id)?;
354 Ok::<_, Error>((folder_row, secret_rows))
355 })
356 .await?;
357
358 let folder_record = FolderRecord::from_row(folder_row).await?;
359 let mut vault = folder_record.into_vault()?;
360 for row in secret_rows {
361 let record = SecretRecord::from_row(row).await?;
362 vault.insert_entry(record.secret_id, record.commit);
363 }
364 Ok(vault)
365 }
366}
367
368impl<'conn> FolderEntity<'conn, Transaction<'conn>> {
369 pub async fn upsert_folder_and_secrets(
375 client: &Client,
376 account_id: i64,
377 vault: &Vault,
378 ) -> Result<(i64, HashMap<SecretId, i64>)> {
379 let folder_id = *vault.id();
380
381 let meta = if let Some(meta) = vault.header().meta() {
382 Some(encode(meta).await?)
383 } else {
384 None
385 };
386 let salt = vault.salt().cloned();
387 let seed = vault.seed().map(|s| s.as_ref().to_vec());
388
389 let folder_row =
390 FolderRow::new_insert_parts(vault.summary(), salt, meta, seed)?;
391
392 let mut secret_rows = Vec::new();
393 for (secret_id, commit) in vault.iter() {
394 let VaultCommit(commit, entry) = commit;
395 secret_rows.push(SecretRow::new(secret_id, commit, entry).await?);
396 }
397
398 client
399 .conn_mut_and_then(move |conn| {
400 let tx = conn.transaction()?;
401 let folder_entity = FolderEntity::new(&tx);
402
403 let folder_id = if let Some(row) =
404 folder_entity.find_optional(&folder_id)?
405 {
406 folder_entity.update_folder(&folder_id, &folder_row)?;
407 folder_entity.delete_all_secrets(row.row_id)?;
408 row.row_id
409 } else {
410 folder_entity.insert_folder(account_id, &folder_row)?
411 };
412
413 let secret_ids = folder_entity.insert_folder_secrets(
414 folder_id,
415 secret_rows.as_slice(),
416 )?;
417 tx.commit()?;
418 Ok::<_, Error>((folder_id, secret_ids))
419 })
420 .await
421 }
422
423 pub async fn replace_all_secrets(
425 client: Client,
426 folder_id: &VaultId,
427 vault: &Vault,
428 ) -> Result<()> {
429 let folder_id = *folder_id;
430 let mut insert_secrets = Vec::new();
431 for (secret_id, commit) in vault.iter() {
432 let VaultCommit(commit, entry) = commit;
433 insert_secrets
434 .push(SecretRow::new(secret_id, commit, entry).await?);
435 }
436
437 let folder_update_row = FolderRow::new_update(vault).await?;
438 client
439 .conn_mut(move |conn| {
440 let tx = conn.transaction()?;
441 let folder = FolderEntity::new(&tx);
442 let folder_row = folder.find_one(&folder_id)?;
443 folder.delete_all_secrets(folder_row.row_id)?;
444 for secret_row in insert_secrets {
445 folder.insert_secret_by_row_id(
446 folder_row.row_id,
447 &secret_row,
448 )?;
449 }
450 folder.update_folder(&folder_id, &folder_update_row)?;
451 tx.commit()?;
452 Ok(())
453 })
454 .await
455 .map_err(Error::from)?;
456 Ok(())
457 }
458}
459
460impl<'conn, C> FolderEntity<'conn, C>
461where
462 C: Deref<Target = Connection>,
463{
464 pub fn new(conn: &'conn C) -> Self {
466 Self { conn }
467 }
468
469 fn select_folder<'a>(
470 &'a self,
471 use_identifier: bool,
472 ) -> StdResult<CachedStatement<'a>, SqlError> {
473 let query = folder_select_columns(sql::Select::new()).from("folders");
474
475 let query = if use_identifier {
476 query.where_clause("identifier = ?1")
477 } else {
478 query.where_clause("folder_id = ?1")
479 };
480 self.conn.prepare_cached(&query.as_string())
481 }
482
483 pub fn find_one(
485 &self,
486 folder_id: &VaultId,
488 ) -> StdResult<FolderRow, SqlError> {
489 let mut stmt = self.select_folder(true)?;
490 stmt
491 .query_row([folder_id.to_string()], |row| row.try_into())
492 }
493
494 pub fn find_optional(
496 &self,
497 folder_id: &VaultId,
499 ) -> StdResult<Option<FolderRow>, SqlError> {
500 let mut stmt = self.select_folder(true)?;
501 stmt
502 .query_row([folder_id.to_string()], |row| {
503 let row: FolderRow = row.try_into()?;
504 Ok(row)
505 })
506 .optional()
507 }
508
509 pub fn find_by_row_id(
511 &self,
512 folder_id: i64,
513 ) -> StdResult<FolderRow, SqlError> {
514 let mut stmt = self.select_folder(false)?;
515 stmt.query_row([folder_id], |row| row.try_into())
516 }
517
518 pub fn find_login_folder(&self, account_id: i64) -> Result<FolderRow> {
520 self
521 .find_login_folder_optional(account_id)?
522 .ok_or_else(|| Error::NoLoginFolder(account_id))
523 }
524
525 pub fn find_login_folder_optional(
527 &self,
528 account_id: i64,
529 ) -> StdResult<Option<FolderRow>, SqlError> {
530 let query = folder_select_columns(sql::Select::new())
531 .from("folders")
532 .left_join(
533 "account_login_folder login ON folders.folder_id = login.folder_id",
534 )
535 .where_clause("folders.account_id=?1")
536 .where_and("login.account_id=?1");
537
538 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
539 stmt
540 .query_row([account_id], |row| row.try_into())
541 .optional()
542 }
543
544 pub fn find_device_folder(
546 &self,
547 account_id: i64,
548 ) -> StdResult<Option<FolderRow>, SqlError> {
549 let query = folder_select_columns(sql::Select::new())
550 .from("folders")
551 .left_join(
552 "account_device_folder device ON folders.folder_id = device.folder_id",
553 )
554 .where_clause("folders.account_id=?1")
555 .where_and("device.account_id=?1");
556
557 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
558 stmt
559 .query_row([account_id], |row| row.try_into())
560 .optional()
561 }
562
563 pub fn list_user_folders(
567 &self,
568 account_id: i64,
569 ) -> Result<Vec<FolderRow>> {
570 let query = folder_select_columns(sql::Select::new())
571 .from("folders")
572 .left_join(
573 "account_login_folder login ON folders.folder_id = login.folder_id",
574 )
575 .left_join(
576 "account_device_folder device ON folders.folder_id = device.folder_id",
577 )
578 .where_clause("folders.account_id=?1")
579 .where_and("login.folder_id IS NULL")
580 .where_and("device.folder_id IS NULL");
581
582 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
583
584 fn convert_row(row: &Row<'_>) -> Result<FolderRow> {
585 Ok(row.try_into()?)
586 }
587
588 let rows = stmt.query_and_then([account_id], convert_row)?;
589 let mut folders = Vec::new();
590 for row in rows {
591 folders.push(row?);
592 }
593 Ok(folders)
594 }
595
596 pub fn update_name(&self, folder_id: &VaultId, name: &str) -> Result<()> {
598 let modified_at = UtcDateTime::default().to_rfc3339()?;
599 let query = sql::Update::new()
600 .update("folders")
601 .set("name = ?1, modified_at = ?2")
602 .where_clause("identifier = ?3");
603 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
604 stmt.execute((name, modified_at, folder_id.to_string()))?;
605 Ok(())
606 }
607
608 pub fn update_flags(
610 &self,
611 folder_id: &VaultId,
612 flags: &VaultFlags,
613 ) -> Result<()> {
614 let flags = flags.bits().to_le_bytes();
615 let modified_at = UtcDateTime::default().to_rfc3339()?;
616 let query = sql::Update::new()
617 .update("folders")
618 .set("flags = ?1, modified_at = ?2")
619 .where_clause("identifier = ?3");
620 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
621 stmt.execute((flags, modified_at, folder_id.to_string()))?;
622 Ok(())
623 }
624
625 pub fn update_meta(
627 &self,
628 folder_id: &VaultId,
629 meta: &[u8],
630 ) -> Result<()> {
631 let modified_at = UtcDateTime::default().to_rfc3339()?;
632 let query = sql::Update::new()
633 .update("folders")
634 .set("meta = ?1, modified_at = ?2")
635 .where_clause("identifier = ?3");
636 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
637 stmt.execute((meta, modified_at, folder_id.to_string()))?;
638 Ok(())
639 }
640
641 pub fn insert_folder(
643 &self,
644 account_id: i64,
645 folder_row: &FolderRow,
646 ) -> StdResult<i64, SqlError> {
647 let query = sql::Insert::new()
648 .insert_into(
649 r#"
650 folders
651 (
652 account_id,
653 created_at,
654 modified_at,
655 identifier,
656 name,
657 salt,
658 meta,
659 seed,
660 version,
661 cipher,
662 kdf,
663 flags
664 )
665 "#,
666 )
667 .values("(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)");
668
669 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
670 stmt.execute((
671 &account_id,
672 &folder_row.created_at,
673 &folder_row.modified_at,
674 &folder_row.identifier,
675 &folder_row.name,
676 &folder_row.salt,
677 &folder_row.meta,
678 &folder_row.seed,
679 &folder_row.version,
680 &folder_row.cipher,
681 &folder_row.kdf,
682 &folder_row.flags,
683 ))?;
684
685 Ok(self.conn.last_insert_rowid())
686 }
687
688 pub fn update_folder(
690 &self,
691 folder_id: &VaultId,
692 folder_row: &FolderRow,
693 ) -> StdResult<(), SqlError> {
694 let query = sql::Update::new()
695 .update("folders")
696 .set(
697 r#"
698 modified_at = ?1,
699 identifier = ?2,
700 name = ?3,
701 salt = ?4,
702 meta = ?5,
703 seed = ?6,
704 version = ?7,
705 cipher = ?8,
706 kdf = ?9,
707 flags = ?10
708 "#,
709 )
710 .where_clause("identifier=?11");
711 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
712 stmt.execute((
713 &folder_row.modified_at,
714 &folder_row.identifier,
715 &folder_row.name,
716 &folder_row.salt,
717 &folder_row.meta,
718 &folder_row.seed,
719 &folder_row.version,
720 &folder_row.cipher,
721 &folder_row.kdf,
722 &folder_row.flags,
723 folder_id.to_string(),
724 ))?;
725
726 Ok(())
727 }
728
729 pub fn insert_folder_secrets(
731 &self,
732 folder_id: i64,
733 rows: &[SecretRow],
734 ) -> Result<HashMap<SecretId, i64>> {
735 let mut secret_ids = HashMap::new();
736 for secret_row in rows {
737 let identifier: SecretId = secret_row.identifier.parse()?;
738 let secret_id =
739 self.insert_secret_by_row_id(folder_id, secret_row)?;
740 secret_ids.insert(identifier, secret_id);
741 }
742 Ok(secret_ids)
743 }
744
745 pub fn insert_secret(
747 &self,
748 folder_id: &VaultId,
749 secret_row: &SecretRow,
750 ) -> StdResult<i64, SqlError> {
751 let row = self.find_one(folder_id)?;
752 self.insert_secret_by_row_id(row.row_id, secret_row)
753 }
754
755 pub fn insert_secret_by_row_id(
757 &self,
758 folder_id: i64,
759 secret_row: &SecretRow,
760 ) -> StdResult<i64, SqlError> {
761 let query = sql::Insert::new()
765 .insert_into("folder_secrets (folder_id, identifier, commit_hash, meta, secret, created_at, modified_at)")
766 .values("(?1, ?2, ?3, ?4, ?5, ?6, ?7)")
767 .on_conflict(
768 r#"
769 (identifier)
770 DO UPDATE SET
771 folder_id=excluded.folder_id,
772 commit_hash=excluded.commit_hash,
773 meta=excluded.meta,
774 secret=excluded.secret,
775 modified_at=excluded.modified_at
776 "#);
777 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
778 stmt.execute((
779 &folder_id,
780 &secret_row.identifier,
781 &secret_row.commit,
782 &secret_row.meta,
783 &secret_row.secret,
784 &secret_row.created_at,
785 &secret_row.modified_at,
786 ))?;
787 Ok(self.conn.last_insert_rowid())
788 }
789
790 pub fn find_secret(
792 &self,
793 folder_id: &VaultId,
794 secret_id: &SecretId,
795 ) -> StdResult<Option<SecretRow>, SqlError> {
796 let row = self.find_one(folder_id)?;
797 let query = secret_select_columns(sql::Select::new())
798 .from("folder_secrets")
799 .where_clause("folder_id=?1")
800 .where_and("identifier=?2");
801
802 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
803 stmt
804 .query_row((row.row_id, secret_id.to_string()), |row| {
805 let row: SecretRow = row.try_into()?;
806 Ok(row)
807 })
808 .optional()
809 }
810
811 pub fn update_secret(
813 &self,
814 folder_id: &VaultId,
815 secret_row: &SecretRow,
816 ) -> Result<bool> {
817 let modified_at = UtcDateTime::default().to_rfc3339()?;
818 let row = self.find_one(folder_id)?;
819 let query = sql::Update::new()
820 .update("folder_secrets")
821 .set(
822 r#"
823
824 modified_at=?1,
825 commit_hash=?2,
826 meta=?3,
827 secret=?4
828 "#,
829 )
830 .where_clause("folder_id=?5")
831 .where_and("identifier = ?6");
832
833 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
834 let affected_rows = stmt.execute((
835 modified_at,
836 &secret_row.commit,
837 &secret_row.meta,
838 &secret_row.secret,
839 row.row_id,
840 &secret_row.identifier,
841 ))?;
842 Ok(affected_rows > 0)
843 }
844
845 pub fn load_secrets(&self, folder_row_id: i64) -> Result<Vec<SecretRow>> {
847 let query = secret_select_columns(sql::Select::new())
848 .from("folder_secrets")
849 .where_clause("folder_id=?1");
850 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
851
852 fn convert_row(row: &Row<'_>) -> Result<SecretRow> {
853 Ok(row.try_into()?)
854 }
855
856 let rows = stmt.query_and_then([folder_row_id], convert_row)?;
857 let mut secrets = Vec::new();
858 for row in rows {
859 secrets.push(row?);
860 }
861 Ok(secrets)
862 }
863
864 pub fn list_secret_ids(
866 &self,
867 folder_id: &VaultId,
868 ) -> Result<Vec<SecretId>> {
869 let folder = self.find_one(folder_id)?;
870 let query = sql::Select::new()
871 .select("identifier")
872 .from("folder_secrets")
873 .where_clause("folder_id=?1");
874 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
875
876 fn convert_row(row: &Row<'_>) -> Result<SecretId> {
877 let id: String = row.get(0)?;
878 Ok(id.parse()?)
879 }
880
881 let rows = stmt.query_and_then([folder.row_id], convert_row)?;
882 let mut secrets = Vec::new();
883 for row in rows {
884 secrets.push(row?);
885 }
886 Ok(secrets)
887 }
888
889 pub fn delete_folder(
891 &self,
892 folder_id: &VaultId,
893 ) -> StdResult<bool, SqlError> {
894 let row = self.find_one(folder_id)?;
895 let query = sql::Delete::new()
896 .delete_from("folders")
897 .where_clause("folder_id = ?1");
898 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
899 let affected_rows = stmt.execute([row.row_id])?;
900 Ok(affected_rows > 0)
901 }
902
903 pub fn delete_secret(
905 &self,
906 folder_id: &VaultId,
907 secret_id: &SecretId,
908 ) -> StdResult<bool, SqlError> {
909 let row = self.find_one(folder_id)?;
910 let query = sql::Delete::new()
911 .delete_from("folder_secrets")
912 .where_clause("folder_id = ?1")
913 .where_and("identifier = ?2");
914 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
915 let affected_rows =
916 stmt.execute((row.row_id, secret_id.to_string()))?;
917 Ok(affected_rows > 0)
918 }
919
920 fn delete_all_secrets(
922 &self,
923 folder_id: i64,
924 ) -> StdResult<usize, SqlError> {
925 let query = sql::Delete::new()
926 .delete_from("folder_secrets")
927 .where_clause("folder_id = ?1");
928 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
929 stmt.execute([folder_id])
930 }
931}