appcore_capabilities/
catalog.rs1use crate::policy::enforce_requirements;
14use crate::{CapabilityError, CapabilityRequest, CapabilityResult};
15use appcore_contracts::ServiceId;
16use appcore_core::{CapabilityDescriptor, CapabilityName, CoreIdentity};
17use appcore_distributed_contracts::ServiceLeadershipGuard;
18use std::collections::HashMap;
19
20pub struct CapabilityEnforcementContext<'a> {
22 pub(crate) identity: &'a CoreIdentity,
23 pub(crate) service_id: &'a ServiceId,
24 pub(crate) leadership: Option<&'a dyn ServiceLeadershipGuard>,
25 pub(crate) now_ms: u64,
26 pub(crate) writes_allowed: bool,
27}
28
29impl<'a> CapabilityEnforcementContext<'a> {
30 pub fn new(identity: &'a CoreIdentity, service_id: &'a ServiceId, now_ms: u64) -> Self {
32 Self {
33 identity,
34 service_id,
35 leadership: None,
36 now_ms,
37 writes_allowed: true,
38 }
39 }
40
41 pub fn with_leadership(mut self, leadership: &'a dyn ServiceLeadershipGuard) -> Self {
43 self.leadership = Some(leadership);
44 self
45 }
46
47 pub fn with_writes_allowed(mut self, writes_allowed: bool) -> Self {
49 self.writes_allowed = writes_allowed;
50 self
51 }
52}
53
54#[derive(Debug, Clone, Default)]
56pub struct CapabilityCatalog {
57 descriptors: HashMap<CapabilityName, CapabilityDescriptor>,
58}
59
60impl CapabilityCatalog {
61 pub fn new() -> Self {
63 Self::default()
64 }
65
66 pub fn from_descriptors(
68 descriptors: impl IntoIterator<Item = CapabilityDescriptor>,
69 ) -> CapabilityResult<Self> {
70 let mut catalog = Self::new();
71 for descriptor in descriptors {
72 catalog.register_descriptor(descriptor)?;
73 }
74 Ok(catalog)
75 }
76
77 pub fn register_descriptor(
79 &mut self,
80 descriptor: CapabilityDescriptor,
81 ) -> CapabilityResult<()> {
82 if descriptor.version.is_empty() || descriptor.version.len() > 256 {
83 return Err(CapabilityError::HandlerRejected(
84 "invalid_descriptor_version_length".into(),
85 ));
86 }
87 if self.descriptors.contains_key(&descriptor.name) {
88 return Err(CapabilityError::DescriptorAlreadyRegistered(
89 descriptor.name.clone(),
90 ));
91 }
92 if self.descriptors.len() >= 4096 {
93 return Err(CapabilityError::HandlerRejected(
94 "descriptor_catalog_capacity_exceeded".into(),
95 ));
96 }
97 self.descriptors.insert(descriptor.name.clone(), descriptor);
98 Ok(())
99 }
100
101 pub fn descriptor(&self, capability: &CapabilityName) -> Option<&CapabilityDescriptor> {
103 self.descriptors.get(capability)
104 }
105
106 pub fn descriptors(&self) -> Vec<&CapabilityDescriptor> {
108 let mut descriptors = self.descriptors.values().collect::<Vec<_>>();
109 descriptors.sort_by(|left, right| left.name.as_str().cmp(right.name.as_str()));
110 descriptors
111 }
112
113 pub fn resolve_local(
115 &self,
116 request: &CapabilityRequest,
117 ) -> CapabilityResult<&CapabilityDescriptor> {
118 let descriptor = self
119 .descriptor(&request.capability)
120 .ok_or_else(|| CapabilityError::CapabilityNotDeclared(request.capability.clone()))?;
121 crate::policy::enforce_request_requirements(request, descriptor)?;
122 Ok(descriptor)
123 }
124
125 pub fn authorize_local(
127 &self,
128 request: &CapabilityRequest,
129 context: CapabilityEnforcementContext<'_>,
130 ) -> CapabilityResult<()> {
131 let descriptor = self.resolve_local(request)?;
132 enforce_requirements(
133 context.identity,
134 context.service_id,
135 request,
136 descriptor,
137 &context.identity.core_id,
138 context.leadership,
139 context.writes_allowed,
140 context.now_ms,
141 )
142 }
143}