Skip to main content

cedar_policy_core/tpe/
err.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! This module contains possible errors thrown by various components of the
18//! type-aware partial evaluator.
19
20use miette::Diagnostic;
21use smol_str::SmolStr;
22use thiserror::Error;
23
24use crate::{
25    ast::{Eid, EntityType, EntityUID, PartialValueToValueError},
26    entities::{conformance::err::EntitySchemaConformanceError, err::Duplicate},
27    evaluator::{evaluation_errors::UnlinkedSlotError, EvaluationError},
28    transitive_closure::TcError,
29    validator::{RequestValidationError, ValidationError},
30};
31
32/// Error thrown when encountered an action
33#[derive(Debug, Error, Diagnostic)]
34#[error("Unexpected action: `{}`", .action)]
35pub struct UnexpectedActionError {
36    pub(super) action: EntityUID,
37}
38
39/// Error thrown when deserializing a [`crate::tpe::entities::PartialEntity`]
40#[derive(Debug, Error, Diagnostic)]
41pub enum JsonDeserializationError {
42    /// Error thrown when deserializing concrete components
43    #[error(transparent)]
44    #[diagnostic(transparent)]
45    Concrete(#[from] crate::entities::json::err::JsonDeserializationError),
46    /// Error thrown when encountered an action
47    /// Actions are automatically inserted from a schema
48    #[error(transparent)]
49    #[diagnostic(transparent)]
50    UnexpectedAction(#[from] UnexpectedActionError),
51    /// Error thrown when a restricted expression does not evaluate to a value
52    #[error(transparent)]
53    #[diagnostic(transparent)]
54    RestrictedExprEvaluation(#[from] EvaluationError),
55}
56
57/// Error thrown when validating a [`crate::tpe::entities::PartialEntity`]
58#[derive(Debug, Error, Diagnostic)]
59pub enum EntityValidationError {
60    /// Error thrown when validating concrete components
61    #[error(transparent)]
62    #[diagnostic(transparent)]
63    Concrete(#[from] EntitySchemaConformanceError),
64    /// Error thrown when an action component is unknown
65    #[error(transparent)]
66    #[diagnostic(transparent)]
67    UnknownActionComponent(#[from] UnknownActionComponentError),
68    /// Error thrown when an action's ancestors do not match the schema
69    #[error(transparent)]
70    #[diagnostic(transparent)]
71    MismatchedActionAncestors(#[from] MismatchedActionAncestorsError),
72}
73
74/// Error thrown when an action has unknown ancestors/attrs/tags
75#[derive(Debug, Error, Diagnostic)]
76#[error("action `{}` has unknown ancestors/attrs/tags", .action)]
77pub struct UnknownActionComponentError {
78    pub(super) action: EntityUID,
79}
80
81/// Error thrown when an action's ancestors do not match the schema
82#[derive(Debug, Error, Diagnostic)]
83#[error("action `{}`'s ancestors do not match the schema", .action)]
84pub struct MismatchedActionAncestorsError {
85    pub(super) action: EntityUID,
86}
87
88/// Error thrown when an ancestor of an ancestor is unknown
89#[derive(Debug, Error, Diagnostic)]
90#[error("ancestor `{ancestor}` of `{uid}` has unknown ancestors")]
91#[diagnostic(help(
92    "an entity with known ancestors cannot have an ancestor whose own ancestors are unknown"
93))]
94pub struct AncestorValidationError {
95    pub(crate) uid: EntityUID,
96    pub(crate) ancestor: EntityUID,
97}
98
99/// Errors for TPE
100#[derive(Debug, Error, Diagnostic)]
101pub enum TpeError {
102    /// Error thrown when there is no matching request environment according to
103    /// a schema
104    #[error(transparent)]
105    #[diagnostic(transparent)]
106    NoMatchingReqEnv(#[from] NoMatchingReqEnvError),
107    /// Error thrown when the policy does not typecheck against the schema
108    #[error(transparent)]
109    #[diagnostic(transparent)]
110    Validation(#[from] PolicyValidationError),
111    /// Error when an expression is not supported by batched evaluation
112    #[error(transparent)]
113    #[diagnostic(transparent)]
114    ExprToResidualError(#[from] ExprToResidualError),
115}
116
117/// Error thrown when a policy fails to typecheck against the schema during
118/// type-aware partial evaluation
119#[derive(Debug, Error, Diagnostic)]
120#[error("policy failed to validate against the schema")]
121pub struct PolicyValidationError {
122    #[related]
123    pub(super) errors: Vec<ValidationError>,
124}
125
126impl PolicyValidationError {
127    pub(crate) fn new(errors: Vec<ValidationError>) -> Self {
128        Self { errors }
129    }
130
131    /// The underlying validation errors
132    pub fn errors(&self) -> impl Iterator<Item = &ValidationError> {
133        self.errors.iter()
134    }
135}
136
137/// Residuals require fully typed expressions without
138/// unknowns or parse errors.
139#[derive(Debug, Error, Diagnostic)]
140#[non_exhaustive]
141pub enum ExprToResidualError {
142    /// Expression is missing type annotation
143    #[error(transparent)]
144    #[diagnostic(transparent)]
145    MissingTypeAnnotation(#[from] MissingTypeAnnotationError),
146    /// Expression contains a slot which is not supported in residuals
147    #[error(transparent)]
148    #[diagnostic(transparent)]
149    UnlinkedSlotError(#[from] UnlinkedSlotError),
150    /// Expression contains an unknown which is not supported in residuals
151    #[error(transparent)]
152    #[diagnostic(transparent)]
153    UnknownNotSupported(#[from] UnknownNotSupportedError),
154    /// Expression contains an error which is not supported in residuals
155    #[error(transparent)]
156    #[diagnostic(transparent)]
157    ErrorNotSupported(#[from] ErrorNotSupportedError),
158}
159
160/// Error thrown when expression is missing type annotation
161#[derive(Debug, Error, Diagnostic)]
162#[error("expression is missing a type annotation")]
163#[diagnostic(help(
164    "expressions must be typechecked by the policy validator before partial evaluation"
165))]
166pub struct MissingTypeAnnotationError;
167
168/// Error thrown when expression contains an unknown which is not supported in residuals
169#[derive(Debug, Error, Diagnostic)]
170#[error("expression contains an unknown, which is not supported in residuals")]
171pub struct UnknownNotSupportedError;
172
173/// Error thrown when expression contains an error which is not supported in residuals
174#[derive(Debug, Error, Diagnostic)]
175#[error("expression contains an error node, which is not supported in residuals")]
176pub struct ErrorNotSupportedError;
177
178/// Error when a request was expected to be concrete
179#[derive(Debug, Error, Diagnostic)]
180#[error("expected a concrete request, but found a partial request")]
181pub struct PartialRequestError {}
182
183/// Error thrown when there is no matching request environment according to a
184/// schema
185#[derive(Debug, Error, Diagnostic)]
186#[error("no request environment in the schema matches the given request")]
187pub struct NoMatchingReqEnvError;
188
189/// Error thrown when using a [`crate::tpe::request::RequestBuilder`]
190#[derive(Debug, Error, Diagnostic)]
191pub enum RequestBuilderError {
192    /// Error thrown when the request cannot be validated
193    #[error(transparent)]
194    #[diagnostic(transparent)]
195    Validation(#[from] RequestValidationError),
196    /// Error thrown when attempting to add a principal when one exists
197    #[error(transparent)]
198    #[diagnostic(transparent)]
199    ExistingPrincipal(#[from] ExistingPrincipalError),
200    /// Error thrown when attempting to add a resource when one exists
201    #[error(transparent)]
202    #[diagnostic(transparent)]
203    ExistingResource(#[from] ExistingResourceError),
204    /// Error thrown when attempting to add a context when one exists
205    #[error("a context has already been set on this request")]
206    ExistingContext,
207    /// Error thrown when attempting to add a principal with an incorrect
208    /// entity type
209    #[error(transparent)]
210    #[diagnostic(transparent)]
211    IncorrectPrincipalEntityType(#[from] IncorrectPrincipalEntityTypeError),
212    /// Error thrown when attempting to add a resource with an incorrect
213    /// entity type
214    #[error(transparent)]
215    #[diagnostic(transparent)]
216    IncorrectResourceEntityType(#[from] IncorrectResourceEntityTypeError),
217    /// Error thrown when the context candidate contains unknowns
218    #[error("context candidate contains unknowns")]
219    UnknownContextCandidate,
220}
221
222/// Error thrown when attempting to add a principal when one already exists
223#[derive(Debug, Error, Diagnostic)]
224#[error("a principal (`{principal}`) has already been set on this request")]
225pub struct ExistingPrincipalError {
226    pub(super) principal: EntityUID,
227}
228
229/// Error thrown when attempting to add a resource when one already exists
230#[derive(Debug, Error, Diagnostic)]
231#[error("a resource (`{resource}`) has already been set on this request")]
232pub struct ExistingResourceError {
233    pub(super) resource: EntityUID,
234}
235
236/// Error thrown when attempting to add a principal with an incorrect
237/// entity type
238#[derive(Debug, Error, Diagnostic)]
239#[error("principal type `{ty}` does not match the partial request's principal type `{expected}`")]
240pub struct IncorrectPrincipalEntityTypeError {
241    pub(super) ty: EntityType,
242    pub(super) expected: EntityType,
243}
244
245/// Error thrown when attempting to add a resource with an incorrect
246/// entity type
247#[derive(Debug, Error, Diagnostic)]
248#[error("resource type `{ty}` does not match the partial request's resource type `{expected}`")]
249pub struct IncorrectResourceEntityTypeError {
250    pub(super) ty: EntityType,
251    pub(super) expected: EntityType,
252}
253
254/// Error thrown when constructing [`crate::tpe::entities::PartialEntities`]
255#[derive(Debug, Error, Diagnostic)]
256pub enum EntitiesError {
257    /// Error thrown when validating concrete components
258    #[error(transparent)]
259    #[diagnostic(transparent)]
260    Deserialization(#[from] JsonDeserializationError),
261    /// Error thrown when validating a [`crate::tpe::entities::PartialEntity`]
262    #[error(transparent)]
263    #[diagnostic(transparent)]
264    Validation(#[from] EntityValidationError),
265    /// Error thrown when validating the ancestors of a [`crate::tpe::entities::PartialEntity`]
266    #[error(transparent)]
267    #[diagnostic(transparent)]
268    AncestorValidation(#[from] AncestorValidationError),
269    /// Error thrown when computing TC
270    #[error(transparent)]
271    #[diagnostic(transparent)]
272    TCComputation(#[from] TcError<EntityUID>),
273    /// Error constructing the Entities collection due to encountering two
274    /// different entities with the same Entity UID
275    #[error(transparent)]
276    #[diagnostic(transparent)]
277    Duplicate(#[from] Duplicate),
278    /// Errors encountered when converting `PartialValue` to `Value`
279    #[error(transparent)]
280    #[diagnostic(transparent)]
281    PartialValueToValue(#[from] PartialValueToValueError),
282}
283
284/// Error thrown when checking the consistency between [`crate::tpe::entities::PartialEntities`] and
285/// [`crate::entities::Entities`]
286#[derive(Debug, Error, Diagnostic)]
287pub enum EntitiesConsistencyError {
288    /// Error thrown when there is an entity missing in the concrete entities
289    #[error(transparent)]
290    #[diagnostic(transparent)]
291    MissingEntity(#[from] MissingEntityError),
292    /// Error thrown when concrete entities contain unknown entities
293    #[error(transparent)]
294    #[diagnostic(transparent)]
295    UnknownEntity(#[from] UnknownEntityError),
296    /// Error thrown when a concrete entity and a partial entity are
297    /// inconsistent
298    #[error(transparent)]
299    #[diagnostic(transparent)]
300    InconsistentEntity(#[from] EntityConsistencyError),
301}
302
303/// Error thrown when checking the consistency between [`crate::tpe::entities::PartialEntity`] and
304/// [`crate::ast::Entity`]
305#[derive(Debug, Error, Diagnostic)]
306pub enum EntityConsistencyError {
307    /// Error thrown when the concrete entity contains unknown attribute
308    #[error(transparent)]
309    #[diagnostic(transparent)]
310    UnknownAttribute(#[from] UnknownAttributeError),
311    /// Error thrown when attributes mismatch
312    #[error(transparent)]
313    #[diagnostic(transparent)]
314    MismatchedAttribute(#[from] MismatchedAttributeError),
315    /// Error thrown when ancestors do not match
316    #[error(transparent)]
317    #[diagnostic(transparent)]
318    MismatchedAncestor(#[from] MismatchedAncestorError),
319    /// Error thrown when the concrete entity contains unknown tag
320    #[error(transparent)]
321    #[diagnostic(transparent)]
322    UnknownTag(#[from] UnknownTagError),
323    /// Error thrown when tags mismatch
324    #[error(transparent)]
325    #[diagnostic(transparent)]
326    MismatchedTag(#[from] MismatchedTagError),
327}
328
329/// Error thrown when the concrete entity contains unknown attribute
330#[derive(Debug, Error, Diagnostic)]
331#[error("concrete entity `{uid}` has attribute `{attr}` not present in the partial entity")]
332pub struct UnknownAttributeError {
333    pub(super) uid: EntityUID,
334    pub(super) attr: SmolStr,
335}
336
337/// Error thrown when attributes mismatch
338#[derive(Debug, Error, Diagnostic)]
339#[error("concrete entity `{uid}` has attribute values that do not match the partial entity")]
340pub struct MismatchedAttributeError {
341    pub(super) uid: EntityUID,
342}
343
344/// Error thrown when the concrete entity contains unknown tag
345#[derive(Debug, Error, Diagnostic)]
346#[error("concrete entity `{uid}` has tag `{tag}` not present in the partial entity")]
347pub struct UnknownTagError {
348    pub(super) uid: EntityUID,
349    pub(super) tag: SmolStr,
350}
351
352/// Error thrown when tags mismatch
353#[derive(Debug, Error, Diagnostic)]
354#[error("concrete entity `{uid}` has tag values that do not match the partial entity")]
355pub struct MismatchedTagError {
356    pub(super) uid: EntityUID,
357}
358
359/// Error thrown when ancestors do not match
360#[derive(Debug, Error, Diagnostic)]
361#[error("concrete entity `{uid}` has ancestors that do not match the partial entity")]
362pub struct MismatchedAncestorError {
363    pub(super) uid: EntityUID,
364}
365
366/// Error thrown when when there is an entity missing in the concrete entities
367#[derive(Debug, Error, Diagnostic)]
368#[error("entity `{uid}` is present in the partial entities but missing from the concrete entities")]
369pub struct MissingEntityError {
370    pub(super) uid: EntityUID,
371}
372
373/// Error thrown when concrete entities contain unknown entities
374#[derive(Debug, Error, Diagnostic)]
375#[error("concrete entities contain unknown entity `{uid}`")]
376pub struct UnknownEntityError {
377    pub(super) uid: EntityUID,
378}
379
380/// Error thrown when some requested entities were not loaded
381#[derive(Debug, Error, Diagnostic)]
382#[error("failed to load entities: {}", .missing_entities.iter().map(|uid| uid.to_string()).collect::<Vec<_>>().join(", "))]
383pub struct MissingEntitiesError {
384    pub(super) missing_entities: Vec<EntityUID>,
385}
386
387impl MissingEntitiesError {
388    /// Construct a new [`MissingEntitiesError`]
389    pub fn new(missing_entities: Vec<EntityUID>) -> Self {
390        Self { missing_entities }
391    }
392}
393
394/// Error thrown when a [`crate::tpe::request::PartialRequest`] is inconsistent with a [`crate::ast::Request`]
395#[derive(Debug, Error, Diagnostic)]
396pub enum RequestConsistencyError {
397    /// Error thrown when the concrete principal is unknown
398    #[error("the concrete request's principal is unknown")]
399    UnknownPrincipal,
400    /// Error thrown when the concrete resource is unknown
401    #[error("the concrete request's resource is unknown")]
402    UnknownResource,
403    /// Error thrown when the concrete action is unknown
404    #[error("the concrete request's action is unknown")]
405    UnknownAction,
406    /// Error thrown when the concrete context is unknown
407    #[error("the concrete request's context is unknown")]
408    UnknownContext,
409    /// Error thrown when principal types are inconsistent
410    #[error(transparent)]
411    #[diagnostic(transparent)]
412    InconsistentPrincipalType(#[from] InconsistentPrincipalTypeError),
413    /// Error thrown when principal eids are inconsistent
414    #[error(transparent)]
415    #[diagnostic(transparent)]
416    InconsistentPrincipalEid(#[from] InconsistentPrincipalEidError),
417    /// Error thrown when resource types are inconsistent
418    #[error(transparent)]
419    #[diagnostic(transparent)]
420    InconsistentResourceType(#[from] InconsistentResourceTypeError),
421    /// Error thrown when resource eids are inconsistent
422    #[error(transparent)]
423    #[diagnostic(transparent)]
424    InconsistentResourceEid(#[from] InconsistentResourceEidError),
425    /// Error thrown when actions are inconsistent
426    #[error(transparent)]
427    #[diagnostic(transparent)]
428    InconsistentAction(#[from] InconsistentActionError),
429    /// Error thrown when contexts are inconsistent
430    #[error("the partial and concrete request contexts do not match")]
431    InconsistentContext,
432    /// Error thrown when the concrete context contains unknowns
433    #[error("the concrete request's context contains unknowns")]
434    ConcreteContextContainsUnknowns,
435}
436
437/// Error thrown when principal types are inconsistent
438#[derive(Debug, Error, Diagnostic)]
439#[error("partial request principal type `{partial}` does not match concrete request principal type `{concrete}`")]
440pub struct InconsistentPrincipalTypeError {
441    pub(super) partial: EntityType,
442    pub(super) concrete: EntityType,
443}
444
445/// Error thrown when principal eids are inconsistent
446#[derive(Debug, Error, Diagnostic)]
447#[error("partial request principal id `{}` does not match concrete request principal id `{}`", .partial.escaped(), .concrete.escaped())]
448pub struct InconsistentPrincipalEidError {
449    pub(super) partial: Eid,
450    pub(super) concrete: Eid,
451}
452
453/// Error thrown when resource types are inconsistent
454#[derive(Debug, Error, Diagnostic)]
455#[error("partial request resource type `{partial}` does not match concrete request resource type `{concrete}`")]
456pub struct InconsistentResourceTypeError {
457    pub(super) partial: EntityType,
458    pub(super) concrete: EntityType,
459}
460
461/// Error thrown when resource eids are inconsistent
462#[derive(Debug, Error, Diagnostic)]
463#[error("partial request resource id `{}` does not match concrete request resource id `{}`", .partial.escaped(), .concrete.escaped())]
464pub struct InconsistentResourceEidError {
465    pub(super) partial: Eid,
466    pub(super) concrete: Eid,
467}
468
469/// Error thrown when actions are inconsistent
470#[derive(Debug, Error, Diagnostic)]
471#[error("partial request action `{partial}` does not match concrete request action `{concrete}`")]
472pub struct InconsistentActionError {
473    pub(super) partial: EntityUID,
474    pub(super) concrete: EntityUID,
475}
476
477/// Error thrown during reauthorization
478#[derive(Debug, Error, Diagnostic)]
479pub enum ReauthorizationError {
480    /// Error thrown when request validation fails
481    #[error(transparent)]
482    #[diagnostic(transparent)]
483    RequestValidation(#[from] RequestValidationError),
484    /// Error thrown when entity validation fails
485    #[error(transparent)]
486    #[diagnostic(transparent)]
487    EntityValidation(#[from] EntitySchemaConformanceError),
488    /// Error thrown when entities and partial entities are inconsistent
489    #[error(transparent)]
490    #[diagnostic(transparent)]
491    EntitiesConsistency(#[from] EntitiesConsistencyError),
492    /// Error thrown when request and partial request are inconsistent
493    #[error(transparent)]
494    #[diagnostic(transparent)]
495    RequestConsistency(#[from] RequestConsistencyError),
496}