cedar_policy_core/entities/conformance/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//! This module cotnains errors around entities not conforming to schemas
17use super::TypeMismatchError;
18use crate::ast::{EntityType, EntityUID};
19use crate::extensions::ExtensionFunctionLookupError;
20use miette::Diagnostic;
21use smol_str::SmolStr;
22use thiserror::Error;
23
24/// Errors raised when entities do not conform to the schema
25//
26// CAUTION: this type is publicly exported in `cedar-policy`.
27// Don't make fields `pub`, don't make breaking changes, and use caution
28// when adding public methods.
29#[derive(Debug, Diagnostic, Error)]
30#[non_exhaustive]
31pub enum EntitySchemaConformanceError {
32 /// Encountered attribute that shouldn't exist on entities of this type
33 #[error(transparent)]
34 #[diagnostic(transparent)]
35 UnexpectedEntityAttr(UnexpectedEntityAttr),
36 /// Encountered tag, but no tags should exist on entities of this type
37 #[error(transparent)]
38 #[diagnostic(transparent)]
39 UnexpectedEntityTag(UnexpectedEntityTag),
40 /// Didn't encounter attribute that should exist
41 #[error(transparent)]
42 #[diagnostic(transparent)]
43 MissingRequiredEntityAttr(MissingRequiredEntityAttr),
44 /// The given attribute on the given entity had a different type than the
45 /// schema indicated
46 #[error(transparent)]
47 #[diagnostic(transparent)]
48 TypeMismatch(TypeMismatch),
49 /// Found an ancestor of a type that's not allowed for that entity
50 #[error(transparent)]
51 #[diagnostic(transparent)]
52 InvalidAncestorType(InvalidAncestorType),
53 /// Encountered an entity of a type which is not declared in the schema.
54 /// Note that this error is only used for non-Action entity types.
55 #[error(transparent)]
56 #[diagnostic(transparent)]
57 UnexpectedEntityType(#[from] UnexpectedEntityTypeError),
58 /// Encountered an action which was not declared in the schema
59 #[error(transparent)]
60 #[diagnostic(transparent)]
61 UndeclaredAction(UndeclaredAction),
62 /// Encountered an action whose definition doesn't precisely match the
63 /// schema's declaration of that action
64 #[error(transparent)]
65 #[diagnostic(transparent)]
66 ActionDeclarationMismatch(ActionDeclarationMismatch),
67 /// Error looking up an extension function. This error can occur when
68 /// checking entity conformance because that may require getting information
69 /// about any extension functions referenced in entity attribute values.
70 #[error(transparent)]
71 #[diagnostic(transparent)]
72 ExtensionFunctionLookup(ExtensionFunctionLookup),
73}
74
75impl EntitySchemaConformanceError {
76 pub(crate) fn unexpected_entity_attr(uid: EntityUID, attr: impl Into<SmolStr>) -> Self {
77 Self::UnexpectedEntityAttr(UnexpectedEntityAttr {
78 uid,
79 attr: attr.into(),
80 })
81 }
82
83 pub(crate) fn unexpected_entity_tag(uid: EntityUID, tag: impl Into<SmolStr>) -> Self {
84 Self::UnexpectedEntityTag(UnexpectedEntityTag {
85 uid,
86 tag: tag.into(),
87 })
88 }
89
90 pub(crate) fn missing_entity_attr(uid: EntityUID, attr: impl Into<SmolStr>) -> Self {
91 Self::MissingRequiredEntityAttr(MissingRequiredEntityAttr {
92 uid,
93 attr: attr.into(),
94 })
95 }
96
97 pub(crate) fn type_mismatch(
98 uid: EntityUID,
99 attr: impl Into<SmolStr>,
100 err: TypeMismatchError,
101 ) -> Self {
102 Self::TypeMismatch(TypeMismatch {
103 uid,
104 attr: attr.into(),
105 err,
106 })
107 }
108
109 pub(crate) fn invalid_ancestor_type(uid: EntityUID, ancestor_type: EntityType) -> Self {
110 Self::InvalidAncestorType(InvalidAncestorType {
111 uid,
112 ancestor_ty: Box::new(ancestor_type),
113 })
114 }
115
116 pub(crate) fn undeclared_action(uid: EntityUID) -> Self {
117 Self::UndeclaredAction(UndeclaredAction { uid })
118 }
119
120 pub(crate) fn action_declaration_mismatch(uid: EntityUID) -> Self {
121 Self::ActionDeclarationMismatch(ActionDeclarationMismatch { uid })
122 }
123
124 pub(crate) fn extension_function_lookup(
125 uid: EntityUID,
126 attr: impl Into<SmolStr>,
127 err: ExtensionFunctionLookupError,
128 ) -> Self {
129 Self::ExtensionFunctionLookup(ExtensionFunctionLookup {
130 uid,
131 attr: attr.into(),
132 err,
133 })
134 }
135}
136
137/// Error looking up an extension function. This error can occur when
138/// checking entity conformance because that may require getting information
139/// about any extension functions referenced in entity attribute values.
140//
141// CAUTION: this type is publicly exported in `cedar-policy`.
142// Don't make fields `pub`, don't make breaking changes, and use caution
143// when adding public methods.
144#[derive(Debug, Error, Diagnostic)]
145#[error("in attribute `{attr}` on `{uid}`, {err}")]
146pub struct ExtensionFunctionLookup {
147 /// Entity where the error occurred
148 uid: EntityUID,
149 /// Name of the attribute where the error occurred
150 attr: SmolStr,
151 /// Underlying error
152 #[diagnostic(transparent)]
153 err: ExtensionFunctionLookupError,
154}
155
156/// Encountered an action whose definition doesn't precisely match the
157/// schema's declaration of that action
158//
159// CAUTION: this type is publicly exported in `cedar-policy`.
160// Don't make fields `pub`, don't make breaking changes, and use caution
161// when adding public methods.
162#[derive(Debug, Error, Diagnostic)]
163#[error("definition of action `{uid}` does not match its schema declaration")]
164#[diagnostic(help(
165 "to use the schema's definition of `{uid}`, simply omit it from the entities input data"
166))]
167pub struct ActionDeclarationMismatch {
168 /// Action whose definition mismatched between entity data and schema
169 uid: EntityUID,
170}
171
172/// Encountered an action which was not declared in the schema
173//
174// CAUTION: this type is publicly exported in `cedar-policy`.
175// Don't make fields `pub`, don't make breaking changes, and use caution
176// when adding public methods.
177#[derive(Debug, Error, Diagnostic)]
178#[error("found action entity `{uid}`, but it was not declared as an action in the schema")]
179pub struct UndeclaredAction {
180 /// Action which was not declared in the schema
181 uid: EntityUID,
182}
183
184/// Found an ancestor of a type that's not allowed for that entity
185//
186// CAUTION: this type is publicly exported in `cedar-policy`.
187// Don't make fields `pub`, don't make breaking changes, and use caution
188// when adding public methods.
189#[derive(Debug, Error, Diagnostic)]
190#[error(
191 "`{uid}` is not allowed to have an ancestor of type `{ancestor_ty}` according to the schema"
192)]
193pub struct InvalidAncestorType {
194 /// Entity that has an invalid ancestor type
195 uid: EntityUID,
196 /// Ancestor type which was invalid
197 ancestor_ty: Box<EntityType>, // boxed to avoid this variant being very large (and thus all EntitySchemaConformanceErrors being large)
198}
199
200/// Encountered attribute that shouldn't exist on entities of this type
201//
202// CAUTION: this type is publicly exported in `cedar-policy`.
203// Don't make fields `pub`, don't make breaking changes, and use caution
204// when adding public methods.
205#[derive(Debug, Error, Diagnostic)]
206#[error("attribute `{attr}` on `{uid}` should not exist according to the schema")]
207pub struct UnexpectedEntityAttr {
208 uid: EntityUID,
209 attr: SmolStr,
210}
211
212/// Encountered tag, but no tags should exist on entities of this type
213//
214// CAUTION: this type is publicly exported in `cedar-policy`.
215// Don't make fields `pub`, don't make breaking changes, and use caution
216// when adding public methods.
217#[derive(Debug, Error, Diagnostic)]
218#[error(
219 "found a tag `{tag}` on `{uid}`, but no tags should exist on `{uid}` according to the schema"
220)]
221pub struct UnexpectedEntityTag {
222 uid: EntityUID,
223 tag: SmolStr,
224}
225
226/// Didn't encounter attribute that should exist
227//
228// CAUTION: this type is publicly exported in `cedar-policy`.
229// Don't make fields `pub`, don't make breaking changes, and use caution
230// when adding public methods.
231#[derive(Debug, Error, Diagnostic)]
232#[error("expected entity `{uid}` to have attribute `{attr}`, but it does not")]
233pub struct MissingRequiredEntityAttr {
234 uid: EntityUID,
235 attr: SmolStr,
236}
237
238/// The given attribute on the given entity had a different type than the
239/// schema indicated
240//
241// CAUTION: this type is publicly exported in `cedar-policy`.
242// Don't make fields `pub`, don't make breaking changes, and use caution
243// when adding public methods.
244#[derive(Debug, Error, Diagnostic)]
245#[error("in attribute `{attr}` on `{uid}`, {err}")]
246pub struct TypeMismatch {
247 uid: EntityUID,
248 attr: SmolStr,
249 #[diagnostic(transparent)]
250 err: TypeMismatchError,
251}
252
253/// Encountered an entity of a type which is not declared in the schema.
254/// Note that this error is only used for non-Action entity types.
255//
256// CAUTION: this type is publicly exported in `cedar-policy`.
257// Don't make fields `pub`, don't make breaking changes, and use caution
258// when adding public methods.
259#[derive(Debug, Error)]
260#[error("entity `{uid}` has type `{}` which is not declared in the schema", .uid.entity_type())]
261pub struct UnexpectedEntityTypeError {
262 /// Entity that had the unexpected type
263 pub uid: EntityUID,
264 /// Suggested similar entity types that actually are declared in the schema (if any)
265 pub suggested_types: Vec<EntityType>,
266}
267
268impl Diagnostic for UnexpectedEntityTypeError {
269 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
270 match self.suggested_types.as_slice() {
271 [] => None,
272 [ty] => Some(Box::new(format!("did you mean `{ty}`?"))),
273 tys => Some(Box::new(format!(
274 "did you mean one of {:?}?",
275 tys.iter().map(ToString::to_string).collect::<Vec<String>>()
276 ))),
277 }
278 }
279}