cedar_policy_validator/
validation_result.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 cedar_policy_core::{ast::PolicyID, parser::SourceInfo};
18use thiserror::Error;
19
20use crate::TypeErrorKind;
21
22/// Contains the result of policy validation. The result includes the list of of
23/// issues found by the validation and whether validation succeeds or fails.
24/// Validation succeeds if there are no fatal errors.  There are currently no
25/// non-fatal warnings, so any issues found will cause validation to fail.
26#[derive(Debug)]
27pub struct ValidationResult<'a> {
28    validation_errors: Vec<ValidationError<'a>>,
29}
30
31impl<'a> ValidationResult<'a> {
32    pub(crate) fn new(validation_errors: impl Iterator<Item = ValidationError<'a>>) -> Self {
33        Self {
34            validation_errors: validation_errors.collect::<Vec<_>>(),
35        }
36    }
37
38    /// True when validation passes. There are no fatal errors.
39    pub fn validation_passed(&self) -> bool {
40        self.validation_errors.is_empty()
41    }
42
43    /// Get the list of errors found by the validator.
44    pub fn validation_errors(&self) -> impl Iterator<Item = &ValidationError> {
45        self.validation_errors.iter()
46    }
47
48    /// Get the list of errors found by the validator.
49    pub fn into_validation_errors(self) -> impl Iterator<Item = ValidationError<'a>> {
50        self.validation_errors.into_iter()
51    }
52}
53
54/// An error generated by the validator when it finds a potential problem in a
55/// policy. The error contains a enumeration that specifies the kind of problem,
56/// and provides details specific to that kind of problem. The error also records
57/// where the problem was encountered.
58#[derive(Debug)]
59#[cfg_attr(test, derive(Eq, PartialEq))]
60pub struct ValidationError<'a> {
61    location: SourceLocation<'a>,
62    error_kind: ValidationErrorKind,
63}
64
65impl<'a> ValidationError<'a> {
66    pub(crate) fn with_policy_id(
67        id: &'a PolicyID,
68        source_info: Option<SourceInfo>,
69        error_kind: ValidationErrorKind,
70    ) -> Self {
71        Self {
72            error_kind,
73            location: SourceLocation::new(id, source_info),
74        }
75    }
76
77    /// Deconstruct this into its component source location and error kind.
78    pub fn into_location_and_error_kind(self) -> (SourceLocation<'a>, ValidationErrorKind) {
79        (self.location, self.error_kind)
80    }
81
82    /// Extract details about the exact issue detected by the validator.
83    pub fn error_kind(&self) -> &ValidationErrorKind {
84        &self.error_kind
85    }
86
87    /// Extract the location where the validator found the issue.
88    pub fn location(&self) -> &SourceLocation {
89        &self.location
90    }
91}
92
93/// Represents a location in Cedar policy source.
94#[derive(Debug, Eq, PartialEq)]
95pub struct SourceLocation<'a> {
96    policy_id: &'a PolicyID,
97    source_info: Option<SourceInfo>,
98}
99
100impl<'a> SourceLocation<'a> {
101    fn new(policy_id: &'a PolicyID, source_info: Option<SourceInfo>) -> Self {
102        Self {
103            policy_id,
104            source_info,
105        }
106    }
107
108    /// Get the `PolicyId` for the policy at this source location.
109    pub fn policy_id(&self) -> &'a PolicyID {
110        self.policy_id
111    }
112
113    pub fn source_info(&self) -> &Option<SourceInfo> {
114        &self.source_info
115    }
116
117    pub fn into_source_info(self) -> Option<SourceInfo> {
118        self.source_info
119    }
120}
121
122/// Enumeration of the possible diagnostic error that could be found by the
123/// verification steps.
124#[derive(Debug, Error)]
125#[cfg_attr(test, derive(Eq, PartialEq))]
126#[non_exhaustive]
127pub enum ValidationErrorKind {
128    /// An entity type was seen in a policy but was not found in the schema.
129    #[error(
130        "Unrecognized entity type {}{}",
131        .0.actual_entity_type,
132        match &.0.suggested_entity_type {
133            Some(s) => format!(", did you mean {}?", s),
134            None => "".to_string()
135        }
136    )]
137    UnrecognizedEntityType(UnrecognizedEntityType),
138    /// An action id was seen in a policy but was not found in the schema.
139    #[error(
140        "Unrecognized action id {}{}",
141        .0.actual_action_id,
142        match &.0.suggested_action_id {
143            Some(s) => format!(", did you mean {}?", s),
144            None => "".to_string()
145        }
146    )]
147    UnrecognizedActionId(UnrecognizedActionId),
148    /// There is no action satisfying the action head constraint that can be
149    /// applied to a principal and resources that both satisfy their respective
150    /// head conditions.
151    #[error(
152        "Unable to find an applicable action given the policy head constraints{}{}",
153        if .0.would_in_fix_principal { ". Note: Try replacing `==` with `in` in the principal clause" } else { "" },
154        if .0.would_in_fix_resource { ". Note: Try replacing `==` with `in` in the resource clause" } else { "" }
155    )]
156    InvalidActionApplication(InvalidActionApplication),
157    /// A type error was found by the type checker.
158    #[error(transparent)]
159    TypeError(TypeErrorKind),
160    /// An unspecified entity was used in a policy. This should be impossible,
161    /// assuming that the policy was constructed using the parser.
162    #[error(
163        "Unspecified entity with eid {}. Unspecified entities cannot be used in policies",
164        .0.entity_id,
165    )]
166    UnspecifiedEntity(UnspecifiedEntity),
167}
168
169impl ValidationErrorKind {
170    pub(crate) fn unrecognized_entity_type(
171        actual_entity_type: String,
172        suggested_entity_type: Option<String>,
173    ) -> ValidationErrorKind {
174        Self::UnrecognizedEntityType(UnrecognizedEntityType {
175            actual_entity_type,
176            suggested_entity_type,
177        })
178    }
179
180    pub(crate) fn unrecognized_action_id(
181        actual_action_id: String,
182        suggested_action_id: Option<String>,
183    ) -> ValidationErrorKind {
184        Self::UnrecognizedActionId(UnrecognizedActionId {
185            actual_action_id,
186            suggested_action_id,
187        })
188    }
189
190    pub(crate) fn invalid_action_application(
191        would_in_fix_principal: bool,
192        would_in_fix_resource: bool,
193    ) -> ValidationErrorKind {
194        Self::InvalidActionApplication(InvalidActionApplication {
195            would_in_fix_principal,
196            would_in_fix_resource,
197        })
198    }
199
200    pub(crate) fn type_error(type_error: TypeErrorKind) -> ValidationErrorKind {
201        Self::TypeError(type_error)
202    }
203
204    pub(crate) fn unspecified_entity(entity_id: String) -> ValidationErrorKind {
205        Self::UnspecifiedEntity(UnspecifiedEntity { entity_id })
206    }
207}
208
209/// Structure containing details about an unrecognized entity type error.
210#[derive(Debug)]
211#[cfg_attr(test, derive(Eq, PartialEq))]
212pub struct UnrecognizedEntityType {
213    /// The entity type seen in the policy.
214    pub(crate) actual_entity_type: String,
215    /// An entity type from the schema that the user might reasonably have
216    /// intended to write.
217    pub(crate) suggested_entity_type: Option<String>,
218}
219
220/// Structure containing details about an unrecognized action id error.
221#[derive(Debug)]
222#[cfg_attr(test, derive(Eq, PartialEq))]
223pub struct UnrecognizedActionId {
224    /// Action Id seen in the policy.
225    pub(crate) actual_action_id: String,
226    /// An action id from the schema that the user might reasonably have
227    /// intended to write.
228    pub(crate) suggested_action_id: Option<String>,
229}
230
231/// Structure containing details about an invalid action application error.
232#[derive(Debug)]
233#[cfg_attr(test, derive(Eq, PartialEq))]
234pub struct InvalidActionApplication {
235    pub(crate) would_in_fix_principal: bool,
236    pub(crate) would_in_fix_resource: bool,
237}
238
239/// Structure containing details about an unspecified entity error.
240#[derive(Debug)]
241#[cfg_attr(test, derive(Eq, PartialEq))]
242pub struct UnspecifiedEntity {
243    /// EID of the unspecified entity.
244    pub(crate) entity_id: String,
245}