pyrus_cert_store/store.rs
1use pyrus_crypto::Fingerprint;
2
3use rusqlite::{params, Connection};
4
5use std::path::Path;
6
7use crate::connection::CertStoreConn;
8use crate::error::Result;
9use crate::lazy_cert::LazyCert;
10
11#[allow(unused)]
12use crate::error::StoreError;
13
14/// A wrapper around an SQL connection to the certificate database.
15///
16/// A connection is made by calling [`CertStore::open`], configuring the
17/// connection with [`CertStoreConn<P>`] and calling [`CertStoreConn::connect`]
18/// which returns an instance of [`CertStore`] with an active connection.
19///
20/// See the crate root [examples](crate#examples) on how to open the store.
21pub struct CertStore {
22 pub(crate) conn: Connection,
23}
24
25impl CertStore {
26 /// Returns a [`CertStoreConn<P>`] which can be used to configure the
27 /// connection to the underlying SQL database.
28 ///
29 /// Supplying an empty path (either `""` or `Path::new("")`) results in
30 /// the store being open in memory (without persistence).
31 pub fn open<P: AsRef<Path>>(path: P) -> CertStoreConn<P> {
32 CertStoreConn::new(path)
33 }
34
35 /// Inserts a [`LazyCert`] to the store. If there is a fingerprint clash,
36 /// the new certificate overwrites the old one.
37 ///
38 /// For now this method takes ownership of `cert`, but this is subject to
39 /// change in the future.
40 ///
41 /// # Errors
42 /// * [`StoreError::SQLiteFail`] - if the SQLite call failed.
43 pub fn insert(&self, cert: LazyCert) -> Result<()> {
44 let mut stmt = self.conn.prepare(
45 "INSERT INTO certs(fpr, uid, bytes)
46 VALUES(?1, ?2, ?3)
47 ON CONFLICT(fpr) DO UPDATE SET uid=?2, bytes=?3;",
48 )?;
49 stmt.execute(params![cert.fpr.to_hex().as_str(), &cert.uid, &cert.bytes])?;
50 Ok(())
51 }
52
53 /// Finds a certificate with the given fingerprint. If a certificate is
54 /// not found, this method will return an error since the SQLite call
55 /// failed.
56 ///
57 /// Returns an owned [`LazyCert`] which is probably why
58 /// [`CertStore::insert`] takes ownership of `cert`.
59 ///
60 /// # Errors
61 /// * [`StoreError::SQLiteFail`] - if the SQLite call failed.
62 pub fn get(&self, fpr: &Fingerprint) -> Result<LazyCert> {
63 let mut stmt = self
64 .conn
65 .prepare("SELECT uid, bytes FROM certs WHERE fpr=?1")?;
66 let (uid, bytes) = stmt.query_row(params![fpr.to_hex().as_str()], |row| {
67 let uid: String = row.get(0)?;
68 let bytes: Vec<u8> = row.get(1)?;
69 Ok((uid, bytes))
70 })?;
71 let cert = LazyCert {
72 fpr: *fpr,
73 uid,
74 bytes,
75 };
76 Ok(cert)
77 }
78
79 /// Removes a certificate with fingerprint `fpr` from the store. If the
80 /// certificate is not found, this method will return an error since
81 /// the SQLite call failed.
82 ///
83 /// # Errors
84 /// * [`StoreError::SQLiteFail`] - if the SQLite call failed.
85 pub fn remove(&self, fpr: &Fingerprint) -> Result<()> {
86 let mut stmt = self.conn.prepare("DELETE FROM certs WHERE fpr=?1")?;
87 stmt.execute(params![fpr.to_hex().as_str()])?;
88 Ok(())
89 }
90
91 /// Returns a [`Vec<LazyCert>`] of all certificates in the store.
92 /// Returning an iterator of [`LazyCert`]s is planned, but doing this is
93 /// quite difficult.
94 ///
95 /// # Errors
96 /// * [`StoreError::SQLiteFail`] - if the SQLite call failed.
97 pub fn certs(&self) -> Result<Vec<LazyCert>> {
98 let mut stmt = self.conn.prepare("SELECT * FROM certs")?;
99 let rows = stmt.query_map([], |row| {
100 let fpr: String = row.get(0)?;
101 let fpr = Fingerprint::from_hex(fpr).map_err(|_| rusqlite::Error::InvalidQuery)?;
102 let uid: String = row.get(1)?;
103 let bytes: Vec<u8> = row.get(2)?;
104 Ok(LazyCert {
105 fpr,
106 uid,
107 bytes,
108 })
109 })?;
110 let rows: Vec<LazyCert> = rows.filter(|i| i.is_ok()).map(|i| i.unwrap()).collect();
111 Ok(rows)
112 }
113
114 /// A "lighter" alternative to [`CertStore::certs`], which returns pairs
115 /// of fingerprints and user ids for easy lookup and filtering.
116 ///
117 /// # Errors
118 /// * [`StoreError::SQLiteFail`] - if the SQLite call failed.
119 pub fn uids(&self) -> Result<Vec<(Fingerprint, String)>> {
120 let mut stmt = self.conn.prepare("SELECT fpr, uid FROM certs")?;
121 let rows = stmt.query_map([], |row| {
122 let fpr: String = row.get(0)?;
123 let fpr = Fingerprint::from_hex(fpr).map_err(|_| rusqlite::Error::InvalidQuery)?;
124 let uid: String = row.get(1)?;
125 Ok((fpr, uid))
126 })?;
127 let rows: Vec<_> = rows.filter(|i| i.is_ok()).map(|i| i.unwrap()).collect();
128 Ok(rows)
129 }
130}