cedar_policy_validator/
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 std::collections::HashSet;
18
19use cedar_policy_core::{
20    ast::{EntityUID, Name},
21    parser::err::{ParseError, ParseErrors},
22    transitive_closure,
23};
24use itertools::Itertools;
25use thiserror::Error;
26
27#[derive(Debug, Error)]
28pub enum SchemaError {
29    /// Error thrown by the `serde_json` crate during deserialization
30    #[error("failed to parse schema: {0}")]
31    Serde(#[from] serde_json::Error),
32    /// Errors occurring while computing or enforcing transitive closure on
33    /// action hierarchy.
34    #[error("transitive closure computation/enforcement error on action hierarchy: {0}")]
35    ActionTransitiveClosure(Box<transitive_closure::TcError<EntityUID>>),
36    /// Errors occurring while computing or enforcing transitive closure on
37    /// entity type hierarchy.
38    #[error("transitive closure computation/enforcement error on entity type hierarchy: {0}")]
39    EntityTypeTransitiveClosure(#[from] transitive_closure::TcError<Name>),
40    /// Error generated when processing a schema file that uses unsupported features
41    #[error("unsupported feature used in schema: {0}")]
42    UnsupportedFeature(UnsupportedFeature),
43    /// Undeclared entity type(s) used in the `memberOf` field of an entity
44    /// type, the `appliesTo` fields of an action, or an attribute type in a
45    /// context or entity attribute record. Entity types in the error message
46    /// are fully qualified, including any implicit or explicit namespaces.
47    #[error("undeclared entity type(s): {0:?}")]
48    UndeclaredEntityTypes(HashSet<String>),
49    /// Undeclared action(s) used in the `memberOf` field of an action.
50    #[error("undeclared action(s): {0:?}")]
51    UndeclaredActions(HashSet<String>),
52    /// Undeclared common type(s) used in entity or context attributes.
53    #[error("undeclared common type(s): {0:?}")]
54    UndeclaredCommonTypes(HashSet<String>),
55    /// Duplicate specifications for an entity type. Argument is the name of
56    /// the duplicate entity type.
57    #[error("duplicate entity type: {0}")]
58    DuplicateEntityType(String),
59    /// Duplicate specifications for an action. Argument is the name of the
60    /// duplicate action.
61    #[error("duplicate action: {0}")]
62    DuplicateAction(String),
63    /// Duplicate specification for a reusable type declaration.
64    #[error("duplicate common type: {0}")]
65    DuplicateCommonType(String),
66    /// Cycle in the schema's action hierarchy.
67    #[error("cycle in action hierarchy")]
68    CycleInActionHierarchy,
69    /// Parse errors occurring while parsing an entity type.
70    #[error("parse error in entity type: {}", Self::format_parse_errs(.0))]
71    EntityTypeParseError(ParseErrors),
72    /// Parse errors occurring while parsing a namespace identifier.
73    #[error("parse error in namespace identifier: {}", Self::format_parse_errs(.0))]
74    NamespaceParseError(ParseErrors),
75    /// Parse errors occurring while parsing an extension type.
76    #[error("parse error in extension type: {}", Self::format_parse_errs(.0))]
77    ExtensionTypeParseError(ParseErrors),
78    /// Parse errors occurring while parsing the name of one of reusable
79    /// declared types.
80    #[error("parse error in common type identifier: {}", Self::format_parse_errs(.0))]
81    CommonTypeParseError(ParseErrors),
82    /// The schema file included an entity type `Action` in the entity type
83    /// list. The `Action` entity type is always implicitly declared, and it
84    /// cannot currently have attributes or be in any groups, so there is no
85    /// purposes in adding an explicit entry.
86    #[error("entity type `Action` declared in `entityTypes` list")]
87    ActionEntityTypeDeclared,
88    /// One or more action entities are declared with `attributes`, but this is
89    /// not currently supported.
90    #[error("action declared with `attribute`: [{}]", .0.iter().join(", "))]
91    ActionHasAttributes(Vec<String>),
92    /// `context` or `shape` fields are not records
93    #[error("{0} is declared with a type other than `Record`")]
94    ContextOrShapeNotRecord(ContextOrShape),
95    /// An action entity (transitively) has an attribute that is an empty set.
96    /// This error variant should only be used when `PermitAttributes` is enabled.
97    #[error("action `{0}` has an attribute that is an empty set")]
98    ActionAttributesContainEmptySet(EntityUID),
99    /// An action entity (transitively) has an attribute of unsupported type (`ExprEscape`, `EntityEscape` or `ExtnEscape`).
100    /// This error variant should only be used when `PermitAttributes` is enabled.
101    #[error(
102        "action `{0}` has an attribute with unsupported type: (escaped expression, entity or extension)"
103    )]
104    UnsupportedActionAttributeType(EntityUID),
105}
106
107impl From<transitive_closure::TcError<EntityUID>> for SchemaError {
108    fn from(e: transitive_closure::TcError<EntityUID>) -> Self {
109        // we use code in transitive_closure to check for cycles in the action
110        // hierarchy, but in case of an error we want to report the more descriptive
111        // CycleInActionHierarchy instead of ActionTransitiveClosureError
112        match e {
113            transitive_closure::TcError::MissingTcEdge { .. } => {
114                SchemaError::ActionTransitiveClosure(Box::new(e))
115            }
116            transitive_closure::TcError::HasCycle { .. } => SchemaError::CycleInActionHierarchy,
117        }
118    }
119}
120
121pub type Result<T> = std::result::Result<T, SchemaError>;
122
123impl SchemaError {
124    fn format_parse_errs(errs: &[ParseError]) -> String {
125        errs.iter().map(|e| e.to_string()).join(", ")
126    }
127}
128
129#[derive(Debug)]
130pub enum ContextOrShape {
131    ActionContext(EntityUID),
132    EntityTypeShape(Name),
133}
134
135impl std::fmt::Display for ContextOrShape {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        match self {
138            ContextOrShape::ActionContext(action) => write!(f, "Context for action {}", action),
139            ContextOrShape::EntityTypeShape(entity_type) => {
140                write!(f, "Shape for entity type {}", entity_type)
141            }
142        }
143    }
144}
145
146#[derive(Debug)]
147pub enum UnsupportedFeature {
148    OpenRecordsAndEntities,
149}
150
151impl std::fmt::Display for UnsupportedFeature {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        match self {
154            UnsupportedFeature::OpenRecordsAndEntities => write!(
155                f,
156                "Records and entities with additional attributes are not yet implemented."
157            ),
158        }
159    }
160}