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,
22    transitive_closure,
23};
24use itertools::Itertools;
25
26#[derive(Debug)]
27pub enum SchemaError {
28    /// Errors loading and parsing schema files
29    ParseFileFormat(serde_json::Error),
30    /// Errors occurring while computing or enforcing transitive closure on
31    /// action id hierarchy.
32    ActionTransitiveClosureError(Box<transitive_closure::Err<EntityUID>>),
33    /// Errors occurring while computing or enforcing transitive closure on
34    /// entity type hierarchy.
35    EntityTransitiveClosureError(transitive_closure::Err<Name>),
36    /// Error generated when processing a schema file that uses features which
37    /// are not yet supported by the implementation.
38    UnsupportedSchemaFeature(UnsupportedFeature),
39    /// Undeclared entity type(s) used in an entity type's memberOf field, an
40    /// action's appliesTo fields, or an attribute type in a context or entity
41    /// attributes record. Entity types are reported fully qualified, including
42    /// any implicit or explicit namespaces.
43    UndeclaredEntityTypes(HashSet<String>),
44    /// Undeclared action(s) used in an action's memberOf field.
45    UndeclaredActions(HashSet<String>),
46    /// Undeclared type used in entity or context attributes.
47    UndeclaredCommonType(HashSet<String>),
48    /// Duplicate specifications for an entity type. Argument is the name of
49    /// the duplicate entity type.
50    DuplicateEntityType(String),
51    /// Duplicate specifications for an action. Argument is the name of the
52    /// duplicate action.
53    DuplicateAction(String),
54    /// Duplicate specification for a reusable type declaration.
55    DuplicateCommonType(String),
56    /// Cycle in the schema's action hierarchy.
57    CycleInActionHierarchy,
58    /// Parse errors occurring while parsing an entity type.
59    EntityTypeParseError(Vec<ParseError>),
60    /// Parse errors occurring while parsing a namespace identifier.
61    NamespaceParseError(Vec<ParseError>),
62    /// Parse errors occurring while parsing an extension type.
63    ExtensionTypeParseError(Vec<ParseError>),
64    /// Parse errors occurring while parsing the name of one of reusable
65    /// declared types.
66    CommonTypeParseError(Vec<ParseError>),
67    /// The schema file included an entity type `Action` in the entity type
68    /// list. The `Action` entity type is always implicitly declared, and it
69    /// cannot currently have attributes or be in any groups, so there is no
70    /// purposes in adding an explicit entry.
71    ActionEntityTypeDeclared,
72    /// One or more action entities are declared with `attributes`, but this is
73    /// not currently supported.
74    ActionEntityAttributes(Vec<String>),
75    ContextOrShapeNotRecord,
76    /// An Action Entity (transitively) has an attribute that is an empty set
77    ActionEntityAttributeEmptySet,
78    /// An Action Entity (transitively) has an attribute of unsupported type (ExprEscape, EntityEscape or ExtnEscape)
79    ActionEntityAttributeUnsupportedType,
80}
81
82impl std::error::Error for SchemaError {}
83
84impl From<transitive_closure::Err<EntityUID>> for SchemaError {
85    fn from(e: transitive_closure::Err<EntityUID>) -> Self {
86        // we use code in transitive_closure to check for cycles in the action
87        // hierarchy, but in case of an error we want to report the more descriptive
88        // CycleInActionHierarchy instead of ActionTransitiveClosureError
89        match e {
90            transitive_closure::Err::TCEnforcementError { .. } => {
91                SchemaError::ActionTransitiveClosureError(Box::new(e))
92            }
93            transitive_closure::Err::HasCycle { .. } => SchemaError::CycleInActionHierarchy,
94        }
95    }
96}
97
98impl From<serde_json::Error> for SchemaError {
99    fn from(e: serde_json::Error) -> Self {
100        SchemaError::ParseFileFormat(e)
101    }
102}
103
104impl From<transitive_closure::Err<Name>> for SchemaError {
105    fn from(e: transitive_closure::Err<Name>) -> Self {
106        SchemaError::EntityTransitiveClosureError(e)
107    }
108}
109
110pub type Result<T> = std::result::Result<T, SchemaError>;
111
112impl SchemaError {
113    fn format_parse_errs(errs: &[ParseError]) -> String {
114        errs.iter()
115            .map(|e| e.to_string())
116            .collect::<Vec<_>>()
117            .join(", ")
118    }
119}
120
121impl std::fmt::Display for SchemaError {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        match self {
124            SchemaError::ParseFileFormat(e) => {
125                write!(f, "JSON Schema file could not be parsed: {e}")
126            }
127            SchemaError::ActionTransitiveClosureError(e) => {
128                write!(f, "Transitive closure error on action hierarchy: {}", e)
129            }
130            SchemaError::EntityTransitiveClosureError(e) => {
131                write!(f, "Transitive closure error on entity hierarchy: {}", e)
132            }
133            SchemaError::UnsupportedSchemaFeature(feat) => {
134                write!(f, "Unsupported feature used in schema: {feat}")
135            }
136            SchemaError::UndeclaredEntityTypes(e) => {
137                write!(f, "Undeclared entity types: {:?}", e)
138            }
139            SchemaError::UndeclaredActions(a) => {
140                write!(f, "Undeclared actions {:?}", a)
141            }
142            SchemaError::UndeclaredCommonType(t) => {
143                write!(f, "Undeclared common types {:?}", t)
144            }
145            SchemaError::DuplicateEntityType(e) => {
146                write!(f, "Duplicate entity type {e}")
147            }
148            SchemaError::DuplicateAction(a) => {
149                write!(f, "Duplicate action {}", a)
150            }
151            SchemaError::DuplicateCommonType(t) => {
152                write!(f, "Duplicate common type {t}")
153            }
154            SchemaError::CycleInActionHierarchy => {
155                write!(f, "Cycle in action hierarchy")
156            }
157            SchemaError::EntityTypeParseError(parse_errs) => {
158                write!(
159                    f,
160                    "Parse error in entity type: {}",
161                    Self::format_parse_errs(parse_errs),
162                )
163            }
164            SchemaError::NamespaceParseError(parse_errs) => {
165                write!(
166                    f,
167                    "Parse error in namespace identifier: {}",
168                    Self::format_parse_errs(parse_errs),
169                )
170            }
171            SchemaError::CommonTypeParseError(parse_errs) => {
172                write!(
173                    f,
174                    "Parse error in common type identifier: {}",
175                    Self::format_parse_errs(parse_errs),
176                )
177            }
178            SchemaError::ExtensionTypeParseError(parse_errs) => {
179                write!(
180                    f,
181                    "Parse error in extension type: {}",
182                    Self::format_parse_errs(parse_errs),
183                )
184            }
185            SchemaError::ActionEntityTypeDeclared => {
186                write!(f, "Entity type `Action` declared in `entityTypes` list.")
187            }
188            SchemaError::ActionEntityAttributes(actions) => {
189                write!(
190                    f,
191                    "Actions declared with `attributes`: [{}]",
192                    actions.iter().join(", ")
193                )
194            }
195            SchemaError::ContextOrShapeNotRecord => {
196                write!(
197                    f,
198                    "An entity shape or action context is declared with a type other than `Record`"
199                )
200            }
201            SchemaError::ActionEntityAttributeEmptySet => {
202                write!(f, "An action entity has an attribute that is an empty set")
203            }
204            SchemaError::ActionEntityAttributeUnsupportedType => {
205                write!(
206                    f,
207                    "An action entity has attribute with unsupported type: (escaped expression, entity or extension)"
208                )
209            }
210        }
211    }
212}
213
214#[derive(Debug)]
215pub enum UnsupportedFeature {
216    OpenRecordsAndEntities,
217}
218
219impl std::fmt::Display for UnsupportedFeature {
220    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221        match self {
222            UnsupportedFeature::OpenRecordsAndEntities => write!(
223                f,
224                "Records and entities with additional attributes are not yet implemented."
225            ),
226        }
227    }
228}