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}