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.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.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 = if let Some(salt) = value.salt {
185 Some(salt)
186 } else {
187 None
188 };
189
190 let meta = if let Some(meta) = &value.meta {
191 Some(decode(meta).await?)
192 } else {
193 None
194 };
195
196 let seed = if let Some(seed) = value.seed {
197 let seed: [u8; 32] = seed.as_slice().try_into()?;
198 Some(seed)
199 } else {
200 None
201 };
202
203 let summary =
204 Summary::new(version, folder_id, value.name, cipher, kdf, flags);
205
206 Ok(FolderRecord {
207 row_id: value.row_id,
208 created_at,
209 modified_at,
210 salt,
211 meta,
212 seed,
213 summary,
214 })
215 }
216
217 pub fn into_vault(&self) -> Result<Vault> {
219 let mut vault: Vault = self.summary.clone().into();
220 vault.header_mut().set_meta(self.meta.clone());
221 vault.header_mut().set_salt(self.salt.clone());
222 vault.header_mut().set_seed(self.seed.clone());
223 Ok(vault)
224 }
225}
226
227#[doc(hidden)]
229#[derive(Debug, Default)]
230pub struct SecretRow {
231 pub row_id: i64,
232 created_at: String,
233 modified_at: String,
234 identifier: String,
235 commit: Vec<u8>,
236 meta: Vec<u8>,
237 secret: Vec<u8>,
238}
239
240impl SecretRow {
241 pub async fn new(
243 secret_id: &SecretId,
244 commit: &CommitHash,
245 entry: &VaultEntry,
246 ) -> Result<Self> {
247 let VaultEntry(meta, secret) = entry;
248 let meta = encode(meta).await?;
249 let secret = encode(secret).await?;
250 Ok(Self {
251 created_at: UtcDateTime::default().to_rfc3339()?,
252 modified_at: UtcDateTime::default().to_rfc3339()?,
253 identifier: secret_id.to_string(),
254 commit: commit.as_ref().to_vec(),
255 meta,
256 secret,
257 ..Default::default()
258 })
259 }
260
261 pub fn identifier(&self) -> &str {
263 &self.identifier
264 }
265
266 pub fn commit(&self) -> &[u8] {
268 &self.commit
269 }
270
271 pub fn meta_bytes(&self) -> &[u8] {
273 &self.meta
274 }
275
276 pub fn secret_bytes(&self) -> &[u8] {
278 &self.secret
279 }
280}
281
282impl<'a> TryFrom<&Row<'a>> for SecretRow {
283 type Error = SqlError;
284 fn try_from(row: &Row<'a>) -> StdResult<Self, Self::Error> {
285 Ok(SecretRow {
286 row_id: row.get(0)?,
287 created_at: row.get(1)?,
288 modified_at: row.get(2)?,
289 identifier: row.get(3)?,
290 commit: row.get(4)?,
291 meta: row.get(5)?,
292 secret: row.get(6)?,
293 })
294 }
295}
296
297#[doc(hidden)]
299#[derive(Debug)]
300pub struct SecretRecord {
301 pub row_id: i64,
302 pub created_at: UtcDateTime,
303 pub modified_at: UtcDateTime,
304 pub secret_id: VaultId,
305 pub commit: VaultCommit,
306}
307
308impl SecretRecord {
309 pub async fn from_row(value: SecretRow) -> Result<Self> {
311 let created_at = UtcDateTime::parse_rfc3339(&value.created_at)?;
312 let modified_at = UtcDateTime::parse_rfc3339(&value.modified_at)?;
313 let secret_id: SecretId = value.identifier.parse()?;
314 let commit_hash = CommitHash(value.commit.as_slice().try_into()?);
315 let meta: AeadPack = decode(&value.meta).await?;
316 let secret: AeadPack = decode(&value.secret).await?;
317 let commit = VaultCommit(commit_hash, VaultEntry(meta, secret));
318
319 Ok(SecretRecord {
320 row_id: value.row_id,
321 created_at,
322 modified_at,
323 secret_id,
324 commit,
325 })
326 }
327}
328
329pub struct FolderEntity<'conn, C>
331where
332 C: Deref<Target = Connection>,
333{
334 conn: &'conn C,
335}
336
337impl<'conn> FolderEntity<'conn, Box<Connection>> {
338 pub fn find_all_secrets_query() -> sql::Select {
340 secret_select_columns(sql::Select::new())
341 .from("folder_secrets")
342 .where_clause("folder_id=?1")
343 }
344
345 pub async fn compute_folder_vault(
347 client: &Client,
348 folder_id: &VaultId,
349 ) -> Result<Vault> {
350 let folder_id = *folder_id;
351
352 let (folder_row, secret_rows) = client
353 .conn_and_then(move |conn| {
354 let folder_entity = FolderEntity::new(&conn);
355 let folder_row = folder_entity.find_one(&folder_id)?;
356 let secret_rows =
357 folder_entity.load_secrets(folder_row.row_id)?;
358 Ok::<_, Error>((folder_row, secret_rows))
359 })
360 .await?;
361
362 let folder_record = FolderRecord::from_row(folder_row).await?;
363 let mut vault = folder_record.into_vault()?;
364 for row in secret_rows {
365 let record = SecretRecord::from_row(row).await?;
366 vault.insert_entry(record.secret_id, record.commit);
367 }
368 Ok(vault)
369 }
370}
371
372impl<'conn> FolderEntity<'conn, Transaction<'conn>> {
373 pub async fn upsert_folder_and_secrets(
379 client: &Client,
380 account_id: i64,
381 vault: &Vault,
382 ) -> Result<(i64, HashMap<SecretId, i64>)> {
383 let folder_id = *vault.id();
384
385 let meta = if let Some(meta) = vault.header().meta() {
386 Some(encode(meta).await?)
387 } else {
388 None
389 };
390 let salt = vault.salt().cloned();
391 let seed = vault.seed().map(|s| s.to_vec());
392
393 let folder_row =
394 FolderRow::new_insert_parts(vault.summary(), salt, meta, seed)?;
395
396 let mut secret_rows = Vec::new();
397 for (secret_id, commit) in vault.iter() {
398 let VaultCommit(commit, entry) = commit;
399 secret_rows.push(SecretRow::new(secret_id, commit, entry).await?);
400 }
401
402 Ok(client
403 .conn_mut_and_then(move |conn| {
404 let tx = conn.transaction()?;
405 let folder_entity = FolderEntity::new(&tx);
406
407 let folder_id = if let Some(row) =
408 folder_entity.find_optional(&folder_id)?
409 {
410 folder_entity.update_folder(&folder_id, &folder_row)?;
411 folder_entity.delete_all_secrets(row.row_id)?;
412 row.row_id
413 } else {
414 folder_entity.insert_folder(account_id, &folder_row)?
415 };
416
417 let secret_ids = folder_entity.insert_folder_secrets(
418 folder_id,
419 secret_rows.as_slice(),
420 )?;
421 tx.commit()?;
422 Ok::<_, Error>((folder_id, secret_ids))
423 })
424 .await?)
425 }
426
427 pub async fn replace_all_secrets(
429 client: Client,
430 folder_id: &VaultId,
431 vault: &Vault,
432 ) -> Result<()> {
433 let folder_id = folder_id.clone();
434 let mut insert_secrets = Vec::new();
435 for (secret_id, commit) in vault.iter() {
436 let VaultCommit(commit, entry) = commit;
437 insert_secrets
438 .push(SecretRow::new(secret_id, commit, entry).await?);
439 }
440
441 let folder_update_row = FolderRow::new_update(vault).await?;
442 client
443 .conn_mut(move |conn| {
444 let tx = conn.transaction()?;
445 let folder = FolderEntity::new(&tx);
446 let folder_row = folder.find_one(&folder_id)?;
447 folder.delete_all_secrets(folder_row.row_id)?;
448 for secret_row in insert_secrets {
449 folder.insert_secret_by_row_id(
450 folder_row.row_id,
451 &secret_row,
452 )?;
453 }
454 folder.update_folder(&folder_id, &folder_update_row)?;
455 tx.commit()?;
456 Ok(())
457 })
458 .await
459 .map_err(Error::from)?;
460 Ok(())
461 }
462}
463
464impl<'conn, C> FolderEntity<'conn, C>
465where
466 C: Deref<Target = Connection>,
467{
468 pub fn new(conn: &'conn C) -> Self {
470 Self { conn }
471 }
472
473 fn select_folder(
474 &self,
475 use_identifier: bool,
476 ) -> StdResult<CachedStatement, SqlError> {
477 let query = folder_select_columns(sql::Select::new()).from("folders");
478
479 let query = if use_identifier {
480 query.where_clause("identifier = ?1")
481 } else {
482 query.where_clause("folder_id = ?1")
483 };
484 Ok(self.conn.prepare_cached(&query.as_string())?)
485 }
486
487 pub fn find_one(
489 &self,
490 folder_id: &VaultId,
492 ) -> StdResult<FolderRow, SqlError> {
493 let mut stmt = self.select_folder(true)?;
494 Ok(stmt
495 .query_row([folder_id.to_string()], |row| Ok(row.try_into()?))?)
496 }
497
498 pub fn find_optional(
500 &self,
501 folder_id: &VaultId,
503 ) -> StdResult<Option<FolderRow>, SqlError> {
504 let mut stmt = self.select_folder(true)?;
505 Ok(stmt
506 .query_row([folder_id.to_string()], |row| {
507 let row: FolderRow = row.try_into()?;
508 Ok(row)
509 })
510 .optional()?)
511 }
512
513 pub fn find_by_row_id(
515 &self,
516 folder_id: i64,
517 ) -> StdResult<FolderRow, SqlError> {
518 let mut stmt = self.select_folder(false)?;
519 Ok(stmt.query_row([folder_id], |row| Ok(row.try_into()?))?)
520 }
521
522 pub fn find_login_folder(&self, account_id: i64) -> Result<FolderRow> {
524 Ok(self
525 .find_login_folder_optional(account_id)?
526 .ok_or_else(|| Error::NoLoginFolder(account_id))?)
527 }
528
529 pub fn find_login_folder_optional(
531 &self,
532 account_id: i64,
533 ) -> StdResult<Option<FolderRow>, SqlError> {
534 let query = folder_select_columns(sql::Select::new())
535 .from("folders")
536 .left_join(
537 "account_login_folder login ON folders.folder_id = login.folder_id",
538 )
539 .where_clause("folders.account_id=?1")
540 .where_and("login.account_id=?1");
541
542 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
543 Ok(stmt
544 .query_row([account_id], |row| Ok(row.try_into()?))
545 .optional()?)
546 }
547
548 pub fn find_device_folder(
550 &self,
551 account_id: i64,
552 ) -> StdResult<Option<FolderRow>, SqlError> {
553 let query = folder_select_columns(sql::Select::new())
554 .from("folders")
555 .left_join(
556 "account_device_folder device ON folders.folder_id = device.folder_id",
557 )
558 .where_clause("folders.account_id=?1")
559 .where_and("device.account_id=?1");
560
561 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
562 Ok(stmt
563 .query_row([account_id], |row| Ok(row.try_into()?))
564 .optional()?)
565 }
566
567 pub fn list_user_folders(
571 &self,
572 account_id: i64,
573 ) -> Result<Vec<FolderRow>> {
574 let query = folder_select_columns(sql::Select::new())
575 .from("folders")
576 .left_join(
577 "account_login_folder login ON folders.folder_id = login.folder_id",
578 )
579 .left_join(
580 "account_device_folder device ON folders.folder_id = device.folder_id",
581 )
582 .where_clause("folders.account_id=?1")
583 .where_and("login.folder_id IS NULL")
584 .where_and("device.folder_id IS NULL");
585
586 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
587
588 fn convert_row(row: &Row<'_>) -> Result<FolderRow> {
589 Ok(row.try_into()?)
590 }
591
592 let rows = stmt.query_and_then([account_id], |row| {
593 Ok::<_, crate::Error>(convert_row(row)?)
594 })?;
595 let mut folders = Vec::new();
596 for row in rows {
597 folders.push(row?);
598 }
599 Ok(folders)
600 }
601
602 pub fn update_name(&self, folder_id: &VaultId, name: &str) -> Result<()> {
604 let modified_at = UtcDateTime::default().to_rfc3339()?;
605 let query = sql::Update::new()
606 .update("folders")
607 .set("name = ?1, modified_at = ?2")
608 .where_clause("identifier = ?3");
609 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
610 stmt.execute((name, modified_at, folder_id.to_string()))?;
611 Ok(())
612 }
613
614 pub fn update_flags(
616 &self,
617 folder_id: &VaultId,
618 flags: &VaultFlags,
619 ) -> Result<()> {
620 let flags = flags.bits().to_le_bytes();
621 let modified_at = UtcDateTime::default().to_rfc3339()?;
622 let query = sql::Update::new()
623 .update("folders")
624 .set("flags = ?1, modified_at = ?2")
625 .where_clause("identifier = ?3");
626 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
627 stmt.execute((flags, modified_at, folder_id.to_string()))?;
628 Ok(())
629 }
630
631 pub fn update_meta(
633 &self,
634 folder_id: &VaultId,
635 meta: &[u8],
636 ) -> Result<()> {
637 let modified_at = UtcDateTime::default().to_rfc3339()?;
638 let query = sql::Update::new()
639 .update("folders")
640 .set("meta = ?1, modified_at = ?2")
641 .where_clause("identifier = ?3");
642 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
643 stmt.execute((meta, modified_at, folder_id.to_string()))?;
644 Ok(())
645 }
646
647 pub fn insert_folder(
649 &self,
650 account_id: i64,
651 folder_row: &FolderRow,
652 ) -> StdResult<i64, SqlError> {
653 let query = sql::Insert::new()
654 .insert_into(
655 r#"
656 folders
657 (
658 account_id,
659 created_at,
660 modified_at,
661 identifier,
662 name,
663 salt,
664 meta,
665 seed,
666 version,
667 cipher,
668 kdf,
669 flags
670 )
671 "#,
672 )
673 .values("(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)");
674
675 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
676 stmt.execute((
677 &account_id,
678 &folder_row.created_at,
679 &folder_row.modified_at,
680 &folder_row.identifier,
681 &folder_row.name,
682 &folder_row.salt,
683 &folder_row.meta,
684 &folder_row.seed,
685 &folder_row.version,
686 &folder_row.cipher,
687 &folder_row.kdf,
688 &folder_row.flags,
689 ))?;
690
691 Ok(self.conn.last_insert_rowid())
692 }
693
694 pub fn update_folder(
696 &self,
697 folder_id: &VaultId,
698 folder_row: &FolderRow,
699 ) -> StdResult<(), SqlError> {
700 let query = sql::Update::new()
701 .update("folders")
702 .set(
703 r#"
704 modified_at = ?1,
705 identifier = ?2,
706 name = ?3,
707 salt = ?4,
708 meta = ?5,
709 seed = ?6,
710 version = ?7,
711 cipher = ?8,
712 kdf = ?9,
713 flags = ?10
714 "#,
715 )
716 .where_clause("identifier=?11");
717 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
718 stmt.execute((
719 &folder_row.modified_at,
720 &folder_row.identifier,
721 &folder_row.name,
722 &folder_row.salt,
723 &folder_row.meta,
724 &folder_row.seed,
725 &folder_row.version,
726 &folder_row.cipher,
727 &folder_row.kdf,
728 &folder_row.flags,
729 folder_id.to_string(),
730 ))?;
731
732 Ok(())
733 }
734
735 pub fn insert_folder_secrets(
737 &self,
738 folder_id: i64,
739 rows: &[SecretRow],
740 ) -> Result<HashMap<SecretId, i64>> {
741 let mut secret_ids = HashMap::new();
742 for secret_row in rows {
743 let identifier: SecretId = secret_row.identifier.parse()?;
744 let secret_id =
745 self.insert_secret_by_row_id(folder_id, &secret_row)?;
746 secret_ids.insert(identifier, secret_id);
747 }
748 Ok(secret_ids)
749 }
750
751 pub fn insert_secret(
753 &self,
754 folder_id: &VaultId,
755 secret_row: &SecretRow,
756 ) -> StdResult<i64, SqlError> {
757 let row = self.find_one(folder_id)?;
758 Ok(self.insert_secret_by_row_id(row.row_id, secret_row)?)
759 }
760
761 pub fn insert_secret_by_row_id(
763 &self,
764 folder_id: i64,
765 secret_row: &SecretRow,
766 ) -> StdResult<i64, SqlError> {
767 let query = sql::Insert::new()
771 .insert_into("folder_secrets (folder_id, identifier, commit_hash, meta, secret, created_at, modified_at)")
772 .values("(?1, ?2, ?3, ?4, ?5, ?6, ?7)")
773 .on_conflict(
774 r#"
775 (identifier)
776 DO UPDATE SET
777 folder_id=excluded.folder_id,
778 commit_hash=excluded.commit_hash,
779 meta=excluded.meta,
780 secret=excluded.secret,
781 modified_at=excluded.modified_at
782 "#);
783 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
784 stmt.execute((
785 &folder_id,
786 &secret_row.identifier,
787 &secret_row.commit,
788 &secret_row.meta,
789 &secret_row.secret,
790 &secret_row.created_at,
791 &secret_row.modified_at,
792 ))?;
793 Ok(self.conn.last_insert_rowid())
794 }
795
796 pub fn find_secret(
798 &self,
799 folder_id: &VaultId,
800 secret_id: &SecretId,
801 ) -> StdResult<Option<SecretRow>, SqlError> {
802 let row = self.find_one(folder_id)?;
803 let query = secret_select_columns(sql::Select::new())
804 .from("folder_secrets")
805 .where_clause("folder_id=?1")
806 .where_and("identifier=?2");
807
808 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
809 Ok(stmt
810 .query_row((row.row_id, secret_id.to_string()), |row| {
811 let row: SecretRow = row.try_into()?;
812 Ok(row)
813 })
814 .optional()?)
815 }
816
817 pub fn update_secret(
819 &self,
820 folder_id: &VaultId,
821 secret_row: &SecretRow,
822 ) -> Result<bool> {
823 let modified_at = UtcDateTime::default().to_rfc3339()?;
824 let row = self.find_one(folder_id)?;
825 let query = sql::Update::new()
826 .update("folder_secrets")
827 .set(
828 r#"
829
830 modified_at=?1,
831 commit_hash=?2,
832 meta=?3,
833 secret=?4
834 "#,
835 )
836 .where_clause("folder_id=?5")
837 .where_and("identifier = ?6");
838
839 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
840 let affected_rows = stmt.execute((
841 modified_at,
842 &secret_row.commit,
843 &secret_row.meta,
844 &secret_row.secret,
845 row.row_id,
846 &secret_row.identifier,
847 ))?;
848 Ok(affected_rows > 0)
849 }
850
851 pub fn load_secrets(&self, folder_row_id: i64) -> Result<Vec<SecretRow>> {
853 let query = secret_select_columns(sql::Select::new())
854 .from("folder_secrets")
855 .where_clause("folder_id=?1");
856 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
857
858 fn convert_row(row: &Row<'_>) -> Result<SecretRow> {
859 Ok(row.try_into()?)
860 }
861
862 let rows = stmt.query_and_then([folder_row_id], |row| {
863 Ok::<_, crate::Error>(convert_row(row)?)
864 })?;
865 let mut secrets = Vec::new();
866 for row in rows {
867 secrets.push(row?);
868 }
869 Ok(secrets)
870 }
871
872 pub fn list_secret_ids(
874 &self,
875 folder_id: &VaultId,
876 ) -> Result<Vec<SecretId>> {
877 let folder = self.find_one(folder_id)?;
878 let query = sql::Select::new()
879 .select("identifier")
880 .from("folder_secrets")
881 .where_clause("folder_id=?1");
882 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
883
884 fn convert_row(row: &Row<'_>) -> Result<SecretId> {
885 let id: String = row.get(0)?;
886 Ok(id.parse()?)
887 }
888
889 let rows = stmt.query_and_then([folder.row_id], |row| {
890 Ok::<_, crate::Error>(convert_row(row)?)
891 })?;
892 let mut secrets = Vec::new();
893 for row in rows {
894 secrets.push(row?);
895 }
896 Ok(secrets)
897 }
898
899 pub fn delete_folder(
901 &self,
902 folder_id: &VaultId,
903 ) -> StdResult<bool, SqlError> {
904 let row = self.find_one(folder_id)?;
905 let query = sql::Delete::new()
906 .delete_from("folders")
907 .where_clause("folder_id = ?1");
908 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
909 let affected_rows = stmt.execute([row.row_id])?;
910 Ok(affected_rows > 0)
911 }
912
913 pub fn delete_secret(
915 &self,
916 folder_id: &VaultId,
917 secret_id: &SecretId,
918 ) -> StdResult<bool, SqlError> {
919 let row = self.find_one(folder_id)?;
920 let query = sql::Delete::new()
921 .delete_from("folder_secrets")
922 .where_clause("folder_id = ?1")
923 .where_and("identifier = ?2");
924 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
925 let affected_rows =
926 stmt.execute((row.row_id, secret_id.to_string()))?;
927 Ok(affected_rows > 0)
928 }
929
930 fn delete_all_secrets(
932 &self,
933 folder_id: i64,
934 ) -> StdResult<usize, SqlError> {
935 let query = sql::Delete::new()
936 .delete_from("folder_secrets")
937 .where_clause("folder_id = ?1");
938 let mut stmt = self.conn.prepare_cached(&query.as_string())?;
939 Ok(stmt.execute([folder_id])?)
940 }
941}