cedar_policy_validator/
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
17use std::collections::HashSet;
18
19use cedar_policy_core::{
20    ast::{EntityAttrEvaluationError, EntityUID, Name},
21    transitive_closure,
22};
23use itertools::Itertools;
24use miette::Diagnostic;
25use thiserror::Error;
26
27use crate::human_schema::parser::HumanSyntaxParseErrors;
28
29#[derive(Debug, Error, Diagnostic)]
30pub enum HumanSchemaError {
31    #[error("{0}")]
32    #[diagnostic(transparent)]
33    Core(#[from] SchemaError),
34    #[error("{0}")]
35    IO(#[from] std::io::Error),
36    #[error("{0}")]
37    #[diagnostic(transparent)]
38    Parsing(#[from] HumanSyntaxParseErrors),
39}
40
41#[derive(Debug, Diagnostic, Error)]
42pub enum SchemaError {
43    /// Error thrown by the `serde_json` crate during deserialization
44    #[error("failed to parse schema: {0}")]
45    Serde(#[from] serde_json::Error),
46    /// Errors occurring while computing or enforcing transitive closure on
47    /// action hierarchy.
48    #[error("transitive closure computation/enforcement error on action hierarchy: {0}")]
49    #[diagnostic(transparent)]
50    ActionTransitiveClosure(Box<transitive_closure::TcError<EntityUID>>),
51    /// Errors occurring while computing or enforcing transitive closure on
52    /// entity type hierarchy.
53    #[error("transitive closure computation/enforcement error on entity type hierarchy: {0}")]
54    #[diagnostic(transparent)]
55    EntityTypeTransitiveClosure(#[from] transitive_closure::TcError<Name>),
56    /// Error generated when processing a schema file that uses unsupported features
57    #[error("unsupported feature used in schema: {0}")]
58    #[diagnostic(transparent)]
59    UnsupportedFeature(UnsupportedFeature),
60    /// Undeclared entity type(s) used in the `memberOf` field of an entity
61    /// type, the `appliesTo` fields of an action, or an attribute type in a
62    /// context or entity attribute record. Entity types in the error message
63    /// are fully qualified, including any implicit or explicit namespaces.
64    #[error("undeclared entity type(s): {0:?}")]
65    #[diagnostic(help(
66        "any entity types appearing anywhere in a schema need to be declared in `entityTypes`"
67    ))]
68    UndeclaredEntityTypes(HashSet<String>),
69    /// Undeclared action(s) used in the `memberOf` field of an action.
70    #[error("undeclared action(s): {0:?}")]
71    #[diagnostic(help("any actions appearing in `memberOf` need to be declared in `actions`"))]
72    UndeclaredActions(HashSet<String>),
73    /// This error occurs in either of the following cases (see discussion on #477):
74    ///     - undeclared common type(s) appearing in entity or context attributes
75    ///     - common type(s) (declared or not) appearing in declarations of other common types
76    #[error("undeclared common type(s), or common type(s) used in the declaration of another common type: {0:?}")]
77    #[diagnostic(help("any common types used in entity or context attributes need to be declared in `commonTypes`, and currently, common types may not reference other common types"))]
78    UndeclaredCommonTypes(HashSet<String>),
79    /// Duplicate specifications for an entity type. Argument is the name of
80    /// the duplicate entity type.
81    #[error("duplicate entity type `{0}`")]
82    DuplicateEntityType(String),
83    /// Duplicate specifications for an action. Argument is the name of the
84    /// duplicate action.
85    #[error("duplicate action `{0}`")]
86    DuplicateAction(String),
87    /// Duplicate specification for a reusable type declaration.
88    #[error("duplicate common type `{0}`")]
89    DuplicateCommonType(String),
90    /// Cycle in the schema's action hierarchy.
91    #[error("cycle in action hierarchy containing `{0}`")]
92    CycleInActionHierarchy(EntityUID),
93    /// Cycle in the schema's common type declarations.
94    #[error("cycle in common type references containing `{0}`")]
95    CycleInCommonTypeReferences(Name),
96    /// The schema file included an entity type `Action` in the entity type
97    /// list. The `Action` entity type is always implicitly declared, and it
98    /// cannot currently have attributes or be in any groups, so there is no
99    /// purposes in adding an explicit entry.
100    #[error("entity type `Action` declared in `entityTypes` list")]
101    ActionEntityTypeDeclared,
102    /// `context` or `shape` fields are not records
103    #[error("{0} is declared with a type other than `Record`")]
104    #[diagnostic(help("{}", match .0 {
105        ContextOrShape::ActionContext(_) => "action contexts must have type `Record`",
106        ContextOrShape::EntityTypeShape(_) => "entity type shapes must have type `Record`",
107    }))]
108    ContextOrShapeNotRecord(ContextOrShape),
109    /// An action entity (transitively) has an attribute that is an empty set.
110    /// The validator cannot assign a type to an empty set.
111    /// This error variant should only be used when `PermitAttributes` is enabled.
112    #[error("action `{0}` has an attribute that is an empty set")]
113    #[diagnostic(help(
114        "actions are not currently allowed to have attributes whose value is an empty set"
115    ))]
116    ActionAttributesContainEmptySet(EntityUID),
117    /// An action entity (transitively) has an attribute of unsupported type (`ExprEscape`, `EntityEscape` or `ExtnEscape`).
118    /// This error variant should only be used when `PermitAttributes` is enabled.
119    #[error("action `{0}` has an attribute with unsupported JSON representation: {1}")]
120    UnsupportedActionAttribute(EntityUID, String),
121    /// Error when evaluating an action attribute
122    #[error(transparent)]
123    #[diagnostic(transparent)]
124    ActionAttrEval(EntityAttrEvaluationError),
125    /// Error thrown when the schema contains the `__expr` escape.
126    /// Support for this escape form has been dropped.
127    #[error("the `__expr` escape is no longer supported")]
128    #[diagnostic(help("to create an entity reference, use `__entity`; to create an extension value, use `__extn`; and for all other values, use JSON directly"))]
129    ExprEscapeUsed,
130}
131
132impl From<transitive_closure::TcError<EntityUID>> for SchemaError {
133    fn from(e: transitive_closure::TcError<EntityUID>) -> Self {
134        // we use code in transitive_closure to check for cycles in the action
135        // hierarchy, but in case of an error we want to report the more descriptive
136        // CycleInActionHierarchy instead of ActionTransitiveClosureError
137        match e {
138            transitive_closure::TcError::MissingTcEdge { .. } => {
139                SchemaError::ActionTransitiveClosure(Box::new(e))
140            }
141            transitive_closure::TcError::HasCycle { vertex_with_loop } => {
142                SchemaError::CycleInActionHierarchy(vertex_with_loop)
143            }
144        }
145    }
146}
147
148pub type Result<T> = std::result::Result<T, SchemaError>;
149
150#[derive(Debug)]
151pub enum ContextOrShape {
152    ActionContext(EntityUID),
153    EntityTypeShape(Name),
154}
155
156impl std::fmt::Display for ContextOrShape {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        match self {
159            ContextOrShape::ActionContext(action) => write!(f, "Context for action {}", action),
160            ContextOrShape::EntityTypeShape(entity_type) => {
161                write!(f, "Shape for entity type {}", entity_type)
162            }
163        }
164    }
165}
166
167#[derive(Debug, Diagnostic, Error)]
168pub enum UnsupportedFeature {
169    #[error("records and entities with `additionalAttributes` are experimental, but the experimental `partial-validate` feature is not enabled")]
170    OpenRecordsAndEntities,
171    // Action attributes are allowed if `ActionBehavior` is `PermitAttributes`
172    #[error("action declared with attributes: [{}]", .0.iter().join(", "))]
173    ActionAttributes(Vec<String>),
174}