use std::collections::HashSet;
use crate::authorize::{authorize_chain, AccessRequest, Decision, DenyReason};
use crate::capability::Ops;
use crate::chain::CapabilityChain;
use crate::directory::KeyDirectory;
use crate::error::AccessResult;
use crate::identity::Did;
use crate::revocation::RevocationSet;
#[derive(Clone, Debug, Default)]
pub struct AccessPolicy {
roster: HashSet<Did>,
require_accountable_root: bool,
}
impl AccessPolicy {
pub fn new() -> Self {
Self::default()
}
pub fn with_roster_member(mut self, did: impl Into<Did>) -> Self {
self.roster.insert(did.into());
self
}
pub fn requiring_accountable_agents(mut self) -> Self {
self.require_accountable_root = true;
self
}
pub fn is_roster(&self, did: &Did) -> bool {
self.roster.contains(did)
}
}
pub fn gate(
policy: &AccessPolicy,
chain: Option<&CapabilityChain>,
request: &AccessRequest,
directory: &dyn KeyDirectory,
now_unix: u64,
revocations: &RevocationSet,
) -> AccessResult<Decision> {
if request.op == Ops::READ && policy.is_roster(request.bearer) {
return Ok(Decision::Allow);
}
let chain = match chain {
Some(c) => c,
None => return Ok(Decision::Deny(DenyReason::NoCapability)),
};
let decision = authorize_chain(chain, request, directory, now_unix, revocations)?;
if !decision.is_allowed() {
return Ok(decision);
}
if policy.require_accountable_root {
let root_issuer = &chain.links()[0].capability.issuer;
if !policy.is_roster(root_issuer) {
return Ok(Decision::Deny(DenyReason::NotAccountable));
}
}
Ok(Decision::Allow)
}