#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Capability {
Pure,
ProcessLocal,
Clock,
Entropy,
ExternalIo,
Spawn,
}
impl Capability {
const ALL: [Self; 6] = [
Self::Pure,
Self::ProcessLocal,
Self::Clock,
Self::Entropy,
Self::ExternalIo,
Self::Spawn,
];
}
pub trait CapabilityPolicy: Send + Sync {
fn is_granted(&self, capability: Capability) -> bool;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct LeastAuthorityPolicy;
impl CapabilityPolicy for LeastAuthorityPolicy {
fn is_granted(&self, capability: Capability) -> bool {
matches!(capability, Capability::Pure | Capability::ProcessLocal)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AllCapabilitiesPolicy;
impl CapabilityPolicy for AllCapabilitiesPolicy {
fn is_granted(&self, _capability: Capability) -> bool {
true
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CapabilitySet {
grants: Vec<Capability>,
}
impl CapabilitySet {
#[must_use]
pub fn all() -> Self {
Self::from_slice(&Capability::ALL)
}
#[must_use]
pub fn from_slice(capabilities: &[Capability]) -> Self {
let mut grants = Vec::new();
for capability in capabilities {
if !grants.contains(capability) {
grants.push(*capability);
}
}
Self { grants }
}
#[must_use]
pub fn grants(&self, capability: Capability) -> bool {
self.grants.contains(&capability)
}
#[must_use]
pub fn contains(&self, capability: Capability) -> bool {
self.grants(capability)
}
#[must_use]
pub fn is_subset_of(&self, other: &Self) -> bool {
self.grants
.iter()
.all(|capability| other.contains(*capability))
}
pub fn iter(&self) -> impl Iterator<Item = Capability> + '_ {
self.grants.iter().copied()
}
}
impl Default for CapabilitySet {
fn default() -> Self {
Self::all()
}
}
impl CapabilityPolicy for CapabilitySet {
fn is_granted(&self, capability: Capability) -> bool {
self.grants(capability)
}
}
#[cfg(test)]
mod tests {
use super::{
AllCapabilitiesPolicy, Capability, CapabilityPolicy, CapabilitySet, LeastAuthorityPolicy,
};
#[test]
fn least_authority_grants_only_pure() {
let policy = LeastAuthorityPolicy;
assert!(policy.is_granted(Capability::Pure));
assert!(policy.is_granted(Capability::ProcessLocal));
assert!(!policy.is_granted(Capability::Clock));
assert!(!policy.is_granted(Capability::Entropy));
assert!(!policy.is_granted(Capability::ExternalIo));
assert!(!policy.is_granted(Capability::Spawn));
}
#[test]
fn all_capabilities_grants_everything() {
let policy = AllCapabilitiesPolicy;
assert!(policy.is_granted(Capability::Pure));
assert!(policy.is_granted(Capability::ProcessLocal));
assert!(policy.is_granted(Capability::Clock));
assert!(policy.is_granted(Capability::Entropy));
assert!(policy.is_granted(Capability::ExternalIo));
assert!(policy.is_granted(Capability::Spawn));
}
#[test]
fn capability_set_grants_exact_members() {
let policy = CapabilitySet::from_slice(&[Capability::Pure, Capability::ProcessLocal]);
assert!(policy.grants(Capability::Pure));
assert!(policy.grants(Capability::ProcessLocal));
assert!(!policy.grants(Capability::Clock));
assert!(!policy.grants(Capability::Entropy));
assert!(!policy.grants(Capability::ExternalIo));
assert!(!policy.grants(Capability::Spawn));
}
}