use std::str::FromStr;
use base64::prelude::*;
use serde_json::json;
use super::{LegacyAgamaPolicyStore, LegacyPolicyStore, ParsePolicySetMessage};
#[test]
fn test_policy_store_deserialization_success() {
let policy = r#"
permit (
principal is Jans::Workload,
action in [Jans::Action::"Update"],
resource is Jans::Issue
) when {
principal.org_id == resource.org_id
};
"#;
cedar_policy::Policy::from_str(policy).expect("invalid cedar policy");
let schema = include_str!("../cedar-schema.json");
cedar_policy::Schema::from_json_str(schema).expect("invalid cedar schema");
let policy_store_json = json!({
"cedar_version": "v4.0.0",
"name": "Jans",
"cedar_policies": {
"840da5d85403f35ea76519ed1a18a33989f855bf1cf8": {
"description": "simple policy example",
"creation_date": "2024-09-20T17:22:39.996050",
"policy_content": BASE64_STANDARD.encode(policy)
}
},
"cedar_schema": BASE64_STANDARD.encode(schema),
});
serde_json::from_str::<LegacyPolicyStore>(policy_store_json.to_string().as_str())
.expect("failed to deserialize LegacyPolicyStore from policy_store_json");
}
#[test]
fn test_base64_decoding_error_in_policy_store() {
let policy = r#"
permit (
principal is Jans::Workload,
action in [Jans::Action::"Update"],
resource is Jans::Issue
) when {
principal.org_id == resource.org_id
};
"#;
cedar_policy::Policy::from_str(policy).expect("invalid cedar policy");
let mut encoded_policy = BASE64_STANDARD.encode(policy);
encoded_policy.push('!');
let schema = include_str!("../cedar-schema.json");
cedar_policy::Schema::from_json_str(schema).expect("invalid cedar schema");
let policy_store_json = json!({
"cedar_version": "v4.0.0",
"name": "Jans",
"cedar_policies": {
"840da5d85403f35ea76519ed1a18a33989f855bf1cf8": {
"description": "simple policy example",
"creation_date": "2024-09-20T17:22:39.996050",
"policy_content": encoded_policy,
}
},
"cedar_schema": BASE64_STANDARD.encode(schema),
});
let policy_result =
serde_json::from_str::<LegacyPolicyStore>(policy_store_json.to_string().as_str());
let err =
policy_result.expect_err("Expected base64 decoding error for invalid base64 character");
assert!(
err.to_string()
.contains(&ParsePolicySetMessage::Base64.to_string()),
"Error message should indicate base64 decoding failure, got: {err}"
);
}
#[test]
fn test_policy_parsing_error_in_policy_store() {
let policy = r#"
permit (
principal is Jans::Workload,
action in [Jans::Action::"Update"],
resource is Jans::Issue
) when {
principal.org_id == resource.org_id
};
"#;
cedar_policy::Policy::from_str(policy).expect("invalid cedar policy");
let mut encoded_policy = BASE64_STANDARD.encode(policy);
let mut invalid_utf8_bytes = BASE64_STANDARD
.decode(&encoded_policy)
.expect("Failed to decode Base64");
invalid_utf8_bytes[10] = 0xFF;
encoded_policy = BASE64_STANDARD.encode(&invalid_utf8_bytes);
let schema = include_str!("../cedar-schema.json");
cedar_policy::Schema::from_json_str(schema).expect("invalid cedar schema");
let policy_store_json = json!({
"cedar_version": "v4.0.0",
"name": "Jans",
"cedar_policies": {
"840da5d85403f35ea76519ed1a18a33989f855bf1cf8": {
"description": "simple policy example",
"creation_date": "2024-09-20T17:22:39.996050",
"policy_content": encoded_policy,
}
},
"cedar_schema": BASE64_STANDARD.encode(schema),
});
let policy_result =
serde_json::from_str::<LegacyPolicyStore>(policy_store_json.to_string().as_str());
let err = policy_result.expect_err("Expected UTF-8 parsing error for invalid byte sequence");
assert!(
err.to_string()
.contains(&ParsePolicySetMessage::String.to_string()),
"Error message should indicate string parsing failure, got: {err}"
);
}
#[test]
fn test_broken_policy_parsing_error_in_policy_store() {
static POLICY_STORE_RAW_YAML: &str =
include_str!("../../../../../test_files/policy-store_policy_err_broken_policy.yaml");
let policy_result = serde_yaml_ng::from_str::<LegacyAgamaPolicyStore>(POLICY_STORE_RAW_YAML);
let err = policy_result.expect_err("Expected policy parsing error for broken policy syntax");
let err_msg = err.to_string();
assert!(
err_msg.contains(
"unable to decode policy with id: 840da5d85403f35ea76519ed1a18a33989f855bf1cf8"
),
"Error should identify the policy ID that failed to decode, got: {err_msg}"
);
assert!(
err_msg.contains(
"unable to decode policy_content from human readable format: this policy is missing the `resource` variable in the scope"
),
"Error should describe the syntax error, got: {err_msg}"
);
}
#[test]
fn test_invalid_policy_store_entry() {
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"policies": {}
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
result.expect("schema is now optional, should succeed without schema field");
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"schema": null,
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
let err = result.expect_err("Expected error for missing policies in policy store entry");
assert!(
err.to_string().contains(
"missing required field 'policies' or 'cedar_policies' in policy store entry"
),
"Error should mention missing policies field, got: {err}"
);
}
#[test]
fn test_invalid_schema_format() {
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"schema": "invalid_schema",
"policies": {}
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
let err = result.expect_err("Expected error for invalid schema format");
assert!(
err.to_string().contains("error parsing schema"),
"Error should mention schema parsing error, got: {err}"
);
}
#[test]
fn test_invalid_policies_format() {
let schema = base64::prelude::BASE64_STANDARD.encode("{}");
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"schema": schema,
"policies": {
"invalid_policy": {
"description": "test",
"policy_content": "invalid_content"
}
}
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
let err = result.expect_err("Expected error for invalid policy content");
assert!(
err.to_string().contains("unable to decode policy with id"),
"Error should mention unable to decode policy, got: {err}"
);
}
#[test]
fn test_legacy_policy_store_with_null_schema_succeeds() {
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"schema": null,
"policies": {}
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
let agama = result.expect("should deserialize with null schema");
let (id, legacy_store) = agama.policy_stores.iter().next().expect("has one store");
assert_eq!(id, "test", "store id should match");
assert!(
legacy_store.schema.is_none(),
"schema should be None when null in JSON"
);
let store: super::super::PolicyStore = legacy_store.clone().into();
assert!(
store.schema.is_none(),
"converted PolicyStore should have None schema"
);
}
#[test]
fn test_legacy_policy_store_missing_schema_field_succeeds() {
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"policies": {}
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
let agama = result.expect("should deserialize with missing schema field");
let (_, legacy_store) = agama.policy_stores.iter().next().expect("has one store");
assert!(
legacy_store.schema.is_none(),
"schema should be None when field is absent"
);
let store: super::super::PolicyStore = legacy_store.clone().into();
assert!(
store.schema.is_none(),
"converted PolicyStore should have None schema"
);
}
#[test]
fn test_legacy_policy_store_invalid_schema_format_with_non_null_still_errors() {
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"schema": "invalid_schema",
"policies": {}
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
let err = result.expect_err("should error on non-null invalid schema");
assert!(
err.to_string().contains("error parsing schema"),
"error should indicate schema parsing failure, got: {err}"
);
}
#[test]
fn test_invalid_trusted_issuers_format() {
let schema = base64::prelude::BASE64_STANDARD.encode("{}");
let json = json!({
"cedar_version": "v4.0.0",
"policy_stores": {
"test": {
"name": "test",
"schema": schema,
"policies": {},
"trusted_issuers": {
"invalid_issuer": {
"name": "test",
"description": "test",
"openid_configuration_endpoint": "invalid_url"
}
}
}
}
});
let result = serde_json::from_str::<LegacyAgamaPolicyStore>(&json.to_string());
let err = result.expect_err("Expected error for invalid openid_configuration_endpoint URL");
assert!(
err.to_string()
.contains("the `\"openid_configuration_endpoint\"` or `\"configuration_endpoint\"` is not a valid url"),
"Error should mention invalid URL, got: {err}"
);
}