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