mod attempts;
mod batch;
mod resource;
use std::{convert::Infallible, sync::Arc};
use serde::Serialize;
use crate::{
AuditEntry, AuditSink, AuthorizationError, Clock, Context, Decision, DecisionAuditId,
DecisionAuditOccurrence, DecisionSummary, FactResolutionEvidence, FactResolver, Lattice,
NoopAuditSink, NoopPolicyObserver, Policy, PolicyAnchor, PolicyId, PolicyObserver,
PreparedPolicy, ResolveError, SystemClock, evaluate, required_facts,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AuthorizationDecision<O> {
pub decision: Decision<O>,
pub audit_occurrence: DecisionAuditOccurrence,
}
pub struct Authorizer<R, A = NoopAuditSink, W = NoopPolicyObserver> {
resolver: Arc<R>,
audit_sink: Arc<A>,
observer: Arc<W>,
clock: Arc<dyn Clock>,
}
impl<R, A, W> Clone for Authorizer<R, A, W> {
fn clone(&self) -> Self {
Self {
resolver: Arc::clone(&self.resolver),
audit_sink: Arc::clone(&self.audit_sink),
observer: Arc::clone(&self.observer),
clock: Arc::clone(&self.clock),
}
}
}
impl<R> Authorizer<R> {
#[must_use]
pub fn unaudited(resolver: R) -> Self {
Self::new(resolver, NoopAuditSink)
}
}
impl<R, A> Authorizer<R, A> {
#[must_use]
pub fn new(resolver: R, audit_sink: A) -> Self {
Self {
resolver: Arc::new(resolver),
audit_sink: Arc::new(audit_sink),
observer: Arc::new(NoopPolicyObserver),
clock: Arc::new(SystemClock),
}
}
}
impl<R, A, W> Authorizer<R, A, W> {
#[must_use]
pub fn with_audit_sink<B>(self, audit_sink: B) -> Authorizer<R, B, W> {
Authorizer {
resolver: self.resolver,
audit_sink: Arc::new(audit_sink),
observer: self.observer,
clock: self.clock,
}
}
#[must_use]
pub fn with_observer<V>(self, observer: V) -> Authorizer<R, A, V> {
Authorizer {
resolver: self.resolver,
audit_sink: self.audit_sink,
observer: Arc::new(observer),
clock: self.clock,
}
}
#[must_use]
pub fn with_clock<C: Clock + 'static>(mut self, clock: C) -> Self {
self.clock = Arc::new(clock);
self
}
}
impl<R: FactResolver, A: AuditSink, W: PolicyObserver> Authorizer<R, A, W> {
pub async fn authorize<O: Lattice + Serialize + Send + Sync>(
&self,
policy: &PreparedPolicy<O>,
context: &Context,
) -> Result<AuthorizationDecision<O>, AuthorizationError<R::Error, A::Error>> {
self.run(
policy.policy(),
policy.anchor(),
policy.required_facts(),
context,
true,
)
.await
}
pub async fn prepare<O: Lattice + Serialize + Send + Sync>(
&self,
policy: &PreparedPolicy<O>,
context: &Context,
) -> Result<crate::PendingDecision<O>, AuthorizationError<R::Error, A::Error>> {
context.validate_at(self.clock.now_utc())?;
let resolution = self
.resolver
.resolve_for_decision(policy.required_facts(), context, self.clock.as_ref())
.await?;
self.assemble(
policy.policy(),
policy.anchor(),
Some(policy.required_facts()),
context,
&resolution,
)
}
pub async fn authorize_policy<O: Lattice + Serialize + Send + Sync>(
&self,
id: PolicyId,
policy: &Policy<O>,
context: &Context,
) -> Result<AuthorizationDecision<O>, AuthorizationError<R::Error, A::Error>> {
context.validate_at(self.clock.now_utc())?;
let anchor = PolicyAnchor::new(id, policy.hash().map_err(AuthorizationError::PolicyHash)?);
let required = required_facts(policy).into_iter().collect::<Vec<_>>();
self.run(policy, &anchor, &required, context, false).await
}
async fn run<O: Lattice + Serialize + Send + Sync>(
&self,
policy: &Policy<O>,
anchor: &PolicyAnchor,
required: &[crate::FactId],
context: &Context,
checked: bool,
) -> Result<AuthorizationDecision<O>, AuthorizationError<R::Error, A::Error>> {
context.validate_at(self.clock.now_utc())?;
let resolution = self
.resolver
.resolve_for_decision(required, context, self.clock.as_ref())
.await?;
self.finish(
policy,
anchor,
checked.then_some(required),
context,
&resolution,
)
.await
}
}
impl<R: Send + Sync, A: AuditSink, W: PolicyObserver> Authorizer<R, A, W> {
pub fn prepare_resolution<O: Lattice + Serialize + Send + Sync>(
&self,
policy: &PreparedPolicy<O>,
context: &Context,
resolution: &crate::FactResolution<crate::KnownFacts>,
) -> Result<crate::PendingDecision<O>, AuthorizationError<Infallible, A::Error>> {
self.assemble(
policy.policy(),
policy.anchor(),
Some(policy.required_facts()),
context,
resolution,
)
}
async fn finish<O: Lattice + Serialize + Send + Sync, E>(
&self,
policy: &Policy<O>,
anchor: &PolicyAnchor,
required: Option<&[crate::FactId]>,
context: &Context,
resolution: &crate::FactResolution<crate::KnownFacts>,
) -> Result<AuthorizationDecision<O>, AuthorizationError<E, A::Error>> {
let pending = self.assemble(policy, anchor, required, context, resolution)?;
self.persist(pending).await
}
fn assemble<O: Lattice + Serialize + Send + Sync, E>(
&self,
policy: &Policy<O>,
anchor: &PolicyAnchor,
required: Option<&[crate::FactId]>,
context: &Context,
resolution: &crate::FactResolution<crate::KnownFacts>,
) -> Result<crate::PendingDecision<O>, AuthorizationError<E, A::Error>> {
let received_at = self.clock.now_utc();
context.validate_at(received_at)?;
resolution
.validate_at(received_at)
.map_err(ResolveError::Resolution)?;
if let Some(fact) = required
.into_iter()
.flatten()
.find(|fact| resolution.facts().observation(fact).is_none())
{
return Err(ResolveError::MissingFact(fact.clone()).into());
}
let evidence = FactResolutionEvidence::from_resolution(resolution)?;
let decision = evaluate(policy, resolution.facts());
let occurrence = match context.decision_audit_occurrence() {
Some(value) => value.clone(),
None => {
DecisionAuditOccurrence::new(DecisionAuditId::generate(), self.clock.now_utc())?
}
};
let entry =
AuditEntry::from_decision(occurrence, anchor.clone(), &decision, context, evidence)
.map_err(|error| match error {
crate::AuditConstructionError::Trace(error) => AuthorizationError::Trace(error),
crate::AuditConstructionError::Entry(error) => {
AuthorizationError::AuditEntry(error)
}
})?;
Ok(crate::PendingDecision::new(decision, entry))
}
pub async fn persist_pending<O: Clone + Send + Sync>(
&self,
pending: &crate::PendingDecision<O>,
) -> Result<AuthorizationDecision<O>, A::Error> {
self.audit_sink.record(pending.entry()).await?;
let entry = pending.entry();
self.observer.observe(&DecisionSummary {
anchor: entry.anchor().clone(),
effect: entry.effect(),
obligations: entry.obligations().to_vec(),
consulted: entry.consulted().to_vec(),
});
Ok(AuthorizationDecision {
decision: pending.decision().clone(),
audit_occurrence: entry.occurrence(),
})
}
async fn persist<O: Clone + Send + Sync, E>(
&self,
pending: crate::PendingDecision<O>,
) -> Result<AuthorizationDecision<O>, AuthorizationError<E, A::Error>> {
self.persist_pending(&pending)
.await
.map_err(|source| AuthorizationError::Audit {
occurrence: pending.entry().occurrence(),
source,
})
}
}