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}