Skip to main content

hessra_cap_engine/
types.rs

1//! Core types for the capability engine.
2//!
3//! Provides the unified object model where everything is an object with a
4//! capability space, and the `PolicyBackend` trait for pluggable policy evaluation.
5
6use hessra_token_core::TokenTimeConfig;
7use serde::{Deserialize, Serialize};
8
9/// Object identifier in the unified namespace.
10///
11/// Object IDs are strings with conventional prefixes for human readability:
12/// `service:api-gateway`, `agent:openclaw`, `data:user-ssn`, `tool:web-search`.
13/// The engine does not interpret the prefix -- all objects are treated uniformly.
14#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct ObjectId(pub String);
16
17impl ObjectId {
18    pub fn new(id: impl Into<String>) -> Self {
19        Self(id.into())
20    }
21
22    pub fn as_str(&self) -> &str {
23        &self.0
24    }
25}
26
27impl std::fmt::Display for ObjectId {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        write!(f, "{}", self.0)
30    }
31}
32
33impl From<&str> for ObjectId {
34    fn from(s: &str) -> Self {
35        Self(s.to_string())
36    }
37}
38
39impl From<String> for ObjectId {
40    fn from(s: String) -> Self {
41        Self(s)
42    }
43}
44
45/// Operation on a target object.
46#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
47pub struct Operation(pub String);
48
49impl Operation {
50    pub fn new(op: impl Into<String>) -> Self {
51        Self(op.into())
52    }
53
54    pub fn as_str(&self) -> &str {
55        &self.0
56    }
57}
58
59impl std::fmt::Display for Operation {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "{}", self.0)
62    }
63}
64
65impl From<&str> for Operation {
66    fn from(s: &str) -> Self {
67        Self(s.to_string())
68    }
69}
70
71impl From<String> for Operation {
72    fn from(s: String) -> Self {
73        Self(s)
74    }
75}
76
77/// Exposure label for information flow control.
78///
79/// Exposure labels are hierarchical strings representing data sensitivity classifications:
80/// `PII:SSN`, `PHI:diagnosis`, `financial:account-number`.
81/// Wildcard matching (e.g., `PII:*`) is supported in policy rules.
82#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
83pub struct ExposureLabel(pub String);
84
85impl ExposureLabel {
86    pub fn new(label: impl Into<String>) -> Self {
87        Self(label.into())
88    }
89
90    pub fn as_str(&self) -> &str {
91        &self.0
92    }
93}
94
95impl std::fmt::Display for ExposureLabel {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        write!(f, "{}", self.0)
98    }
99}
100
101impl From<&str> for ExposureLabel {
102    fn from(s: &str) -> Self {
103        Self(s.to_string())
104    }
105}
106
107impl From<String> for ExposureLabel {
108    fn from(s: String) -> Self {
109        Self(s)
110    }
111}
112
113/// A capability grant: permission for a subject to perform operations on a target.
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct CapabilityGrant {
116    /// The target object this capability grants access to.
117    pub target: ObjectId,
118    /// The operations allowed on the target.
119    pub operations: Vec<Operation>,
120}
121
122/// Result of minting a capability token.
123///
124/// Contains the capability token and optionally an updated context token
125/// with exposure labels applied if the target was a classified data source.
126pub struct MintResult {
127    /// The minted capability token (base64-encoded).
128    pub token: String,
129    /// Updated context token with exposure labels, if a context was provided
130    /// and the target had data classifications.
131    pub context: Option<crate::ContextToken>,
132}
133
134/// Options for customizing capability minting beyond the basic case.
135///
136/// Used with `CapabilityEngine::mint_capability_with_options` to add namespace
137/// restrictions or custom time configuration to minted tokens.
138#[derive(Debug, Clone, Default)]
139pub struct MintOptions {
140    /// Restrict the token to a specific namespace.
141    pub namespace: Option<String>,
142    /// Override the default time config. If `None`, uses default (5 minutes).
143    pub time_config: Option<TokenTimeConfig>,
144}
145
146/// A designation label-value pair for narrowing capability scope.
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct Designation {
149    pub label: String,
150    pub value: String,
151}
152
153/// Configuration for minting identity tokens.
154#[derive(Debug, Clone)]
155pub struct IdentityConfig {
156    /// Token time-to-live in seconds.
157    pub ttl: i64,
158    /// Whether the identity token can be delegated to sub-identities.
159    pub delegatable: bool,
160    /// Optional namespace restriction.
161    pub namespace: Option<String>,
162}
163
164impl Default for IdentityConfig {
165    fn default() -> Self {
166        Self {
167            ttl: 3600,
168            delegatable: false,
169            namespace: None,
170        }
171    }
172}
173
174/// Configuration for context token sessions.
175#[derive(Debug, Clone)]
176pub struct SessionConfig {
177    /// Session time-to-live in seconds.
178    pub ttl: i64,
179}
180
181impl Default for SessionConfig {
182    fn default() -> Self {
183        Self { ttl: 3600 }
184    }
185}
186
187/// Result of a policy evaluation.
188#[derive(Debug, Clone)]
189pub enum PolicyDecision {
190    /// The capability request is granted.
191    Granted,
192    /// The capability request is denied by policy (object doesn't hold this capability).
193    Denied { reason: String },
194    /// The capability request is denied due to exposure restrictions.
195    DeniedByExposure {
196        label: ExposureLabel,
197        blocked_target: ObjectId,
198    },
199}
200
201impl PolicyDecision {
202    pub fn is_granted(&self) -> bool {
203        matches!(self, PolicyDecision::Granted)
204    }
205}
206
207/// Pluggable policy backend trait.
208///
209/// Implementations evaluate capability requests against their policy model.
210/// The default implementation is the CList backend in `hessra-cap-policy`.
211pub trait PolicyBackend: Send + Sync {
212    /// Evaluate whether a subject can access a target with the given operation,
213    /// considering any exposure labels from the subject's context.
214    fn evaluate(
215        &self,
216        subject: &ObjectId,
217        target: &ObjectId,
218        operation: &Operation,
219        exposure_labels: &[ExposureLabel],
220    ) -> PolicyDecision;
221
222    /// Get the data classification (exposure labels) for a target.
223    ///
224    /// When the engine mints a capability for a classified target, these labels
225    /// are automatically added to the subject's context token.
226    fn classification(&self, target: &ObjectId) -> Vec<ExposureLabel>;
227
228    /// List all capability grants for a subject (for introspection and audit).
229    fn list_grants(&self, subject: &ObjectId) -> Vec<CapabilityGrant>;
230
231    /// Check if a subject can delegate capabilities to other objects.
232    fn can_delegate(&self, subject: &ObjectId) -> bool;
233}