Skip to main content

hessra_cap_engine/
error.rs

1//! Error types for the capability engine.
2
3use crate::resolver::ResolverError;
4use crate::types::{ExposureLabel, ObjectId, Operation};
5use thiserror::Error;
6
7/// Errors from the capability engine.
8#[derive(Error, Debug)]
9pub enum EngineError {
10    /// Capability request denied by policy.
11    #[error("capability denied: {subject} cannot perform '{operation}' on '{target}': {reason}")]
12    CapabilityDenied {
13        subject: ObjectId,
14        target: ObjectId,
15        operation: Operation,
16        reason: String,
17    },
18
19    /// Capability request denied due to exposure restriction.
20    #[error("capability denied by exposure: label '{label}' blocks access to '{target}'")]
21    ExposureRestriction {
22        label: ExposureLabel,
23        target: ObjectId,
24    },
25
26    /// Identity token operation failed.
27    #[error("identity error: {0}")]
28    Identity(String),
29
30    /// Context token operation failed.
31    #[error("context error: {0}")]
32    Context(String),
33
34    /// Token error from underlying token crate.
35    #[error("token error: {0}")]
36    Token(#[from] hessra_token_core::TokenError),
37
38    /// Token creation or verification failed.
39    #[error("token operation failed: {0}")]
40    TokenOperation(String),
41
42    /// Policy backend error.
43    #[error("policy error: {0}")]
44    Policy(String),
45
46    /// A required designation declared in the schema was not supplied at mint
47    /// time (neither by the policy declaration nor by the caller).
48    #[error("missing required designation '{label}' for target '{target}' operation '{operation}'")]
49    MissingRequiredDesignation {
50        target: ObjectId,
51        operation: Operation,
52        label: String,
53    },
54
55    /// A static designation declared in policy references a label that does
56    /// not appear in the target's schema for the matched operation.
57    /// Surfaced at engine construction.
58    #[error(
59        "policy declares static designation '{label}' for target '{target}' operation '{operation}', but the schema does not declare that label"
60    )]
61    UnknownLabelInPolicy {
62        target: ObjectId,
63        operation: Operation,
64        label: String,
65    },
66
67    /// Cross-validation between policy and schema failed at engine construction.
68    /// Either a policy-declared static designation references an unknown label
69    /// (see [`EngineError::UnknownLabelInPolicy`]) or another structural
70    /// mismatch was detected.
71    #[error("schema/policy mismatch: {0}")]
72    SchemaPolicyMismatch(String),
73
74    /// A designation resolver failed during a `mint_with_context` call.
75    #[error("resolver error: {0}")]
76    Resolver(#[from] ResolverError),
77
78    /// The mint failed the delegated identity chain check: an ancestor of
79    /// `subject` either does not hold a grant for `(target, operation)`, or
80    /// holds a grant whose static designations are not all present in the
81    /// capability being minted. This enforces "sub-identity capabilities ⊆
82    /// parent identity capabilities" transitively, including the per-grant
83    /// designation envelope.
84    #[error(
85        "chain check failed: ancestor '{ancestor}' of '{subject}' does not encompass '{operation}' on '{target}': {reason}"
86    )]
87    ChainCheckFailed {
88        subject: ObjectId,
89        ancestor: ObjectId,
90        target: ObjectId,
91        operation: Operation,
92        reason: ChainCheckFailure,
93    },
94}
95
96/// Why the chain check rejected a mint.
97#[derive(Debug, Clone, thiserror::Error)]
98pub enum ChainCheckFailure {
99    /// The ancestor has no grant for the requested target+operation.
100    #[error("no grant for target/operation")]
101    NoGrant,
102    /// The ancestor has a grant for the requested target+operation, but it
103    /// carries a static designation that the cap being minted does not
104    /// include (or carries with a different value). The cap would therefore
105    /// exceed the ancestor's envelope on that label.
106    #[error(
107        "ancestor grant requires designation '{label}'='{value}' which the minted cap does not include"
108    )]
109    DesignationNotCovered { label: String, value: String },
110}