cedar_policy_core/entities/json/
err.rs

1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
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
17use super::SchemaType;
18use crate::ast::{
19    EntityType, EntityUID, Expr, ExprKind, Name, RestrictedExpr, RestrictedExpressionError,
20};
21use crate::extensions::ExtensionsError;
22use smol_str::SmolStr;
23use thiserror::Error;
24
25/// Errors thrown during deserialization from JSON
26#[derive(Debug, Error)]
27pub enum JsonDeserializationError {
28    /// Error thrown by `serde_json`
29    #[error("{0}")]
30    Serde(#[from] serde_json::Error),
31    /// Contents of an `__expr` escape failed to parse as a Cedar expression.
32    ///
33    /// `__expr` is deprecated (starting with the 1.2 release), and once it is
34    /// removed, this error will also be removed.
35    #[error(transparent)]
36    ExprParseError(crate::parser::err::ParseError),
37    /// Contents of an `__entity` escape failed to parse as an entity reference
38    #[error(transparent)]
39    EntityParseError(crate::parser::err::ParseError),
40    /// Function name in an `__extn` escape failed to parse as an extension function name
41    #[error(transparent)]
42    ExtnParseError(crate::parser::err::ParseError),
43    /// Restricted expression error
44    #[error(transparent)]
45    RestrictedExpressionError(#[from] RestrictedExpressionError),
46    /// Error thrown by an operation on `Extensions`
47    #[error(transparent)]
48    ExtensionsError(#[from] ExtensionsError),
49    /// A field that needs to be a literal entity reference, was some other JSON value
50    #[error("{ctx}, expected a literal entity reference, but got {got}")]
51    ExpectedLiteralEntityRef {
52        /// Context of this error
53        ctx: Box<JsonDeserializationErrorContext>,
54        /// the expression we got instead
55        got: Box<Expr>,
56    },
57    /// A field that needs to be an extension value, was some other JSON value
58    #[error("{ctx}, expected an extension value, but got {got}")]
59    ExpectedExtnValue {
60        /// Context of this error
61        ctx: Box<JsonDeserializationErrorContext>,
62        /// the expression we got instead
63        got: Box<Expr>,
64    },
65    /// Contexts need to be records, but we got some other JSON value
66    #[error("Expected Context to be a record, but got {got}")]
67    ExpectedContextToBeRecord {
68        /// Expression we got instead
69        got: Box<RestrictedExpr>,
70    },
71    /// Parents of actions should be actions, but this action has a non-action parent
72    #[error("{uid} is an action, so it should not have a parent {parent}, which is not an action")]
73    ActionParentIsNonAction {
74        /// Action entity that had the invalid parent
75        uid: EntityUID,
76        /// Parent that is invalid
77        parent: EntityUID,
78    },
79    /// Schema-based parsing needed an implicit extension constructor, but no suitable
80    /// constructor was found
81    #[error("{ctx}, extension constructor for {arg_type} -> {return_type} not found")]
82    ImpliedConstructorNotFound {
83        /// Context of this error
84        ctx: Box<JsonDeserializationErrorContext>,
85        /// return type of the constructor we were looking for
86        return_type: Box<SchemaType>,
87        /// argument type of the constructor we were looking for
88        arg_type: Box<SchemaType>,
89    },
90    /// During schema-based parsing, encountered an entity of a type which is
91    /// not declared in the schema. (This error is only used for non-Action entity types.)
92    #[error("{uid} has type {} which is not declared in the schema{}",
93        &.uid.entity_type(),
94        match .suggested_types.as_slice() {
95            [] => String::new(),
96            [ty] => format!("; did you mean {ty}?"),
97            tys => format!("; did you mean one of {:?}?", tys.iter().map(ToString::to_string).collect::<Vec<String>>())
98        }
99    )]
100    UnexpectedEntityType {
101        /// Entity that had the unexpected type
102        uid: EntityUID,
103        /// Suggested similar entity types that actually are declared in the schema (if any)
104        suggested_types: Vec<EntityType>,
105    },
106    /// During schema-based parsing, encountered an action which was not
107    /// declared in the schema
108    #[error("Found entity data for {uid}, but it was not declared as an action in the schema")]
109    UndeclaredAction {
110        /// Action which was not declared in the schema
111        uid: EntityUID,
112    },
113    /// During schema-based parsing, encountered an action whose definition
114    /// doesn't precisely match the schema's declaration of that action
115    #[error("Definition of {uid} does not match the schema's declaration of that action")]
116    ActionDeclarationMismatch {
117        /// Action whose definition mismatched between entity data and schema
118        uid: EntityUID,
119    },
120    /// During schema-based parsing, encountered this attribute on this entity, but that
121    /// attribute shouldn't exist on entities of this type
122    #[error("Attribute {:?} on {uid} shouldn't exist according to the schema", &.attr)]
123    UnexpectedEntityAttr {
124        /// Entity that had the unexpected attribute
125        uid: EntityUID,
126        /// Name of the attribute that was unexpected
127        attr: SmolStr,
128    },
129    /// During schema-based parsing, encountered this attribute on a record, but
130    /// that attribute shouldn't exist on that record
131    #[error("{ctx}, record attribute {record_attr:?} shouldn't exist according to the schema")]
132    UnexpectedRecordAttr {
133        /// Context of this error
134        ctx: Box<JsonDeserializationErrorContext>,
135        /// Name of the (Record) attribute which was unexpected
136        record_attr: SmolStr,
137    },
138    /// During schema-based parsing, didn't encounter this attribute of a
139    /// record, but that attribute should have existed
140    #[error("Expected {uid} to have an attribute {attr:?}, but it didn't")]
141    MissingRequiredEntityAttr {
142        /// Entity that is missing a required attribute
143        uid: EntityUID,
144        /// Name of the attribute which was expected
145        attr: SmolStr,
146    },
147    /// During schema-based parsing, didn't encounter this attribute of a
148    /// record, but that attribute should have existed
149    #[error("{ctx}, expected the record to have an attribute {record_attr:?}, but it didn't")]
150    MissingRequiredRecordAttr {
151        /// Context of this error
152        ctx: Box<JsonDeserializationErrorContext>,
153        /// Name of the (Record) attribute which was expected
154        record_attr: SmolStr,
155    },
156    /// During schema-based parsing, the given attribute on the given entity had
157    /// a different type than the schema indicated to expect
158    #[error("{ctx}, type mismatch: attribute was expected to have type {expected}, but actually has type {actual}")]
159    TypeMismatch {
160        /// Context of this error
161        ctx: Box<JsonDeserializationErrorContext>,
162        /// Type which was expected
163        expected: Box<SchemaType>,
164        /// Type which was encountered instead
165        actual: Box<SchemaType>,
166    },
167    /// During schema-based parsing, found a set whose elements don't all have the
168    /// same type.  This doesn't match any possible schema.
169    #[error("{ctx}, set elements have different types: {ty1} and {ty2}")]
170    HeterogeneousSet {
171        /// Context of this error
172        ctx: Box<JsonDeserializationErrorContext>,
173        /// First element type which was found
174        ty1: Box<SchemaType>,
175        /// Second element type which was found
176        ty2: Box<SchemaType>,
177    },
178    /// During schema-based parsing, found a parent of a type that's not allowed
179    /// for that entity
180    #[error(
181        "{ctx}, {uid} is not allowed to have a parent of type {parent_ty} according to the schema"
182    )]
183    InvalidParentType {
184        /// Context of this error
185        ctx: Box<JsonDeserializationErrorContext>,
186        /// Entity that has an invalid parent type
187        uid: EntityUID,
188        /// Parent type which was invalid
189        parent_ty: Box<EntityType>, // boxed to avoid this variant being very large (and thus all JsonDeserializationErrors being large)
190    },
191}
192
193/// Errors thrown during serialization to JSON
194#[derive(Debug, Error)]
195pub enum JsonSerializationError {
196    /// Error thrown by `serde_json`
197    #[error("{0}")]
198    Serde(#[from] serde_json::Error),
199    /// Extension-function calls with 0 arguments are not currently supported in
200    /// our JSON format.
201    #[error("extension-function calls with 0 arguments are not currently supported in our JSON format. found call of {func}")]
202    ExtnCall0Arguments {
203        /// Name of the function which was called with 0 arguments
204        func: Name,
205    },
206    /// Extension-function calls with 2 or more arguments are not currently
207    /// supported in our JSON format.
208    #[error("extension-function calls with 2 or more arguments are not currently supported in our JSON format. found call of {func}")]
209    ExtnCall2OrMoreArguments {
210        /// Name of the function which was called with 2 or more arguments
211        func: Name,
212    },
213    /// Encountered a `Record` which can't be serialized to JSON because it
214    /// contains a key which is reserved as a JSON escape.
215    #[error("record uses reserved key: {key}")]
216    ReservedKey {
217        /// Reserved key which was used by the `Record`
218        key: SmolStr,
219    },
220    /// Encountered an `ExprKind` which we didn't expect. Either a case is
221    /// missing in `JSONValue::from_expr()`, or an internal invariant was
222    /// violated and there is a non-restricted expression in `RestrictedExpr`
223    #[error("unexpected restricted expression: {kind:?}")]
224    UnexpectedRestrictedExprKind {
225        /// `ExprKind` which we didn't expect to find
226        kind: ExprKind,
227    },
228}
229
230/// Gives information about the context of a JSON deserialization error (e.g.,
231/// where we were in the JSON document).
232#[derive(Debug, Clone)]
233pub enum JsonDeserializationErrorContext {
234    /// The error occurred while deserializing the attribute `attr` of an entity.
235    EntityAttribute {
236        /// Entity where the error occurred
237        uid: EntityUID,
238        /// Attribute where the error occurred
239        attr: SmolStr,
240    },
241    /// The error occurred while deserializing the `parents` field of an entity.
242    EntityParents {
243        /// Entity where the error occurred
244        uid: EntityUID,
245    },
246    /// The error occurred while deserializing the `uid` field of an entity.
247    EntityUid,
248    /// The error occurred while deserializing the `Context`.
249    Context,
250}
251
252impl std::fmt::Display for JsonDeserializationErrorContext {
253    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254        match self {
255            Self::EntityAttribute { uid, attr } => write!(f, "in attribute {attr:?} on {uid}"),
256            Self::EntityParents { uid } => write!(f, "in parents field of {uid}"),
257            Self::EntityUid => write!(f, "in uid field of <unknown entity>"),
258            Self::Context => write!(f, "while parsing context"),
259        }
260    }
261}