use std::collections::HashMap;
use std::sync::Mutex;
use axess_identity::{TenantId, UserId};
use chrono::{DateTime, Utc};
use crate::delegated::error::DelegatedError;
use axess_factors::ZeroizedString;
#[derive(Debug, Clone)]
pub struct StoredDelegation {
pub provider: String,
pub access_token: ZeroizedString,
pub refresh_token: Option<ZeroizedString>,
pub expires_at: Option<DateTime<Utc>>,
pub scopes: Vec<String>,
pub token_type: String,
}
impl StoredDelegation {
pub fn is_fresh(&self, now: DateTime<Utc>, skew: chrono::Duration) -> bool {
match self.expires_at {
Some(exp) => now < exp - skew,
None => true,
}
}
}
pub trait DelegatedCredentialStore: Send + Sync + 'static {
fn load(
&self,
tenant: &TenantId,
user: &UserId,
provider: &str,
) -> impl std::future::Future<Output = Result<Option<StoredDelegation>, String>> + Send;
fn save(
&self,
tenant: &TenantId,
user: &UserId,
credential: StoredDelegation,
) -> impl std::future::Future<Output = Result<(), String>> + Send;
fn revoke(
&self,
tenant: &TenantId,
user: &UserId,
provider: &str,
) -> impl std::future::Future<Output = Result<(), String>> + Send;
}
#[derive(Debug, Default)]
pub struct MemoryDelegatedCredentialStore {
inner: Mutex<HashMap<(TenantId, UserId, String), StoredDelegation>>,
}
impl MemoryDelegatedCredentialStore {
pub fn new() -> Self {
Self::default()
}
}
impl DelegatedCredentialStore for MemoryDelegatedCredentialStore {
async fn load(
&self,
tenant: &TenantId,
user: &UserId,
provider: &str,
) -> Result<Option<StoredDelegation>, String> {
let guard = self.inner.lock().map_err(|e| e.to_string())?;
Ok(guard.get(&(*tenant, *user, provider.to_string())).cloned())
}
async fn save(
&self,
tenant: &TenantId,
user: &UserId,
credential: StoredDelegation,
) -> Result<(), String> {
let mut guard = self.inner.lock().map_err(|e| e.to_string())?;
let key = (*tenant, *user, credential.provider.clone());
guard.insert(key, credential);
Ok(())
}
async fn revoke(&self, tenant: &TenantId, user: &UserId, provider: &str) -> Result<(), String> {
let mut guard = self.inner.lock().map_err(|e| e.to_string())?;
guard.remove(&(*tenant, *user, provider.to_string()));
Ok(())
}
}
pub(crate) fn store_err(e: String) -> DelegatedError {
DelegatedError::Store(e)
}