use crate::models::CredentialAndPurposeKey;
use crate::utils::now;
use crate::{
CredentialRepository, CredentialRetriever, CredentialRetrieverCreator, Identifier,
IdentityError, TimestampInSeconds,
};
use async_trait::async_trait;
use ockam_core::compat::boxed::Box;
use ockam_core::compat::string::String;
use ockam_core::compat::sync::Arc;
use ockam_core::{Address, Result};
use tracing::{debug, error, trace};
pub const DEFAULT_CREDENTIAL_CLOCK_SKEW_GAP: TimestampInSeconds = TimestampInSeconds(60);
pub struct CachedCredentialRetriever {
issuer: Identifier,
subject: Identifier,
scope: String,
cache: Arc<dyn CredentialRepository>,
}
impl CachedCredentialRetriever {
pub fn new(
issuer: Identifier,
subject: Identifier,
scope: String,
cache: Arc<dyn CredentialRepository>,
) -> Self {
Self {
issuer,
subject,
scope,
cache,
}
}
pub async fn retrieve_impl(
issuer: &Identifier,
for_identity: &Identifier,
scope: &str,
now: TimestampInSeconds,
cache: Arc<dyn CredentialRepository>,
clock_skew_gap: TimestampInSeconds,
) -> Result<Option<CredentialAndPurposeKey>> {
debug!(
"Requested credential for: {} from: {}",
for_identity, issuer
);
if let Some(cached_credential) = cache.get(for_identity, issuer, scope).await? {
if cached_credential.get_expires_at()? > now + clock_skew_gap {
debug!("Found valid cached credential for: {}", for_identity);
Ok(Some(cached_credential))
} else {
debug!(
"Found expired cached credential for: {}. Deleting...",
for_identity
);
let delete_res = cache.delete(for_identity, issuer, scope).await;
if let Some(err) = delete_res.err() {
error!(
"Error deleting expired credential for {} from {}. Err={}",
for_identity, issuer, err
);
}
Ok(None)
}
} else {
debug!("Found no cached credential for: {}", for_identity);
Ok(None)
}
}
}
pub struct CachedCredentialRetrieverCreator {
issuer: Identifier,
scope: String,
cache: Arc<dyn CredentialRepository>,
}
impl CachedCredentialRetrieverCreator {
pub fn new(issuer: Identifier, scope: String, cache: Arc<dyn CredentialRepository>) -> Self {
Self {
issuer,
scope,
cache,
}
}
}
#[async_trait]
impl CredentialRetrieverCreator for CachedCredentialRetrieverCreator {
async fn create(&self, subject: &Identifier) -> Result<Arc<dyn CredentialRetriever>> {
Ok(Arc::new(CachedCredentialRetriever::new(
self.issuer.clone(),
subject.clone(),
self.scope.clone(),
self.cache.clone(),
)))
}
}
#[async_trait]
impl CredentialRetriever for CachedCredentialRetriever {
async fn initialize(&self) -> Result<()> {
trace!("using a cached credential retriever");
Ok(())
}
async fn retrieve(&self) -> Result<CredentialAndPurposeKey> {
let now = now()?;
match Self::retrieve_impl(
&self.issuer,
&self.subject,
&self.scope,
now,
self.cache.clone(),
0.into(),
)
.await?
{
Some(credential) => Ok(credential),
None => Err(IdentityError::NoCredential)?,
}
}
fn subscribe(&self, _address: &Address) -> Result<()> {
Ok(())
}
fn unsubscribe(&self, _address: &Address) -> Result<()> {
Ok(())
}
}