use chrono::{DateTime, Utc};
use cortex_core::{PolicyContribution, PolicyOutcome, TemporalAuthorityReport, TrustTier};
use cortex_store::repo::{AuthorityRepo, TemporalAuthorityQuery};
use cortex_store::Pool;
#[derive(Debug)]
pub struct TemporalAuthorityContribution {
pub report: TemporalAuthorityReport,
pub rule_id: &'static str,
}
impl TemporalAuthorityContribution {
#[must_use]
pub fn outcome(&self) -> PolicyOutcome {
self.report.policy_decision().final_outcome
}
#[must_use]
pub fn reason(&self) -> String {
if self.report.valid_now {
return format!(
"operator temporal authority valid for current use (key {})",
self.report.key_id,
);
}
let reasons = self
.report
.reasons
.iter()
.map(|reason| reason.wire_str())
.collect::<Vec<_>>()
.join(",");
if self.report.valid_at_event_time {
format!(
"operator temporal authority is historical evidence only (key {}; reasons: {reasons})",
self.report.key_id,
)
} else {
format!(
"operator temporal authority invalid at event time (key {}; reasons: {reasons})",
self.report.key_id,
)
}
}
#[must_use]
pub fn contribution(&self) -> PolicyContribution {
PolicyContribution::new(self.rule_id, self.outcome(), self.reason())
.expect("temporal authority contribution shape is valid")
}
}
#[derive(Debug)]
pub enum TemporalAuthorityError {
Store(cortex_store::StoreError),
}
impl std::fmt::Display for TemporalAuthorityError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Store(err) => write!(f, "authority repo read failed: {err}"),
}
}
}
impl std::error::Error for TemporalAuthorityError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Store(err) => Some(err),
}
}
}
impl From<cortex_store::StoreError> for TemporalAuthorityError {
fn from(err: cortex_store::StoreError) -> Self {
Self::Store(err)
}
}
pub fn revalidate_operator_temporal_authority(
pool: &Pool,
rule_id: &'static str,
key_id: &str,
event_time: DateTime<Utc>,
minimum_trust_tier: TrustTier,
) -> Result<TemporalAuthorityContribution, TemporalAuthorityError> {
let now = Utc::now();
let report = AuthorityRepo::new(pool).revalidate(&TemporalAuthorityQuery {
key_id: key_id.to_string(),
event_time,
now,
minimum_trust_tier,
})?;
Ok(TemporalAuthorityContribution { report, rule_id })
}
#[must_use]
pub fn revalidation_failed_invariant(surface: &str) -> String {
format!("{surface}.operator_temporal_authority.revalidation_failed")
}