use p256::{ ecdsa::SigningKey, pkcs8::{DecodePrivateKey as _, EncodePrivateKey as _, LineEnding}, }; use sqlx::{Sqlite, SqliteConnection, Transaction}; use super::{ History, event::{Changed, Event}, }; use crate::{ clock::DateTime, db::NotFound, event::{Instant, Sequence}, }; pub trait Provider { fn vapid(&mut self) -> Vapid<'_>; } impl Provider for Transaction<'_, Sqlite> { fn vapid(&mut self) -> Vapid<'_> { Vapid(self) } } pub struct Vapid<'a>(&'a mut SqliteConnection); impl Vapid<'_> { pub async fn record_events( &mut self, events: impl IntoIterator, ) -> Result<(), sqlx::Error> { for event in events { self.record_event(&event).await?; } Ok(()) } pub async fn record_event(&mut self, event: &Event) -> Result<(), sqlx::Error> { match event { Event::Changed(changed) => self.record_changed(changed).await, } } async fn record_changed(&mut self, changed: &Changed) -> Result<(), sqlx::Error> { sqlx::query!( r#" insert into vapid_key (changed_at, changed_sequence) values ($1, $2) "#, changed.instant.at, changed.instant.sequence, ) .execute(&mut *self.0) .await?; Ok(()) } pub async fn clear(&mut self) -> Result<(), sqlx::Error> { sqlx::query!( r#" delete from vapid_key "# ) .execute(&mut *self.0) .await?; sqlx::query!( r#" delete from vapid_signing_key "# ) .execute(&mut *self.0) .await?; Ok(()) } pub async fn store_signing_key(&mut self, key: &SigningKey) -> Result<(), Error> { let key = key.to_pkcs8_pem(LineEnding::CRLF)?; let key = key.as_str(); sqlx::query!( r#" insert into vapid_signing_key (key) values ($1) "#, key, ) .execute(&mut *self.0) .await?; Ok(()) } pub async fn current(&mut self) -> Result { let key = sqlx::query!( r#" select key.changed_at as "changed_at: DateTime", key.changed_sequence as "changed_sequence: Sequence", signing.key from vapid_key as key join vapid_signing_key as signing "# ) .map(|row| { let key = SigningKey::from_pkcs8_pem(&row.key)?; let key = key.verifying_key().to_owned(); let changed = Instant::new(row.changed_at, row.changed_sequence); Ok::<_, Error>(History { key, changed }) }) .fetch_one(&mut *self.0) .await??; Ok(key) } } #[derive(Debug, thiserror::Error)] #[error(transparent)] pub enum Error { Ecdsa(#[from] p256::ecdsa::Error), Pkcs8(#[from] p256::pkcs8::Error), Database(#[from] sqlx::Error), } impl NotFound for Result { type Ok = T; type Error = Error; fn optional(self) -> Result, Error> { match self { Ok(value) => Ok(Some(value)), Err(Error::Database(sqlx::Error::RowNotFound)) => Ok(None), Err(other) => Err(other), } } }