use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use crate::error::CoolError;
#[async_trait::async_trait]
pub trait KeyProvider: Send + Sync + 'static {
async fn resolve_signing_key(&self, kid: &str) -> Result<Vec<u8>, CoolError>;
}
#[derive(Debug, Clone, Default)]
pub struct StaticKeyProvider {
keys: BTreeMap<String, Vec<u8>>,
}
impl StaticKeyProvider {
pub fn new() -> Self {
Self::default()
}
pub fn with_key(mut self, kid: impl Into<String>, key: Vec<u8>) -> Self {
self.keys.insert(kid.into(), key);
self
}
}
#[async_trait::async_trait]
impl KeyProvider for StaticKeyProvider {
async fn resolve_signing_key(&self, kid: &str) -> Result<Vec<u8>, CoolError> {
self.keys
.get(kid)
.cloned()
.ok_or_else(|| CoolError::Unauthorized("unknown signing key".to_owned()))
}
}
#[async_trait::async_trait]
pub trait NonceStore: Send + Sync + 'static {
async fn record_if_unseen(
&self,
nonce: &str,
expires_at: chrono::DateTime<chrono::Utc>,
) -> Result<bool, CoolError>;
}
#[derive(Debug, Clone, Default)]
pub struct InMemoryNonceStore {
seen: Arc<RwLock<BTreeMap<String, chrono::DateTime<chrono::Utc>>>>,
}
impl InMemoryNonceStore {
pub fn new() -> Self {
Self::default()
}
}
#[async_trait::async_trait]
impl NonceStore for InMemoryNonceStore {
async fn record_if_unseen(
&self,
nonce: &str,
expires_at: chrono::DateTime<chrono::Utc>,
) -> Result<bool, CoolError> {
let mut seen = self
.seen
.write()
.map_err(|_| CoolError::Internal("nonce store poisoned".to_owned()))?;
let now = chrono::Utc::now();
seen.retain(|_, exp| *exp > now);
if seen.contains_key(nonce) {
return Ok(false);
}
seen.insert(nonce.to_owned(), expires_at);
Ok(true)
}
}