use cedar_policy_core::{ast::PolicyID, parser::SourceInfo};
use thiserror::Error;
use crate::TypeErrorKind;
#[derive(Debug)]
pub struct ValidationResult<'a> {
validation_errors: Vec<ValidationError<'a>>,
}
impl<'a> ValidationResult<'a> {
pub(crate) fn new(validation_errors: impl Iterator<Item = ValidationError<'a>>) -> Self {
Self {
validation_errors: validation_errors.collect::<Vec<_>>(),
}
}
pub fn validation_passed(&self) -> bool {
self.validation_errors.is_empty()
}
pub fn validation_errors(&self) -> impl Iterator<Item = &ValidationError> {
self.validation_errors.iter()
}
pub fn into_validation_errors(self) -> impl Iterator<Item = ValidationError<'a>> {
self.validation_errors.into_iter()
}
}
#[derive(Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct ValidationError<'a> {
location: SourceLocation<'a>,
error_kind: ValidationErrorKind,
}
impl<'a> ValidationError<'a> {
pub(crate) fn with_policy_id(
id: &'a PolicyID,
source_info: Option<SourceInfo>,
error_kind: ValidationErrorKind,
) -> Self {
Self {
error_kind,
location: SourceLocation::new(id, source_info),
}
}
pub fn into_location_and_error_kind(self) -> (SourceLocation<'a>, ValidationErrorKind) {
(self.location, self.error_kind)
}
pub fn error_kind(&self) -> &ValidationErrorKind {
&self.error_kind
}
pub fn location(&self) -> &SourceLocation {
&self.location
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct SourceLocation<'a> {
policy_id: &'a PolicyID,
source_info: Option<SourceInfo>,
}
impl<'a> SourceLocation<'a> {
fn new(policy_id: &'a PolicyID, source_info: Option<SourceInfo>) -> Self {
Self {
policy_id,
source_info,
}
}
pub fn policy_id(&self) -> &'a PolicyID {
self.policy_id
}
pub fn source_info(&self) -> &Option<SourceInfo> {
&self.source_info
}
pub fn into_source_info(self) -> Option<SourceInfo> {
self.source_info
}
}
#[derive(Debug, Error)]
#[cfg_attr(test, derive(Eq, PartialEq))]
#[non_exhaustive]
pub enum ValidationErrorKind {
#[error(
"unrecognized entity type `{}`{}",
.0.actual_entity_type,
match &.0.suggested_entity_type {
Some(s) => format!(", did you mean `{}`?", s),
None => "".to_string()
}
)]
UnrecognizedEntityType(UnrecognizedEntityType),
#[error(
"unrecognized action `{}`{}",
.0.actual_action_id,
match &.0.suggested_action_id {
Some(s) => format!(", did you mean `{}`?", s),
None => "".to_string()
}
)]
UnrecognizedActionId(UnrecognizedActionId),
#[error(
"unable to find an applicable action given the policy head constraints{}{}",
if .0.would_in_fix_principal { ". Note: Try replacing `==` with `in` in the principal clause" } else { "" },
if .0.would_in_fix_resource { ". Note: Try replacing `==` with `in` in the resource clause" } else { "" }
)]
InvalidActionApplication(InvalidActionApplication),
#[error(transparent)]
TypeError(TypeErrorKind),
#[error(
"unspecified entity with eid `{}`. Unspecified entities cannot be used in policies",
.0.entity_id,
)]
UnspecifiedEntity(UnspecifiedEntity),
}
impl ValidationErrorKind {
pub(crate) fn unrecognized_entity_type(
actual_entity_type: String,
suggested_entity_type: Option<String>,
) -> ValidationErrorKind {
Self::UnrecognizedEntityType(UnrecognizedEntityType {
actual_entity_type,
suggested_entity_type,
})
}
pub(crate) fn unrecognized_action_id(
actual_action_id: String,
suggested_action_id: Option<String>,
) -> ValidationErrorKind {
Self::UnrecognizedActionId(UnrecognizedActionId {
actual_action_id,
suggested_action_id,
})
}
pub(crate) fn invalid_action_application(
would_in_fix_principal: bool,
would_in_fix_resource: bool,
) -> ValidationErrorKind {
Self::InvalidActionApplication(InvalidActionApplication {
would_in_fix_principal,
would_in_fix_resource,
})
}
pub(crate) fn type_error(type_error: TypeErrorKind) -> ValidationErrorKind {
Self::TypeError(type_error)
}
pub(crate) fn unspecified_entity(entity_id: String) -> ValidationErrorKind {
Self::UnspecifiedEntity(UnspecifiedEntity { entity_id })
}
}
#[derive(Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct UnrecognizedEntityType {
pub(crate) actual_entity_type: String,
pub(crate) suggested_entity_type: Option<String>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct UnrecognizedActionId {
pub(crate) actual_action_id: String,
pub(crate) suggested_action_id: Option<String>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct InvalidActionApplication {
pub(crate) would_in_fix_principal: bool,
pub(crate) would_in_fix_resource: bool,
}
#[derive(Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct UnspecifiedEntity {
pub(crate) entity_id: String,
}