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}