use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use arc_swap::ArcSwap;
use thiserror::Error;
use crate::event::EventHub;
use frame_capability::ComponentId;
pub use frame_capability::{
Capability, CapabilityDenied, CapabilityKind, CapabilityRequest, CapabilityScope, CheckVerdict,
Grant, GrantProvenance, NetworkHost, PathRoot, ScopeValidationError,
};
pub struct CapabilityTable {
components: ArcSwap<HashMap<ComponentId, Arc<ComponentGrants>>>,
mutations: Mutex<()>,
}
impl CapabilityTable {
pub(crate) fn new() -> Self {
Self {
components: ArcSwap::from_pointee(HashMap::new()),
mutations: Mutex::new(()),
}
}
pub(crate) fn register_component(
&self,
component_id: ComponentId,
needs: Vec<CapabilityRequest>,
) -> Result<(), CapabilityMutationError> {
let _mutation = self
.mutations
.lock()
.map_err(|_| CapabilityMutationError::SynchronizationPoisoned)?;
let mut components = (**self.components.load()).clone();
components.insert(
component_id,
Arc::new(ComponentGrants {
needs,
active: AtomicBool::new(true),
grants: ArcSwap::from_pointee(HashMap::new()),
mutations: Mutex::new(HashMap::new()),
}),
);
self.components.store(Arc::new(components));
Ok(())
}
pub(crate) fn remove_component(
&self,
component_id: ComponentId,
) -> Result<(), CapabilityMutationError> {
let _table_mutation = self
.mutations
.lock()
.map_err(|_| CapabilityMutationError::SynchronizationPoisoned)?;
let component = self.component(component_id)?;
let mut grants = component
.mutations
.lock()
.map_err(|_| CapabilityMutationError::SynchronizationPoisoned)?;
component.active.store(false, Ordering::Release);
grants.clear();
component.grants.store(Arc::new(HashMap::new()));
drop(grants);
let mut components = (**self.components.load()).clone();
components.remove(&component_id);
self.components.store(Arc::new(components));
Ok(())
}
fn component(
&self,
component_id: ComponentId,
) -> Result<Arc<ComponentGrants>, CapabilityMutationError> {
self.components
.load()
.get(&component_id)
.cloned()
.ok_or(CapabilityMutationError::NotRegistered { component_id })
}
fn grant(
&self,
component_id: ComponentId,
request: CapabilityRequest,
provenance: GrantProvenance,
) -> Result<Grant, CapabilityMutationError> {
let component = self.component(component_id)?;
if !component.needs.contains(&request) {
return Err(CapabilityMutationError::UndeclaredNeed {
component_id,
request,
});
}
let mut grants = component
.mutations
.lock()
.map_err(|_| CapabilityMutationError::SynchronizationPoisoned)?;
if !component.active.load(Ordering::Acquire) {
return Err(CapabilityMutationError::NotRegistered { component_id });
}
if let Some(current) = grants.get(&request) {
return Err(CapabilityMutationError::AlreadyGranted {
current: current.clone(),
});
}
let grant = Grant {
component_id,
request: request.clone(),
provenance,
};
grants.insert(request, grant.clone());
component.grants.store(Arc::new(grants.clone()));
Ok(grant)
}
fn revoke(
&self,
component_id: ComponentId,
request: &CapabilityRequest,
) -> Result<RevokeOutcome, CapabilityMutationError> {
let component = self.component(component_id)?;
let mut grants = component
.mutations
.lock()
.map_err(|_| CapabilityMutationError::SynchronizationPoisoned)?;
if !component.active.load(Ordering::Acquire) {
return Err(CapabilityMutationError::NotRegistered { component_id });
}
let removed = grants.remove(request);
if removed.is_some() {
component.grants.store(Arc::new(grants.clone()));
}
Ok(removed.map_or(RevokeOutcome::NotGranted, RevokeOutcome::Revoked))
}
fn grants_for(&self, component_id: ComponentId) -> Result<Vec<Grant>, CapabilityMutationError> {
let component = self.component(component_id)?;
if !component.active.load(Ordering::Acquire) {
return Err(CapabilityMutationError::NotRegistered { component_id });
}
let mut grants: Vec<_> = component.grants.load().values().cloned().collect();
sort_grants(&mut grants);
Ok(grants)
}
fn full_table(&self) -> Vec<Grant> {
let components = self.components.load();
let mut grants = Vec::new();
for component in components.values() {
if component.active.load(Ordering::Acquire) {
grants.extend(component.grants.load().values().cloned());
}
}
sort_grants(&mut grants);
grants
}
}
fn sort_grants(grants: &mut [Grant]) {
grants.sort_by(|left, right| {
left.component_id
.cmp(&right.component_id)
.then_with(|| left.request.cmp(&right.request))
});
}
struct ComponentGrants {
needs: Vec<CapabilityRequest>,
active: AtomicBool,
grants: ArcSwap<HashMap<CapabilityRequest, Grant>>,
mutations: Mutex<HashMap<CapabilityRequest, Grant>>,
}
#[derive(Clone)]
pub struct CapabilityChecker {
component_id: ComponentId,
component: Arc<ComponentGrants>,
events: Arc<EventHub>,
}
impl CapabilityChecker {
#[must_use]
pub const fn component_id(&self) -> ComponentId {
self.component_id
}
pub fn check(&self, capability: &Capability) -> Result<CheckVerdict, CapabilityCheckError> {
let valid = capability.validate().is_ok();
let declared = valid
&& self
.component
.needs
.iter()
.any(|need| need.capability.covers(capability));
let allowed = self.component.active.load(Ordering::Acquire)
&& valid
&& self
.component
.grants
.load()
.values()
.any(|grant| grant.request.capability.covers(capability));
if allowed {
return Ok(CheckVerdict::Allowed);
}
let denial = CapabilityDenied {
component_id: self.component_id,
kind: capability.kind(),
scope: capability.scope(),
declared,
};
self.events
.publish_denial(denial.clone())
.map_err(|_| CapabilityCheckError::SynchronizationPoisoned)?;
Ok(CheckVerdict::Denied(denial))
}
}
pub struct HostCapabilityFacade<'a> {
table: &'a CapabilityTable,
events: Arc<EventHub>,
}
impl<'a> HostCapabilityFacade<'a> {
pub(crate) fn new(table: &'a CapabilityTable, events: Arc<EventHub>) -> Self {
Self { table, events }
}
pub fn grant(
&self,
component_id: ComponentId,
request: CapabilityRequest,
provenance: GrantProvenance,
) -> Result<Grant, CapabilityMutationError> {
self.table.grant(component_id, request, provenance)
}
pub fn revoke(
&self,
component_id: ComponentId,
request: &CapabilityRequest,
) -> Result<RevokeOutcome, CapabilityMutationError> {
self.table.revoke(component_id, request)
}
pub fn grants_for(
&self,
component_id: ComponentId,
) -> Result<Vec<Grant>, CapabilityMutationError> {
self.table.grants_for(component_id)
}
#[must_use]
pub fn full_table(&self) -> Vec<Grant> {
self.table.full_table()
}
pub fn checker(
&self,
component_id: ComponentId,
) -> Result<CapabilityChecker, CapabilityMutationError> {
Ok(CapabilityChecker {
component_id,
component: self.table.component(component_id)?,
events: Arc::clone(&self.events),
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RevokeOutcome {
Revoked(Grant),
NotGranted,
}
#[derive(Clone, Debug, Error, Eq, PartialEq)]
pub enum CapabilityMutationError {
#[error("component {component_id} is not registered in the capability table")]
NotRegistered {
component_id: ComponentId,
},
#[error("component {component_id} did not declare capability need {request:?}")]
UndeclaredNeed {
component_id: ComponentId,
request: CapabilityRequest,
},
#[error("capability grant already exists: {current:?}")]
AlreadyGranted {
current: Grant,
},
#[error("capability table synchronization is poisoned")]
SynchronizationPoisoned,
}
#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
pub enum CapabilityCheckError {
#[error("component {component_id} is not registered for capability checks")]
NotRegistered {
component_id: ComponentId,
},
#[error("capability check synchronization is poisoned")]
SynchronizationPoisoned,
}