Skip to content

Commit

Permalink
Implement #745 for api::SchemaError (#876)
Browse files Browse the repository at this point in the history
Signed-off-by: Shaobo He <shaobohe@amazon.com>
  • Loading branch information
shaobo-he-aws authored May 28, 2024
1 parent 8995551 commit 11ae14a
Show file tree
Hide file tree
Showing 11 changed files with 413 additions and 473 deletions.
431 changes: 276 additions & 155 deletions cedar-policy-validator/src/err.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion cedar-policy-validator/src/human_schema/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,8 +1742,9 @@ mod common_type_references {
use cool_asserts::assert_matches;

use crate::{
schema_error::SchemaError,
types::{AttributeType, EntityRecordKind, Type},
SchemaError, SchemaFragment, ValidatorSchema,
SchemaFragment, ValidatorSchema,
};

#[test]
Expand Down
2 changes: 2 additions & 0 deletions cedar-policy-validator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ mod test {
};
use itertools::Itertools;

use crate::schema_error::Result;

#[test]
fn top_level_validate() -> Result<()> {
let mut set = PolicySet::new();
Expand Down
2 changes: 1 addition & 1 deletion cedar-policy-validator/src/rbac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ mod test {

use super::*;
use crate::{
err::*,
err::schema_error::*,
schema_file_format::{NamespaceDefinition, *},
validation_errors::{UnrecognizedEntityType, UnspecifiedEntity},
ValidationMode, ValidationWarning, Validator,
Expand Down
93 changes: 54 additions & 39 deletions cedar-policy-validator/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ use cedar_policy_core::{
};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use smol_str::ToSmolStr;

use super::NamespaceDefinition;
use crate::{
err::schema_error::*,
err::*,
human_schema::SchemaWarning,
types::{Attributes, EntityRecordKind, OpenTag, Type},
Expand Down Expand Up @@ -251,7 +253,7 @@ impl ValidatorSchema {
match type_defs.entry(name) {
Entry::Vacant(v) => v.insert(ty),
Entry::Occupied(o) => {
return Err(SchemaError::DuplicateCommonType(o.key().to_string()));
return Err(DuplicateCommonTypeError(o.key().clone()).into());
}
};
}
Expand All @@ -260,7 +262,7 @@ impl ValidatorSchema {
match entity_type_fragments.entry(name) {
Entry::Vacant(v) => v.insert(entity_type),
Entry::Occupied(o) => {
return Err(SchemaError::DuplicateEntityType(o.key().to_string()))
return Err(DuplicateEntityTypeError(o.key().clone()).into())
}
};
}
Expand All @@ -269,7 +271,7 @@ impl ValidatorSchema {
match action_fragments.entry(action_euid) {
Entry::Vacant(v) => v.insert(action),
Entry::Occupied(o) => {
return Err(SchemaError::DuplicateAction(o.key().to_string()))
return Err(DuplicateActionError(o.key().to_smolstr()).into())
}
};
}
Expand Down Expand Up @@ -306,9 +308,9 @@ impl ValidatorSchema {
let (attributes, open_attributes) = Self::record_attributes_or_none(
entity_type.attributes.resolve_type_defs(&type_defs)?,
)
.ok_or(SchemaError::ContextOrShapeNotRecord(
.ok_or(SchemaError::from(ContextOrShapeNotRecordError(
ContextOrShape::EntityTypeShape(name.clone()),
))?;
)))?;
Ok((
name.clone(),
ValidatorEntityType {
Expand Down Expand Up @@ -336,9 +338,9 @@ impl ValidatorSchema {
let descendants = action_children.remove(&name).unwrap_or_default();
let (context, open_context_attributes) =
Self::record_attributes_or_none(action.context.resolve_type_defs(&type_defs)?)
.ok_or(SchemaError::ContextOrShapeNotRecord(
.ok_or(SchemaError::from(ContextOrShapeNotRecordError(
ContextOrShape::ActionContext(name.clone()),
))?;
)))?;
Ok((
name.clone(),
ValidatorActionId {
Expand All @@ -358,7 +360,8 @@ impl ValidatorSchema {

// We constructed entity types and actions with child maps, but we need
// transitively closed descendants.
compute_tc(&mut entity_types, false)?;
compute_tc(&mut entity_types, false)
.map_err(|e| EntityTypeTransitiveClosureError::from(Box::new(e)))?;
// Pass `true` here so that we also check that the action hierarchy does
// not contain cycles.
compute_tc(&mut action_ids, true)?;
Expand Down Expand Up @@ -398,7 +401,6 @@ impl ValidatorSchema {
// any undeclared entity types which appeared in a `memberOf` list.
let mut undeclared_e = undeclared_parent_entities
.into_iter()
.map(|n| n.to_string())
.collect::<HashSet<_>>();
// Looking at entity types, we need to check entity references in
// attribute types. We already know that all elements of the
Expand All @@ -418,7 +420,7 @@ impl ValidatorSchema {
// Undeclared actions in a `memberOf` list.
let undeclared_a = undeclared_parent_actions
.into_iter()
.map(|n| n.to_string())
.map(|n| n.to_smolstr())
.collect::<HashSet<_>>();
// For actions, we check entity references in the context attribute
// types and `appliesTo` lists. See the `entity_types` loop for why the
Expand All @@ -430,7 +432,7 @@ impl ValidatorSchema {
match p_entity {
EntityType::Specified(p_entity) => {
if !entity_types.contains_key(&p_entity) {
undeclared_e.insert(p_entity.to_string());
undeclared_e.insert(p_entity.clone());
}
}
EntityType::Unspecified => (),
Expand All @@ -441,18 +443,18 @@ impl ValidatorSchema {
match r_entity {
EntityType::Specified(r_entity) => {
if !entity_types.contains_key(&r_entity) {
undeclared_e.insert(r_entity.to_string());
undeclared_e.insert(r_entity.clone());
}
}
EntityType::Unspecified => (),
}
}
}
if !undeclared_e.is_empty() {
return Err(SchemaError::UndeclaredEntityTypes(undeclared_e));
return Err(UndeclaredEntityTypesError(undeclared_e).into());
}
if !undeclared_a.is_empty() {
return Err(SchemaError::UndeclaredActions(undeclared_a));
return Err(UndeclaredActionsError(undeclared_a).into());
}

Ok(())
Expand All @@ -474,13 +476,13 @@ impl ValidatorSchema {
fn check_undeclared_in_type(
ty: &Type,
entity_types: &HashMap<Name, ValidatorEntityType>,
undeclared_types: &mut HashSet<String>,
undeclared_types: &mut HashSet<Name>,
) {
match ty {
Type::EntityOrRecord(EntityRecordKind::Entity(lub)) => {
for name in lub.iter() {
if !entity_types.contains_key(name) {
undeclared_types.insert(name.to_string());
undeclared_types.insert(name.clone());
}
}
}
Expand Down Expand Up @@ -793,9 +795,9 @@ impl<'a> CommonTypeResolver<'a> {
match ty {
SchemaType::TypeDef { type_name } => resolve_table
.get(&type_name)
.ok_or(SchemaError::UndeclaredCommonTypes(HashSet::from_iter(
std::iter::once(type_name.to_string()),
)))
.ok_or(SchemaError::UndeclaredCommonTypes(
UndeclaredCommonTypesError(HashSet::from_iter(std::iter::once(type_name))),
))
.cloned(),
SchemaType::Type(SchemaTypeVariant::Set { element }) => {
Ok(SchemaType::Type(SchemaTypeVariant::Set {
Expand Down Expand Up @@ -828,9 +830,9 @@ impl<'a> CommonTypeResolver<'a> {

// Resolve common type references
fn resolve(&self, extensions: Extensions) -> Result<HashMap<Name, Type>> {
let sorted_names = self
.topo_sort()
.map_err(SchemaError::CycleInCommonTypeReferences)?;
let sorted_names = self.topo_sort().map_err(|n| {
SchemaError::CycleInCommonTypeReferences(CycleInCommonTypeReferencesError(n))
})?;

let mut resolve_table = HashMap::new();
let mut tys = HashMap::new();
Expand Down Expand Up @@ -1016,7 +1018,7 @@ mod test {
let schema: Result<ValidatorSchema> = schema_file.try_into();
match schema {
Ok(_) => panic!("from_schema_file should have failed"),
Err(SchemaError::UndeclaredEntityTypes(v)) => {
Err(SchemaError::UndeclaredEntityTypes(UndeclaredEntityTypesError(v))) => {
assert_eq!(v.len(), 3)
}
_ => panic!("Unexpected error from from_schema_file"),
Expand All @@ -1039,8 +1041,8 @@ mod test {
let schema: Result<ValidatorSchema> = schema_file.try_into();
match schema {
Ok(_) => panic!("try_into should have failed"),
Err(SchemaError::UndeclaredEntityTypes(v)) => {
assert_eq!(v, HashSet::from(["Bar::Group".to_string()]))
Err(SchemaError::UndeclaredEntityTypes(UndeclaredEntityTypesError(v))) => {
assert_eq!(v, HashSet::from(["Bar::Group".parse().unwrap()]))
}
_ => panic!("Unexpected error from try_into"),
}
Expand All @@ -1064,10 +1066,10 @@ mod test {
let schema: Result<ValidatorSchema> = schema_file.try_into();
match schema {
Ok(_) => panic!("try_into should have failed"),
Err(SchemaError::UndeclaredEntityTypes(v)) => {
Err(SchemaError::UndeclaredEntityTypes(UndeclaredEntityTypesError(v))) => {
assert_eq!(
v,
HashSet::from(["Bar::Photo".to_string(), "Bar::User".to_string()])
HashSet::from(["Bar::Photo".parse().unwrap(), "Bar::User".parse().unwrap()])
)
}
_ => panic!("Unexpected error from try_into"),
Expand Down Expand Up @@ -1104,7 +1106,9 @@ mod test {
let schema: Result<ValidatorSchema> = schema_file.try_into();
match schema {
Ok(_) => panic!("from_schema_file should have failed"),
Err(SchemaError::UndeclaredActions(v)) => assert_eq!(v.len(), 1),
Err(SchemaError::UndeclaredActions(UndeclaredActionsError(v))) => {
assert_eq!(v.len(), 1)
}
_ => panic!("Unexpected error from from_schema_file"),
}
}
Expand All @@ -1126,7 +1130,7 @@ mod test {
let schema: Result<ValidatorSchema> = schema_file.try_into();
assert_matches!(
schema,
Err(SchemaError::CycleInActionHierarchy(euid)) => {
Err(SchemaError::CycleInActionHierarchy(CycleInActionHierarchyError(euid))) => {
assert_eq!(euid, r#"Action::"view_photo""#.parse().unwrap());
}
)
Expand Down Expand Up @@ -1266,8 +1270,8 @@ mod test {

let schema: Result<ValidatorSchema> = schema_json.try_into();
match schema {
Err(SchemaError::UndeclaredEntityTypes(tys)) => {
assert_eq!(tys, HashSet::from(["C::D::Foo".to_string()]))
Err(SchemaError::UndeclaredEntityTypes(UndeclaredEntityTypesError(v))) => {
assert_eq!(v, HashSet::from(["C::D::Foo".parse().unwrap()]))
}
_ => panic!("Schema construction should have failed due to undeclared entity type."),
}
Expand Down Expand Up @@ -1325,7 +1329,10 @@ mod test {
.expect("Expected valid schema");

let schema: Result<ValidatorSchema> = schema_json.try_into();
assert!(matches!(schema, Err(SchemaError::ActionEntityTypeDeclared)));
assert!(matches!(
schema,
Err(SchemaError::ActionEntityTypeDeclared(_))
));
}

#[test]
Expand Down Expand Up @@ -1372,7 +1379,9 @@ mod test {
Extensions::all_available(),
);
match schema {
Err(SchemaError::UnsupportedFeature(UnsupportedFeature::ActionAttributes(actions))) => {
Err(SchemaError::UnsupportedFeature(UnsupportedFeatureError(
UnsupportedFeature::ActionAttributes(actions),
))) => {
assert_eq!(
actions.into_iter().collect::<HashSet<_>>(),
HashSet::from([
Expand Down Expand Up @@ -1833,7 +1842,11 @@ mod test {
);

match schema {
Err(SchemaError::DuplicateCommonType(s)) if s.contains("A::MyLong") => (),
Err(SchemaError::DuplicateCommonType(DuplicateCommonTypeError(s)))
if s == "A::MyLong".parse().unwrap() =>
{
()
}
_ => panic!("should have errored because schema fragments have duplicate types"),
};
}
Expand Down Expand Up @@ -2189,8 +2202,8 @@ mod test {
}
);
let schema = ValidatorSchema::from_json_value(src, Extensions::all_available());
assert_matches!(schema, Err(SchemaError::UndeclaredCommonTypes(types)) =>
assert_eq!(types, HashSet::from(["Demo::id".to_string()])));
assert_matches!(schema, Err(SchemaError::UndeclaredCommonTypes(UndeclaredCommonTypesError(types))) =>
assert_eq!(types, HashSet::from(["Demo::id".parse().unwrap()])));
}

#[test]
Expand Down Expand Up @@ -2223,8 +2236,8 @@ mod test {
}
);
let schema = ValidatorSchema::from_json_value(src, Extensions::all_available());
assert_matches!(schema, Err(SchemaError::UndeclaredCommonTypes(types)) =>
assert_eq!(types, HashSet::from(["Demo::id".to_string()])));
assert_matches!(schema, Err(SchemaError::UndeclaredCommonTypes(UndeclaredCommonTypesError(types))) =>
assert_eq!(types, HashSet::from(["Demo::id".parse().unwrap()])));
}

#[test]
Expand Down Expand Up @@ -2326,7 +2339,9 @@ mod test_resolver {
use cool_asserts::assert_matches;

use super::CommonTypeResolver;
use crate::{types::Type, SchemaError, SchemaFragment, ValidatorSchemaFragment};
use crate::{
err::schema_error::SchemaError, types::Type, SchemaFragment, ValidatorSchemaFragment,
};

fn resolve(schema: SchemaFragment) -> Result<HashMap<Name, Type>, SchemaError> {
let schema: ValidatorSchemaFragment = schema.try_into().unwrap();
Expand Down
Loading

0 comments on commit 11ae14a

Please sign in to comment.