From 1609d748cd0db68ca6ff9b230f9c7a1589f46fdf Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Wed, 18 Jun 2025 22:02:16 -0400 Subject: [PATCH 1/3] Wrote new tests for CedarJavaFFI internal methods Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 423 ++++++++++++++++++++++++++++++++++ 1 file changed, 423 insertions(+) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 4d607e99..236b0a54 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -715,6 +715,10 @@ mod jvm_based_tests { static JVM: LazyLock = LazyLock::new(|| create_jvm().unwrap()); mod policy_tests { + use std::result; + + use cedar_policy::Effect; + use super::*; #[track_caller] @@ -802,8 +806,202 @@ mod jvm_based_tests { "myAnnotatedValue", ); } + #[test] + fn test_parse_policy_internal_valid(){ + let mut env = JVM.attach_current_thread().unwrap(); + let input = r#"permit(principal,action,resource);"#; + let policy_jstr = env.new_string(input).unwrap(); + let result = parse_policy_internal(&mut env, policy_jstr); + assert!(result.is_ok(),"Expected parse_policy_internal succeeded"); + + let jvalue = result.unwrap(); + let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); + let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); + + assert!(parsed_string.contains("permit")); + assert!(parsed_string.contains("principal")) + } + + #[test] + fn parse_policy_internal_invalid() + { + let mut env = JVM.attach_current_thread().unwrap(); + let input_invalid = r#"permit(Principal Action Resource);"#; + let jstr = env.new_string(input_invalid).unwrap(); + let result = parse_policy_internal(&mut env, jstr); + if let Err(e) = &result { + println!("expected parsing failed") + } + assert!(result.is_err()); + } + + + #[test] + fn test_parse_policies_internal_invalid(){ + let mut env = JVM.attach_current_thread().unwrap(); + + let invalid_input= "not a valid input"; + let policy_jstr = env.new_string(invalid_input).unwrap(); + + let result = parse_policies_internal(&mut env, policy_jstr); + assert!(result.is_err(),"Expected to fail or invalid input"); + } + #[test] + fn parse_policy_template_valid_test() + { + let mut env = JVM.attach_current_thread().unwrap(); + let policy_template = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; + let jstr = env.new_string(policy_template).unwrap(); + let result =parse_policy_template_internal (&mut env, jstr); + assert!(result.is_ok()); + } + + #[test] + fn parse_policy_template_invalid_test(){ + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_input = r#"permit(Principa,Action,Resource );"#; + let jstr = env.new_string(invalid_input).unwrap(); + let result = parse_policy_template_internal(&mut env, jstr); + + if let Err(_e) = &result{ + println!("expected failure{}",_e); + } + + assert!(result.is_err()) + } + + #[test] + fn from_json_test_valid() { + let mut env = JVM.attach_current_thread().unwrap(); + + let policy_json = r#" + { + "effect": "permit", + "principal": { + "op": "==", + "entity": { "type": "User", "id": "12UA45" } + }, + "action": { + "op": "==", + "entity": { "type": "Action", "id": "view" } + }, + "resource": { + "op": "in", + "entity": { "type": "Folder", "id": "abc" } + }, + "conditions": [ + { + "kind": "when", + "body": { + "==": { + "left": { + ".": { + "left": { + "Var": "context" + }, + "attr": "tls_version" + } + }, + "right": { + "Value": "1.3" + } + } + } + } + ] + } + "#; + + let jstr = env.new_string(policy_json).unwrap(); + let result = from_json_internal(&mut env, jstr); + + assert!(result.is_ok(), "Parsing failed: {:?}", result); + + let jval = result.unwrap(); + let obj = jval.l().unwrap(); + let str_val: String = env.get_string(&obj.into()).unwrap().into(); + + println!("Returned policy string: '{}'", str_val); + + assert!( + str_val.to_lowercase().contains("permit"), + "Expected 'permit' in the policy string, got: '{}'", + str_val + ); +} + #[test] + fn from_json_invalid() + { + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_input = r#" + { + "Effect": "permit", + "Principal": { + "op": "==", + "Entity": { "type": "User", "id": "12UA45" } + }, + "Action": { + "op": "==", + "entity": { "type": "Action", "id": "view" } + }, + "Resource": { + "op": "in", + "entity": { "type": "Folder", "id": "abc" } + }, + "Conditions": [ + { + "kind": "when", + "body": { + "==": { + "left": { + ".": { + "left": { + "Var": "context" + }, + "attr": "tls_version" + } + }, + "right": { + "Value": "1.3" + } + } + } + } + ] + } + "#; + + let jstr = env.new_string(invalid_input).unwrap(); + let result = from_json_internal(&mut env, jstr); + assert!(result.is_err(),"Not valid jason "); } + + + #[test] + fn to_json_internal_test() + { + let mut env = JVM.attach_current_thread().unwrap(); + let input = r#"permit(principal, action, resource);"#; + let jstr = env.new_string(input).unwrap(); + let result = to_json_internal(&mut env, jstr); + + assert!(result.is_ok(),"Valid json"); + + } + + #[test] + fn to_json_internal_invalid() + { + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_input = r#"Permit(Principal, Resource, Action);"#; + let jstr = env.new_string(invalid_input).unwrap(); + + let result = from_json_internal(&mut env, jstr); + assert!(result.is_err(),"invalid input"); + } + + } mod map_tests { use super::*; @@ -820,6 +1018,7 @@ mod jvm_based_tests { "Object should be a HashMap instance." ); } + #[test] fn map_put_tests() { @@ -870,5 +1069,229 @@ mod jvm_based_tests { "Retrieved value should be equal to the inserted value." ) } + + + } + mod schema_test{ + use cedar_policy::Schema; + use super::*; + + #[test] + fn test_parse_json_str_valid() { + let json = r#" + { + "schema": { + "entityTypes": { + "User": { + "memberOfTypes": ["Group"] + }, + "Group": {}, + "File": {} + }, + "actions": { + "read": { + "appliesTo": { + "principalTypes": ["User"], + "resourceTypes": ["File"] + } + } + } + } + }"#; + + let result = Schema::from_json_str(json); + assert!(result.is_ok(), "Expected schema parsing to succeed"); } + + #[test] + fn test_parse_json_str_invalid() { + let invalid_json = "this is not valid JSON"; + let result = Schema::from_json_str(invalid_json); + assert!(result.is_err(), "Expected schema parsing to fail"); + } + #[test] + fn parse_json_schema_internal_valid_test() + { + let mut env = JVM.attach_current_thread().unwrap(); + let input = r#" { + "schema": { + "entityTypes": { + "User": { + "memberOfTypes": ["Group"] + }, + "Group": {}, + "File": {} + }, + "actions": { + "read": { + "appliesTo": { + "principalTypes": ["User"], + "resourceTypes": ["File"] + } + } + } + } + }"#; + let jstr = env.new_string(input).unwrap(); + let result = parse_json_schema_internal(&mut env, jstr); + assert!(result.is_ok(), "successfully parsed schema"); + + let output = result.unwrap(); + let jstring_obj = output.l().unwrap(); + let jstring : jni::objects::JString = JString:: from(jstring_obj); + let rust_output: String = env.get_string(&jstring).unwrap().into(); + assert_eq!(rust_output,"success"); + + } + + #[test] + fn parse_json_schema_internal_invalid_test() + { + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_input = r#" { + "Schema": { + "entityTypes": { + "User": { + "MemberOfTypes": ["Group"] + }, + "Group": {}, + "File": {} + }, + "Actions": { + "read": { + "AppliesTo": { + "principalTypes": ["User"], + "AesourceTypes": ["File"] + } + } + } + } + }"#; + + let jstr = env.new_string(invalid_input).unwrap(); + let result = parse_json_schema_internal(&mut env, jstr); + assert!(result.is_err(), " Expected parsing has failed"); + } + #[test] + fn test_parse_cedar_schema_internal_invalid(){ + let mut env = JVM.attach_current_thread().unwrap(); + + let invalid_input = "Not a valid input"; + let schema_jstr = env.new_string(invalid_input).unwrap(); + let result = parse_cedar_schema_internal(&mut env, schema_jstr); + assert!(result.is_err(), "Expected parse_cedar_schema_internal to fail"); + } + #[test] +fn test_parse_cedar_schema_internal_valid() { + let mut env = JVM.attach_current_thread().unwrap(); + + let input = r#" + entity User = { + name: String, + age?: Long, + }; + + entity Photo in Album; + entity Album; + + action view appliesTo { + principal : [User], + resource: [Album,Photo] + }; + "#; + + let schema_jstr = env.new_string(input).unwrap(); + let result = parse_cedar_schema_internal(&mut env, schema_jstr); + + assert!(result.is_ok(), "Expected parse_cedar_schema_internal to succeed"); + + let jvalue = result.unwrap(); + let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); + let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); + + println!("Parsed schema output: {}", parsed_string); + assert_eq!(parsed_string, "success"); } +#[test] + fn test_parse_policy_set_valid() { + let input = r#" + permit(principal, action , resource); + permit(principal,action,resource) when {principal has x && principal.x == 5}; + "#; + + let result = PolicySet::from_str(input); + assert!(result.is_ok(), "Expected parsing to succeed"); + + let policy_set = result.unwrap(); + + assert_eq!(policy_set.policies().count(), 2); + assert_eq!(policy_set.templates().count(), 0); + } + + #[test] + fn test_parse_policy_set_invalid() { + let input = r#" + permit(); + "#; + + let result = PolicySet::from_str(input); + assert!(result.is_err(), "Expected parsing to fail on invalid policy"); + } + + #[test] + fn test_parse_policy_set_with_template() { + let input = r#" + permit(principal == ?principal, action, resource == ?resource); + "#; + + let result = PolicySet::from_str(input); + assert!(result.is_ok(), "Expected template policy parsing to succeed"); + + let policy_set = result.unwrap(); + assert_eq!(policy_set.policies().count(), 0); + assert_eq!(policy_set.templates().count(), 1); + } + #[test] + fn template_effect_jni_internal_permit_test(){ + let mut env = JVM.attach_current_thread().unwrap(); + let template_policy = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; + + let jstr = env.new_string(template_policy).unwrap(); + let result = template_effect_jni_internal(&mut env, jstr); + assert!(result.is_ok()); + + let jvalue = result.unwrap(); + let jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); + let effect = String::from(env.get_string(&jstring).unwrap()); + assert_eq!(effect, "permit"); + } + + #[test] + fn template_effect_jni_internal_forbid_test() + { + let mut env = JVM.attach_current_thread().unwrap(); + let cedar_policy = r#"forbid(principal==?principal,action == Action::"readfile",resource==?resource );"#; + let jstr = env.new_string(cedar_policy).unwrap(); + + let result = template_effect_jni_internal(&mut env, jstr); + if let Err(_e) = &result{ + println!("failed to parse template"); + } + + assert!(result.is_ok()); + + let jvalue = result.unwrap(); + let jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); + let effect = String::from(env.get_string(&jstring).unwrap()); + + assert_eq!(effect,"forbid"); + + } +} + +} + + + + + \ No newline at end of file From e39597d579dbd156847046efebb9011428b1bf9f Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Thu, 19 Jun 2025 15:15:16 -0400 Subject: [PATCH 2/3] Fixed test formatting Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 358 ++++++++++++++++------------------ 1 file changed, 173 insertions(+), 185 deletions(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 236b0a54..3ee74556 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -807,24 +807,23 @@ mod jvm_based_tests { ); } #[test] - fn test_parse_policy_internal_valid(){ + fn test_parse_policy_internal_valid() { let mut env = JVM.attach_current_thread().unwrap(); let input = r#"permit(principal,action,resource);"#; - let policy_jstr = env.new_string(input).unwrap(); - let result = parse_policy_internal(&mut env, policy_jstr); - assert!(result.is_ok(),"Expected parse_policy_internal succeeded"); + let policy_jstr = env.new_string(input).unwrap(); + let result = parse_policy_internal(&mut env, policy_jstr); + assert!(result.is_ok(), "Expected parse_policy_internal succeeded"); - let jvalue = result.unwrap(); - let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); - let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); + let jvalue = result.unwrap(); + let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); + let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); - assert!(parsed_string.contains("permit")); - assert!(parsed_string.contains("principal")) + assert!(parsed_string.contains("permit")); + assert!(parsed_string.contains("principal")) } - + #[test] - fn parse_policy_internal_invalid() - { + fn parse_policy_internal_invalid() { let mut env = JVM.attach_current_thread().unwrap(); let input_invalid = r#"permit(Principal Action Resource);"#; let jstr = env.new_string(input_invalid).unwrap(); @@ -835,46 +834,44 @@ mod jvm_based_tests { assert!(result.is_err()); } - #[test] - fn test_parse_policies_internal_invalid(){ + fn test_parse_policies_internal_invalid() { let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input= "not a valid input"; + let invalid_input = "not a valid input"; let policy_jstr = env.new_string(invalid_input).unwrap(); let result = parse_policies_internal(&mut env, policy_jstr); - assert!(result.is_err(),"Expected to fail or invalid input"); + assert!(result.is_err(), "Expected to fail or invalid input"); } - #[test] - fn parse_policy_template_valid_test() - { - let mut env = JVM.attach_current_thread().unwrap(); - let policy_template = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; - let jstr = env.new_string(policy_template).unwrap(); - let result =parse_policy_template_internal (&mut env, jstr); + #[test] + fn parse_policy_template_valid_test() { + let mut env = JVM.attach_current_thread().unwrap(); + let policy_template = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; + let jstr = env.new_string(policy_template).unwrap(); + let result = parse_policy_template_internal(&mut env, jstr); assert!(result.is_ok()); - } + } - #[test] - fn parse_policy_template_invalid_test(){ + #[test] + fn parse_policy_template_invalid_test() { let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#"permit(Principa,Action,Resource );"#; + let invalid_input = r#"permit(Principa,Action,Resource );"#; let jstr = env.new_string(invalid_input).unwrap(); let result = parse_policy_template_internal(&mut env, jstr); - if let Err(_e) = &result{ - println!("expected failure{}",_e); + if let Err(_e) = &result { + println!("expected failure{}", _e); } assert!(result.is_err()) - } + } - #[test] + #[test] fn from_json_test_valid() { - let mut env = JVM.attach_current_thread().unwrap(); + let mut env = JVM.attach_current_thread().unwrap(); - let policy_json = r#" + let policy_json = r#" { "effect": "permit", "principal": { @@ -912,28 +909,27 @@ mod jvm_based_tests { } "#; - let jstr = env.new_string(policy_json).unwrap(); - let result = from_json_internal(&mut env, jstr); + let jstr = env.new_string(policy_json).unwrap(); + let result = from_json_internal(&mut env, jstr); - assert!(result.is_ok(), "Parsing failed: {:?}", result); + assert!(result.is_ok(), "Parsing failed: {:?}", result); - let jval = result.unwrap(); - let obj = jval.l().unwrap(); - let str_val: String = env.get_string(&obj.into()).unwrap().into(); + let jval = result.unwrap(); + let obj = jval.l().unwrap(); + let str_val: String = env.get_string(&obj.into()).unwrap().into(); - println!("Returned policy string: '{}'", str_val); + println!("Returned policy string: '{}'", str_val); - assert!( - str_val.to_lowercase().contains("permit"), - "Expected 'permit' in the policy string, got: '{}'", - str_val - ); -} - #[test] - fn from_json_invalid() - { - let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#" + assert!( + str_val.to_lowercase().contains("permit"), + "Expected 'permit' in the policy string, got: '{}'", + str_val + ); + } + #[test] + fn from_json_invalid() { + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_input = r#" { "Effect": "permit", "Principal": { @@ -971,36 +967,30 @@ mod jvm_based_tests { } "#; - let jstr = env.new_string(invalid_input).unwrap(); - let result = from_json_internal(&mut env, jstr); - assert!(result.is_err(),"Not valid jason "); - } - - - - #[test] - fn to_json_internal_test() - { - let mut env = JVM.attach_current_thread().unwrap(); - let input = r#"permit(principal, action, resource);"#; - let jstr = env.new_string(input).unwrap(); - let result = to_json_internal(&mut env, jstr); + let jstr = env.new_string(invalid_input).unwrap(); + let result = from_json_internal(&mut env, jstr); + assert!(result.is_err(), "Not valid jason "); + } - assert!(result.is_ok(),"Valid json"); + #[test] + fn to_json_internal_test() { + let mut env = JVM.attach_current_thread().unwrap(); + let input = r#"permit(principal, action, resource);"#; + let jstr = env.new_string(input).unwrap(); + let result = to_json_internal(&mut env, jstr); - } + assert!(result.is_ok(), "Valid json"); + } - #[test] - fn to_json_internal_invalid() - { - let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#"Permit(Principal, Resource, Action);"#; - let jstr = env.new_string(invalid_input).unwrap(); + #[test] + fn to_json_internal_invalid() { + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_input = r#"Permit(Principal, Resource, Action);"#; + let jstr = env.new_string(invalid_input).unwrap(); - let result = from_json_internal(&mut env, jstr); - assert!(result.is_err(),"invalid input"); - } - + let result = from_json_internal(&mut env, jstr); + assert!(result.is_err(), "invalid input"); + } } mod map_tests { use super::*; @@ -1018,7 +1008,6 @@ mod jvm_based_tests { "Object should be a HashMap instance." ); } - #[test] fn map_put_tests() { @@ -1069,16 +1058,14 @@ mod jvm_based_tests { "Retrieved value should be equal to the inserted value." ) } - - } - mod schema_test{ - use cedar_policy::Schema; + mod schema_test { use super::*; + use cedar_policy::Schema; #[test] - fn test_parse_json_str_valid() { - let json = r#" + fn test_parse_json_str_valid() { + let json = r#" { "schema": { "entityTypes": { @@ -1099,21 +1086,20 @@ mod jvm_based_tests { } }"#; - let result = Schema::from_json_str(json); - assert!(result.is_ok(), "Expected schema parsing to succeed"); - } + let result = Schema::from_json_str(json); + assert!(result.is_ok(), "Expected schema parsing to succeed"); + } - #[test] - fn test_parse_json_str_invalid() { - let invalid_json = "this is not valid JSON"; - let result = Schema::from_json_str(invalid_json); - assert!(result.is_err(), "Expected schema parsing to fail"); - } - #[test] - fn parse_json_schema_internal_valid_test() - { - let mut env = JVM.attach_current_thread().unwrap(); - let input = r#" { + #[test] + fn test_parse_json_str_invalid() { + let invalid_json = "this is not valid JSON"; + let result = Schema::from_json_str(invalid_json); + assert!(result.is_err(), "Expected schema parsing to fail"); + } + #[test] + fn parse_json_schema_internal_valid_test() { + let mut env = JVM.attach_current_thread().unwrap(); + let input = r#" { "schema": { "entityTypes": { "User": { @@ -1132,23 +1118,21 @@ mod jvm_based_tests { } } }"#; - let jstr = env.new_string(input).unwrap(); - let result = parse_json_schema_internal(&mut env, jstr); - assert!(result.is_ok(), "successfully parsed schema"); - - let output = result.unwrap(); - let jstring_obj = output.l().unwrap(); - let jstring : jni::objects::JString = JString:: from(jstring_obj); - let rust_output: String = env.get_string(&jstring).unwrap().into(); - assert_eq!(rust_output,"success"); - - } - - #[test] - fn parse_json_schema_internal_invalid_test() - { - let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#" { + let jstr = env.new_string(input).unwrap(); + let result = parse_json_schema_internal(&mut env, jstr); + assert!(result.is_ok(), "successfully parsed schema"); + + let output = result.unwrap(); + let jstring_obj = output.l().unwrap(); + let jstring: jni::objects::JString = JString::from(jstring_obj); + let rust_output: String = env.get_string(&jstring).unwrap().into(); + assert_eq!(rust_output, "success"); + } + + #[test] + fn parse_json_schema_internal_invalid_test() { + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_input = r#" { "Schema": { "entityTypes": { "User": { @@ -1168,24 +1152,27 @@ mod jvm_based_tests { } }"#; - let jstr = env.new_string(invalid_input).unwrap(); - let result = parse_json_schema_internal(&mut env, jstr); - assert!(result.is_err(), " Expected parsing has failed"); - } - #[test] - fn test_parse_cedar_schema_internal_invalid(){ + let jstr = env.new_string(invalid_input).unwrap(); + let result = parse_json_schema_internal(&mut env, jstr); + assert!(result.is_err(), " Expected parsing has failed"); + } + #[test] + fn test_parse_cedar_schema_internal_invalid() { let mut env = JVM.attach_current_thread().unwrap(); let invalid_input = "Not a valid input"; let schema_jstr = env.new_string(invalid_input).unwrap(); let result = parse_cedar_schema_internal(&mut env, schema_jstr); - assert!(result.is_err(), "Expected parse_cedar_schema_internal to fail"); + assert!( + result.is_err(), + "Expected parse_cedar_schema_internal to fail" + ); } #[test] -fn test_parse_cedar_schema_internal_valid() { - let mut env = JVM.attach_current_thread().unwrap(); + fn test_parse_cedar_schema_internal_valid() { + let mut env = JVM.attach_current_thread().unwrap(); - let input = r#" + let input = r#" entity User = { name: String, age?: Long, @@ -1200,98 +1187,99 @@ fn test_parse_cedar_schema_internal_valid() { }; "#; - let schema_jstr = env.new_string(input).unwrap(); - let result = parse_cedar_schema_internal(&mut env, schema_jstr); + let schema_jstr = env.new_string(input).unwrap(); + let result = parse_cedar_schema_internal(&mut env, schema_jstr); - assert!(result.is_ok(), "Expected parse_cedar_schema_internal to succeed"); + assert!( + result.is_ok(), + "Expected parse_cedar_schema_internal to succeed" + ); - let jvalue = result.unwrap(); - let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); - let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); + let jvalue = result.unwrap(); + let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); + let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); - println!("Parsed schema output: {}", parsed_string); - assert_eq!(parsed_string, "success"); -} -#[test] - fn test_parse_policy_set_valid() { - let input = r#" + println!("Parsed schema output: {}", parsed_string); + assert_eq!(parsed_string, "success"); + } + #[test] + fn test_parse_policy_set_valid() { + let input = r#" permit(principal, action , resource); permit(principal,action,resource) when {principal has x && principal.x == 5}; "#; - let result = PolicySet::from_str(input); - assert!(result.is_ok(), "Expected parsing to succeed"); + let result = PolicySet::from_str(input); + assert!(result.is_ok(), "Expected parsing to succeed"); - let policy_set = result.unwrap(); + let policy_set = result.unwrap(); - assert_eq!(policy_set.policies().count(), 2); - assert_eq!(policy_set.templates().count(), 0); - } + assert_eq!(policy_set.policies().count(), 2); + assert_eq!(policy_set.templates().count(), 0); + } - #[test] - fn test_parse_policy_set_invalid() { - let input = r#" + #[test] + fn test_parse_policy_set_invalid() { + let input = r#" permit(); "#; - let result = PolicySet::from_str(input); - assert!(result.is_err(), "Expected parsing to fail on invalid policy"); - } + let result = PolicySet::from_str(input); + assert!( + result.is_err(), + "Expected parsing to fail on invalid policy" + ); + } - #[test] - fn test_parse_policy_set_with_template() { - let input = r#" + #[test] + fn test_parse_policy_set_with_template() { + let input = r#" permit(principal == ?principal, action, resource == ?resource); "#; - let result = PolicySet::from_str(input); - assert!(result.is_ok(), "Expected template policy parsing to succeed"); + let result = PolicySet::from_str(input); + assert!( + result.is_ok(), + "Expected template policy parsing to succeed" + ); - let policy_set = result.unwrap(); - assert_eq!(policy_set.policies().count(), 0); - assert_eq!(policy_set.templates().count(), 1); - } - #[test] - fn template_effect_jni_internal_permit_test(){ + let policy_set = result.unwrap(); + assert_eq!(policy_set.policies().count(), 0); + assert_eq!(policy_set.templates().count(), 1); + } + #[test] + fn template_effect_jni_internal_permit_test() { let mut env = JVM.attach_current_thread().unwrap(); - let template_policy = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; - - let jstr = env.new_string(template_policy).unwrap(); - let result = template_effect_jni_internal(&mut env, jstr); - assert!(result.is_ok()); - - let jvalue = result.unwrap(); - let jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); - let effect = String::from(env.get_string(&jstring).unwrap()); - assert_eq!(effect, "permit"); + let template_policy = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; + + let jstr = env.new_string(template_policy).unwrap(); + let result = template_effect_jni_internal(&mut env, jstr); + assert!(result.is_ok()); + + let jvalue = result.unwrap(); + let jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); + let effect = String::from(env.get_string(&jstring).unwrap()); + assert_eq!(effect, "permit"); } - + #[test] - fn template_effect_jni_internal_forbid_test() - { + fn template_effect_jni_internal_forbid_test() { let mut env = JVM.attach_current_thread().unwrap(); let cedar_policy = r#"forbid(principal==?principal,action == Action::"readfile",resource==?resource );"#; let jstr = env.new_string(cedar_policy).unwrap(); - + let result = template_effect_jni_internal(&mut env, jstr); - if let Err(_e) = &result{ + if let Err(_e) = &result { println!("failed to parse template"); } assert!(result.is_ok()); - + let jvalue = result.unwrap(); let jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); let effect = String::from(env.get_string(&jstring).unwrap()); - assert_eq!(effect,"forbid"); - + assert_eq!(effect, "forbid"); } + } } - -} - - - - - \ No newline at end of file From 5584a6dffe4d46fbf3bca130ee9a0b94a773c93c Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Wed, 9 Jul 2025 21:49:11 -0400 Subject: [PATCH 3/3] Refactors and corresponding tests Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 1020 +++++++++++++++++++-------------- 1 file changed, 595 insertions(+), 425 deletions(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 3ee74556..9b42a5ff 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -14,33 +14,36 @@ * limitations under the License. */ -use cedar_policy::entities_errors::EntitiesError; +/// +/// +/// Refactors and corresponding tests +/// +/// +use crate::objects::JFormatterConfig; +use crate::{ + answer::Answer, + jmap::Map, + jset::Set, + objects::{JEntityId, JEntityTypeName, JEntityUID, JPolicy, Object}, + utils::raise_npe, +}; #[cfg(feature = "partial-eval")] use cedar_policy::ffi::is_authorized_partial_json_str; +use cedar_policy::{entities_errors::EntitiesError, EntityTypeName}; use cedar_policy::{ ffi::{is_authorized_json_str, validate_json_str}, Entities, EntityUid, Policy, PolicySet, Schema, Template, }; use cedar_policy_formatter::{policies_str_to_pretty, Config}; use jni::{ - objects::{JClass, JObject, JString, JValueGen, JValueOwned}, + objects::{JClass, JObject, JString, JValue, JValueGen, JValueOwned}, sys::{jstring, jvalue}, JNIEnv, }; use jni_fn::jni_fn; use serde::{Deserialize, Serialize}; use serde_json::{from_str, Value}; -use std::{error::Error, str::FromStr, thread}; - -use crate::objects::JFormatterConfig; -use crate::{ - answer::Answer, - jmap::Map, - jset::Set, - objects::{JEntityId, JEntityTypeName, JEntityUID, JPolicy, Object}, - utils::raise_npe, -}; - +use std::{collections::HashMap, error::Error, str::FromStr, thread}; type Result = std::result::Result>; const V0_AUTH_OP: &str = "AuthorizationOperation"; @@ -64,6 +67,13 @@ fn build_err_obj(env: &JNIEnv<'_>, err: &str) -> jstring { fn call_cedar_in_thread(call_str: String, input_str: String) -> String { call_cedar(&call_str, &input_str) } +fn build_err_str(err: &str) -> String { + serde_json::to_string(&Answer::fail_bad_request(vec![format!( + "failed {} java string", + err + )])) + .expect("Could not serialize Answer::Failure") +} /// JNI entry point for authorization and validation requests #[jni_fn("com.cedarpolicy.BasicAuthorizationEngine")] @@ -274,6 +284,22 @@ pub fn parsePoliciesJni<'a>(mut env: JNIEnv<'a>, _: JClass, policies_jstr: JStri } } +pub fn parse_policy_set_to_text_map( + input: &str, +) -> Result<(Vec<(String, String)>, Vec<(String, String)>)> { + let policy_set = PolicySet::from_str(input)?; + let policies = policy_set + .policies() + .map(|p| (p.id().to_string(), p.to_string())) + .collect(); + + let templates = policy_set + .templates() + .map(|t| (t.id().to_string(), t.to_string())) + .collect(); + Ok((policies, templates)) +} + fn parse_policies_internal<'a>( env: &mut JNIEnv<'a>, policies_jstr: JString<'a>, @@ -281,60 +307,51 @@ fn parse_policies_internal<'a>( if policies_jstr.is_null() { raise_npe(env) } else { - // Parse the string into the Rust PolicySet let policies_jstring = env.get_string(&policies_jstr)?; let policies_string = String::from(policies_jstring); - let policy_set = PolicySet::from_str(&policies_string)?; - - // Enumerate over the parsed policies - let mut policies_java_hash_set = Set::new(env)?; - for policy in policy_set.policies() { - let policy_id = format!("{}", policy.id()); - let policy_text = format!("{}", policy); - let java_policy_object = JPolicy::new( - env, - &env.new_string(&policy_text)?, - &env.new_string(&policy_id)?, - )?; - let _ = policies_java_hash_set.add(env, java_policy_object); - } - let mut templates_java_hash_set = Set::new(env)?; - for template in policy_set.templates() { - let policy_id = format!("{}", template.id()); - let policy_text = format!("{}", template); - let java_policy_object = JPolicy::new( - env, - &env.new_string(&policy_text)?, - &env.new_string(&policy_id)?, - )?; - let _ = templates_java_hash_set.add(env, java_policy_object); - } + match parse_policy_set_to_text_map(&policies_string) { + Ok((policies, templates)) => { + let policies_set = build_jpolicy_set(env, policies)?; + let template_set = build_jpolicy_set(env, templates)?; - let java_policy_set = create_java_policy_set( - env, - policies_java_hash_set.as_ref(), - templates_java_hash_set.as_ref(), - ); + let policy_set_obj = + create_java_policy_set(env, policies_set.as_ref(), template_set.as_ref())?; + Ok(JValueGen::Object(policy_set_obj)) + } - Ok(JValueGen::Object(java_policy_set)) + Err(e) => Err(e), + } } } - +fn build_jpolicy_set<'a>( + env: &mut JNIEnv<'a>, + entries: Vec<(String, String)>, +) -> Result>> { + let mut set = Set::new(env)?; + for (id, text) in entries { + let j_policy_id = env.new_string(id)?; + let j_policy_text = env.new_string(text)?; + let policy_obj = JPolicy::new(env, &j_policy_text, &j_policy_id)?; + set.add(env, policy_obj)?; + } + Ok(set) +} fn create_java_policy_set<'a>( env: &mut JNIEnv<'a>, policies_java_hash_set: &JObject<'a>, templates_java_hash_set: &JObject<'a>, -) -> JObject<'a> { - env.new_object( +) -> Result> { + let policy_set_obj = env.new_object( "com/cedarpolicy/model/policy/PolicySet", "(Ljava/util/Set;Ljava/util/Set;)V", &[ JValueGen::Object(policies_java_hash_set), JValueGen::Object(templates_java_hash_set), ], - ) - .expect("Failed to create new PolicySet object") + )?; + + Ok(policy_set_obj) } #[jni_fn("com.cedarpolicy.model.policy.Policy")] @@ -549,18 +566,22 @@ pub fn getEntityIdentifierRepr<'a>(mut env: JNIEnv<'a>, _: JClass, obj: JObject< } } +fn cedar_escape_string(input: &str) -> String { + input.replace('\\', "\\\\").replace('"', "\\\"") +} +// added javlue gen fn get_entity_identifier_repr_internal<'a>( env: &mut JNIEnv<'a>, obj: JObject<'a>, ) -> Result> { if obj.is_null() { - raise_npe(env) - } else { - let eid = JEntityId::cast(env, obj)?; - let repr = eid.get_string_repr(); - let jstring = env.new_string(repr)?.into(); - Ok(JValueGen::Object(jstring)) + return raise_npe(env); } + let jstr = JString::from(obj); + let rust_str: String = env.get_string(&jstr)?.into(); + let cedar_style = format!("\"{}\"", cedar_escape_string(&rust_str)); + + Ok(JValueGen::Object(env.new_string(cedar_style)?.into())) } #[jni_fn("com.cedarpolicy.value.EntityTypeName")] @@ -571,17 +592,20 @@ pub fn parseEntityTypeName<'a>(mut env: JNIEnv<'a>, _: JClass, obj: JString<'a>) } } +fn parse_entity_type_name_str(input: &str) -> Result { + Ok(EntityTypeName::from_str(input)?) +} pub fn parse_entity_type_name_internal<'a>( env: &mut JNIEnv<'a>, obj: JString<'a>, ) -> Result>> { if obj.is_null() { - raise_npe(env) - } else { - let jstring = env.get_string(&obj)?; - let src = String::from(jstring); - JEntityTypeName::parse(env, &src).map(Into::into) + return raise_npe(env); } + let input_str: String = env.get_string(&obj)?.into(); + let entity_type = parse_entity_type_name_str(&input_str)?; + let j_entity_type_name = JEntityTypeName::try_from(env, &entity_type)?; + Ok(j_entity_type_name.into()) } #[jni_fn("com.cedarpolicy.value.EntityTypeName")] @@ -597,12 +621,11 @@ fn get_entity_type_name_repr_internal<'a>( obj: JObject<'a>, ) -> Result> { if obj.is_null() { - raise_npe(env) - } else { - let etype = JEntityTypeName::cast(env, obj)?; - let repr = etype.get_string_repr(); - Ok(env.new_string(repr)?.into()) + return raise_npe(env); } + let jstr = JString::from(obj); + let rust_str: String = env.get_string(&jstr)?.into(); + Ok(env.new_string(rust_str)?.into()) } #[jni_fn("com.cedarpolicy.value.EntityUID")] @@ -613,19 +636,41 @@ pub fn parseEntityUID<'a>(mut env: JNIEnv<'a>, _: JClass, obj: JString<'a>) -> j }; r } +pub fn entity_uid_str(euid_str: &str) -> Result> { + let cedar_euid = EntityUid::from_str(euid_str)?; + let mut result = HashMap::new(); + result.insert("id".to_string(), format!("{:?}", cedar_euid.id())); + result.insert("type".to_string(), cedar_euid.type_name().to_string()); + Ok(result) +} fn parse_entity_uid_internal<'a>( env: &mut JNIEnv<'a>, obj: JString<'a>, ) -> Result> { if obj.is_null() { - raise_npe(env) - } else { - let jstring = env.get_string(&obj)?; - let src = String::from(jstring); - let obj = JEntityUID::parse(env, &src)?; - Ok(obj.into()) + return raise_npe(env); + } + + let jstring = env.get_string(&obj)?; + let src = jstring.to_str()?; + let parsed_result_map = entity_uid_str(src)?; + let map_obj = env.new_object("java/util/HashMap", "()V", &[])?; + for (key, value) in parsed_result_map { + let j_key_string = env.new_string(key)?; + let j_value_string = env.new_string(value)?; + env.call_method( + &map_obj, + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + &[ + JValueGen::Object(&j_key_string), + JValueGen::Object(&j_value_string), + ], + )?; } + + Ok(JValueOwned::Object(map_obj)) } #[jni_fn("com.cedarpolicy.value.EntityUID")] @@ -644,18 +689,29 @@ pub fn getEUIDRepr<'a>( fn get_euid_repr_internal<'a>( env: &mut JNIEnv<'a>, - type_name: JObject<'a>, - id: JObject<'a>, + type_name_obj: JObject<'a>, + id_obj: JObject<'a>, ) -> Result> { - if type_name.is_null() || id.is_null() { - raise_npe(env) - } else { - let etype = JEntityTypeName::cast(env, type_name)?.get_rust_repr(); - let id = JEntityId::cast(env, id)?.get_rust_repr(); - let euid = EntityUid::from_type_name_and_id(etype, id); - let jstring = env.new_string(euid.to_string())?; - Ok(jstring.into()) + if type_name_obj.is_null() || id_obj.is_null() { + return raise_npe(env); } + + // Extract string from EntityTypeName object + let type_name_result = + env.call_method(&type_name_obj, "toString", "()Ljava/lang/String;", &[])?; + let type_jstr = JString::from(type_name_result.l()?); + let type_name: String = env.get_string(&type_jstr)?.into(); + + // Extract string from EntityIdentifier object + let id_result = env.call_method(&id_obj, "toString", "()Ljava/lang/String;", &[])?; + let id_jstr = JString::from(id_result.l()?); + let id: String = env.get_string(&id_jstr)?.into(); + + // Format into expected EntityUid string format + let euid_string = format!(r#"{type}::"{id}""#, type = type_name, id = id); + + // Return as Java JString + Ok(env.new_string(euid_string)?.into()) } #[jni_fn("com.cedarpolicy.formatter.PolicyFormatter")] @@ -683,6 +739,10 @@ pub fn policiesStrToPrettyWithConfig<'a>( } } +fn format_policies_str_with_config(input: &str, config: &Config) -> Result { + policies_str_to_pretty(input, config).map_err(|e| Box::::from(e.to_string())) +} + fn policies_str_to_pretty_internal<'a>( env: &mut JNIEnv<'a>, policies_jstr: JString<'a>, @@ -697,13 +757,10 @@ fn policies_str_to_pretty_internal<'a>( Config::default() }; let policies_str = String::from(env.get_string(&policies_jstr)?); - match policies_str_to_pretty(&policies_str, &config) { - Ok(formatted_policies) => Ok(env.new_string(formatted_policies)?.into()), - Err(e) => Err(e.into()), - } + let formatted_string = format_policies_str_with_config(&policies_str, &config)?; + Ok(env.new_string(formatted_string)?.into()) } } - #[cfg(test)] mod jvm_based_tests { use super::*; @@ -711,7 +768,6 @@ mod jvm_based_tests { use jni::JavaVM; use std::sync::LazyLock; - // Static JVM to be used by all the tests. LazyLock for thread-safe lazy initialization static JVM: LazyLock = LazyLock::new(|| create_jvm().unwrap()); mod policy_tests { @@ -736,6 +792,17 @@ mod jvm_based_tests { policy_effect_test_util(&mut env, "permit(principal,action,resource);", "permit"); policy_effect_test_util(&mut env, "forbid(principal,action,resource);", "forbid"); } + #[test] + fn policy_effect_jni_internal_null() { + let mut env = JVM.attach_current_thread().unwrap(); + let null_obj = JObject::null(); + let result = policy_effect_jni_internal(&mut env, null_obj.into()); + assert!(result.is_ok(), "Expected error on null input"); + assert!( + env.exception_check().unwrap(), + "Expected Java exception due to a null input" + ); + } #[track_caller] fn assert_id_annotation_eq( @@ -807,35 +874,30 @@ mod jvm_based_tests { ); } #[test] - fn test_parse_policy_internal_valid() { + fn get_template_annotations_internal_null() { let mut env = JVM.attach_current_thread().unwrap(); - let input = r#"permit(principal,action,resource);"#; - let policy_jstr = env.new_string(input).unwrap(); - let result = parse_policy_internal(&mut env, policy_jstr); - assert!(result.is_ok(), "Expected parse_policy_internal succeeded"); - - let jvalue = result.unwrap(); - let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); - let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); - - assert!(parsed_string.contains("permit")); - assert!(parsed_string.contains("principal")) + let null_obj = JObject::null(); + let result = get_template_annotations_internal(&mut env, null_obj.into()); + assert!(result.is_ok(), "Expected error on null input"); + assert!( + env.exception_check().unwrap(), + "Expected java exception due to a null input" + ); } #[test] - fn parse_policy_internal_invalid() { + fn get_policy_annotations_internal_null() { let mut env = JVM.attach_current_thread().unwrap(); - let input_invalid = r#"permit(Principal Action Resource);"#; - let jstr = env.new_string(input_invalid).unwrap(); - let result = parse_policy_internal(&mut env, jstr); - if let Err(e) = &result { - println!("expected parsing failed") - } - assert!(result.is_err()); + let null_obj = JObject::null(); + let result = get_policy_annotations_internal(&mut env, null_obj.into()); + assert!(result.is_ok(), "Expected error on null input"); + assert!( + env.exception_check().unwrap(), + "Expected java exception due to a null input" + ); } - #[test] - fn test_parse_policies_internal_invalid() { + fn parse_policies_internal_invalid() { let mut env = JVM.attach_current_thread().unwrap(); let invalid_input = "not a valid input"; @@ -845,151 +907,11 @@ mod jvm_based_tests { assert!(result.is_err(), "Expected to fail or invalid input"); } #[test] - fn parse_policy_template_valid_test() { + fn parse_policy_internal_null() { let mut env = JVM.attach_current_thread().unwrap(); - let policy_template = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; - let jstr = env.new_string(policy_template).unwrap(); - let result = parse_policy_template_internal(&mut env, jstr); - assert!(result.is_ok()); - } - - #[test] - fn parse_policy_template_invalid_test() { - let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#"permit(Principa,Action,Resource );"#; - let jstr = env.new_string(invalid_input).unwrap(); - let result = parse_policy_template_internal(&mut env, jstr); - - if let Err(_e) = &result { - println!("expected failure{}", _e); - } - - assert!(result.is_err()) - } - - #[test] - fn from_json_test_valid() { - let mut env = JVM.attach_current_thread().unwrap(); - - let policy_json = r#" - { - "effect": "permit", - "principal": { - "op": "==", - "entity": { "type": "User", "id": "12UA45" } - }, - "action": { - "op": "==", - "entity": { "type": "Action", "id": "view" } - }, - "resource": { - "op": "in", - "entity": { "type": "Folder", "id": "abc" } - }, - "conditions": [ - { - "kind": "when", - "body": { - "==": { - "left": { - ".": { - "left": { - "Var": "context" - }, - "attr": "tls_version" - } - }, - "right": { - "Value": "1.3" - } - } - } - } - ] - } - "#; - - let jstr = env.new_string(policy_json).unwrap(); - let result = from_json_internal(&mut env, jstr); - - assert!(result.is_ok(), "Parsing failed: {:?}", result); - - let jval = result.unwrap(); - let obj = jval.l().unwrap(); - let str_val: String = env.get_string(&obj.into()).unwrap().into(); - - println!("Returned policy string: '{}'", str_val); - - assert!( - str_val.to_lowercase().contains("permit"), - "Expected 'permit' in the policy string, got: '{}'", - str_val - ); - } - #[test] - fn from_json_invalid() { - let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#" - { - "Effect": "permit", - "Principal": { - "op": "==", - "Entity": { "type": "User", "id": "12UA45" } - }, - "Action": { - "op": "==", - "entity": { "type": "Action", "id": "view" } - }, - "Resource": { - "op": "in", - "entity": { "type": "Folder", "id": "abc" } - }, - "Conditions": [ - { - "kind": "when", - "body": { - "==": { - "left": { - ".": { - "left": { - "Var": "context" - }, - "attr": "tls_version" - } - }, - "right": { - "Value": "1.3" - } - } - } - } - ] - } - "#; - - let jstr = env.new_string(invalid_input).unwrap(); - let result = from_json_internal(&mut env, jstr); - assert!(result.is_err(), "Not valid jason "); - } - - #[test] - fn to_json_internal_test() { - let mut env = JVM.attach_current_thread().unwrap(); - let input = r#"permit(principal, action, resource);"#; - let jstr = env.new_string(input).unwrap(); - let result = to_json_internal(&mut env, jstr); - - assert!(result.is_ok(), "Valid json"); - } - - #[test] - fn to_json_internal_invalid() { - let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#"Permit(Principal, Resource, Action);"#; - let jstr = env.new_string(invalid_input).unwrap(); - - let result = from_json_internal(&mut env, jstr); - assert!(result.is_err(), "invalid input"); + let null_str = JString::from(JObject::null()); + let result = parse_policy_internal(&mut env, null_str); + assert!(result.is_ok(), "Expected error on null input"); } } mod map_tests { @@ -1060,226 +982,474 @@ mod jvm_based_tests { } } mod schema_test { + use std::result; + use super::*; - use cedar_policy::Schema; + use cedar_policy::{EntityId, Schema}; #[test] - fn test_parse_json_str_valid() { - let json = r#" - { - "schema": { - "entityTypes": { - "User": { - "memberOfTypes": ["Group"] - }, - "Group": {}, - "File": {} - }, - "actions": { - "read": { - "appliesTo": { - "principalTypes": ["User"], - "resourceTypes": ["File"] - } - } - } - } - }"#; + fn parse_policies_set_text_map_valid() { + let input_policies = r#"permit(principal, action , resource); + permit(principal,action,resource) when {principal has x && principal.x == 5};"#; - let result = Schema::from_json_str(json); - assert!(result.is_ok(), "Expected schema parsing to succeed"); - } + let result = parse_policy_set_to_text_map(input_policies); - #[test] - fn test_parse_json_str_invalid() { - let invalid_json = "this is not valid JSON"; - let result = Schema::from_json_str(invalid_json); - assert!(result.is_err(), "Expected schema parsing to fail"); - } - #[test] - fn parse_json_schema_internal_valid_test() { - let mut env = JVM.attach_current_thread().unwrap(); - let input = r#" { - "schema": { - "entityTypes": { - "User": { - "memberOfTypes": ["Group"] - }, - "Group": {}, - "File": {} - }, - "actions": { - "read": { - "appliesTo": { - "principalTypes": ["User"], - "resourceTypes": ["File"] - } - } - } - } - }"#; - let jstr = env.new_string(input).unwrap(); - let result = parse_json_schema_internal(&mut env, jstr); - assert!(result.is_ok(), "successfully parsed schema"); - - let output = result.unwrap(); - let jstring_obj = output.l().unwrap(); - let jstring: jni::objects::JString = JString::from(jstring_obj); - let rust_output: String = env.get_string(&jstring).unwrap().into(); - assert_eq!(rust_output, "success"); + assert!(result.is_ok()) } #[test] - fn parse_json_schema_internal_invalid_test() { - let mut env = JVM.attach_current_thread().unwrap(); - let invalid_input = r#" { - "Schema": { - "entityTypes": { - "User": { - "MemberOfTypes": ["Group"] - }, - "Group": {}, - "File": {} - }, - "Actions": { - "read": { - "AppliesTo": { - "principalTypes": ["User"], - "AesourceTypes": ["File"] - } - } - } - } - }"#; + fn parse_policies_set_map_invalid() { + let invalid_policies = r#"permit(principal?, action? , resource); + permit(principal,action,resource) if {principal has x && principal.x == 5};"#; - let jstr = env.new_string(invalid_input).unwrap(); - let result = parse_json_schema_internal(&mut env, jstr); - assert!(result.is_err(), " Expected parsing has failed"); + let result = parse_policy_set_to_text_map(invalid_policies); + assert!(result.is_err(), "Expected policy set to fail") } #[test] - fn test_parse_cedar_schema_internal_invalid() { - let mut env = JVM.attach_current_thread().unwrap(); + fn parse_policies_empty() { + let input = ""; + let result = parse_policy_set_to_text_map(input).unwrap(); - let invalid_input = "Not a valid input"; - let schema_jstr = env.new_string(invalid_input).unwrap(); - let result = parse_cedar_schema_internal(&mut env, schema_jstr); - assert!( - result.is_err(), - "Expected parse_cedar_schema_internal to fail" - ); + let (policies, templates) = result; + assert!(policies.is_empty()); + assert!(templates.is_empty()) } #[test] - fn test_parse_cedar_schema_internal_valid() { + fn parse_policies_internal_null() { let mut env = JVM.attach_current_thread().unwrap(); - + let null_str = JString::from(JObject::null()); + let result = parse_policies_internal(&mut env, null_str); + assert!(result.is_ok(), "Expected error on null input"); + } + #[test] + fn parse_policy_set_to_text_map_static_policies() { let input = r#" - entity User = { - name: String, - age?: Long, - }; - - entity Photo in Album; - entity Album; - - action view appliesTo { - principal : [User], - resource: [Album,Photo] - }; - "#; + permit(principal,action,resource); + forbid(principal == User::"bob",action,resource);"#; + let result = parse_policy_set_to_text_map(input).unwrap(); - let schema_jstr = env.new_string(input).unwrap(); - let result = parse_cedar_schema_internal(&mut env, schema_jstr); + let (policies, templates) = result; + assert_eq!(policies.len(), 2); - assert!( - result.is_ok(), - "Expected parse_cedar_schema_internal to succeed" - ); + let p1_found = policies + .iter() + .any(|(id, src)| src.contains("permit") && id.starts_with("policy")); + let p2_found = policies.iter().any(|(id, src)| { + src.contains("forbid") && src.contains("User::\"bob\"") && id.starts_with("policy") + }); - let jvalue = result.unwrap(); - let parsed_jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); - let parsed_string = String::from(env.get_string(&parsed_jstring).unwrap()); + assert!(p1_found); + assert!(p2_found); - println!("Parsed schema output: {}", parsed_string); - assert_eq!(parsed_string, "success"); + assert!(templates.is_empty()); } #[test] - fn test_parse_policy_set_valid() { + fn parse_policy_set_to_text_map_templates() { let input = r#" - permit(principal, action , resource); - permit(principal,action,resource) when {principal has x && principal.x == 5}; - "#; + permit(principal,action,resource); + "#; - let result = PolicySet::from_str(input); - assert!(result.is_ok(), "Expected parsing to succeed"); + let result = parse_policy_set_to_text_map(input).unwrap(); + let (policies, templates) = result; - let policy_set = result.unwrap(); + assert_eq!(policies.len(), 1, "Expected 1 static policy."); + assert!(templates.is_empty(), "Expected no templates."); - assert_eq!(policy_set.policies().count(), 2); - assert_eq!(policy_set.templates().count(), 0); + let p1_found = policies.iter().any(|(_id, src)| src.contains("permit")); + assert!(p1_found, "permit policy not found or content is incorrect."); } #[test] - fn test_parse_policy_set_invalid() { - let input = r#" - permit(); - "#; - - let result = PolicySet::from_str(input); + fn get_entity_identifier_repr_internal_null_input() { + let mut env = JVM.attach_current_thread().unwrap(); + let result = get_entity_identifier_repr_internal(&mut env, JObject::null()); + assert!(env.exception_check().unwrap()); assert!( - result.is_err(), - "Expected parsing to fail on invalid policy" + result.is_ok(), + "Expected get_entity_identifier_repr_internal to succeed" ); } - + //mock test #[test] - fn test_parse_policy_set_with_template() { - let input = r#" - permit(principal == ?principal, action, resource == ?resource); - "#; + fn get_entity_identifier_repr_internal_raw_string_mock_valid() { + let mut env = JVM.attach_current_thread().unwrap(); + let input = env.new_string("alice").unwrap(); - let result = PolicySet::from_str(input); - assert!( - result.is_ok(), - "Expected template policy parsing to succeed" - ); + let result_jval = + get_entity_identifier_repr_internal(&mut env, JObject::from(input)).unwrap(); + let output_jstring = JString::from(result_jval.l().unwrap()); + let output_str: String = env.get_string(&output_jstring).unwrap().into(); - let policy_set = result.unwrap(); - assert_eq!(policy_set.policies().count(), 0); - assert_eq!(policy_set.templates().count(), 1); + assert_eq!(output_str, r#""alice""#); } #[test] - fn template_effect_jni_internal_permit_test() { + fn get_entity_identifier_repr_internal_valid_entity_obj() { let mut env = JVM.attach_current_thread().unwrap(); - let template_policy = r#"permit(principal==?principal,action == Action::"readfile",resource==?resource );"#; + let input = env.new_string("alice").unwrap(); - let jstr = env.new_string(template_policy).unwrap(); - let result = template_effect_jni_internal(&mut env, jstr); - assert!(result.is_ok()); + let result_jval = + get_entity_identifier_repr_internal(&mut env, JObject::from(input)).unwrap(); + let output_jstring = JString::from(result_jval.l().unwrap()); + let output_str: String = env.get_string(&output_jstring).unwrap().into(); - let jvalue = result.unwrap(); - let jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); - let effect = String::from(env.get_string(&jstring).unwrap()); - assert_eq!(effect, "permit"); + assert_eq!(output_str, r#""alice""#); } #[test] - fn template_effect_jni_internal_forbid_test() { + fn get_entity_identifier_repr_internal_with_escape() { let mut env = JVM.attach_current_thread().unwrap(); - let cedar_policy = r#"forbid(principal==?principal,action == Action::"readfile",resource==?resource );"#; - let jstr = env.new_string(cedar_policy).unwrap(); + let input = env.new_string(r#"bob\smith"#).unwrap(); + + let result_jval = + get_entity_identifier_repr_internal(&mut env, JObject::from(input)).unwrap(); + let output_jstring = JString::from(result_jval.l().unwrap()); + let output_str: String = env.get_string(&output_jstring).unwrap().into(); + + assert_eq!(output_str, r#""bob\\smith""#); + } - let result = template_effect_jni_internal(&mut env, jstr); - if let Err(_e) = &result { - println!("failed to parse template"); + #[cfg(test)] + mod entity_tests { + use super::*; + use cedar_policy::EntityId; + use std::result; + + #[test] + fn parse_entity_type_name_internal_test() { + let input = "User"; + let result = parse_entity_type_name_str(input).unwrap(); + assert_eq!(result.basename(), "User"); + assert!(result.namespace_components().next().is_none()); + assert_eq!(result.to_string(), "User") + } + #[test] + fn parse_entity_type_name_internal_null_input() { + let mut env = JVM.attach_current_thread().unwrap(); + let null_input = JString::from(JObject::null()); + + let result = parse_entity_type_name_internal(&mut env, null_input); + + assert!(result.is_ok(), "Expected Ok result for null input"); + assert!( + env.exception_check().unwrap(), + "Expected Java exception due to null input" + ); + } + #[test] + fn parse_entity_type_name_internal_invalid() { + let mut env = JVM.attach_current_thread().unwrap(); + let input_jstr = env.new_string("Invalid::Type!").unwrap(); + + let result = parse_entity_type_name_internal(&mut env, input_jstr); + assert!( + result.is_err(), + "Expected error for invalid entity type name input" + ); } - assert!(result.is_ok()); + #[test] + fn parse_entity_type_name_valid_namespacestr() { + let input = "PhotoApp::UserGroup::Admin"; + let result = parse_entity_type_name_str(input).unwrap(); + assert_eq!(result.basename(), "Admin"); + let namespace: Vec<&str> = result.namespace_components().collect(); + assert_eq!(namespace, vec!["PhotoApp", "UserGroup"]); + assert_eq!(result.to_string(), "PhotoApp::UserGroup::Admin"); + } + #[test] + fn parse_entity_type_name_emptystr() { + let input = ""; + let result = parse_entity_type_name_str(input); + assert!( + result.is_err(), + "Expected parsing failed due to an empty string" + ); + } + #[test] + fn parse_entity_type_name_separators_str() { + let input = "::"; + let result = parse_entity_type_name_str(input); + assert!( + result.is_err(), + "Expected parsing failed for only separators" + ); + } + #[test] + fn simple_entity_type_valid() { + let input = "User"; + let result = parse_entity_type_name_str(input).unwrap(); + assert_eq!(result.basename().to_string(), "User"); + assert!(result.namespace().is_empty(), "Expected no namespace"); + } + #[test] + fn valid_policy_formats_successfully() { + let input = r#" + permit(principal, action, resource); + "#; + let config = Config::default(); - let jvalue = result.unwrap(); - let jstring = JString::cast(&mut env, jvalue.l().unwrap()).unwrap(); - let effect = String::from(env.get_string(&jstring).unwrap()); + let result = format_policies_str_with_config(input, &config); + assert!(result.is_ok(), "Expected formatting to succeed"); - assert_eq!(effect, "forbid"); + let output = result.unwrap(); + assert!( + output.contains("permit"), + "Expected output to include 'permit'" + ); + } + #[test] + fn invalid_policy_returns_error() { + let input = r#" + permit(principal,, action, resource); + "#; + let config = Config::default(); + + let result = format_policies_str_with_config(input, &config); + assert!( + result.is_err(), + "Expected formatting to fail due to syntax error" + ); + } + #[test] + fn format_policies_str_with_config_failures() { + let invalid_policies = [ + "permit(principal,, action, resource);", + "invalid syntax here", + "permit(principal action resource", + ]; + + let config = cedar_policy_formatter::Config::default(); + for invalid_input in invalid_policies { + let result = format_policies_str_with_config(invalid_input, &config); + assert!(result.is_err(), "Expected error for: {}", invalid_input); + } + } + #[test] + fn policies_str_to_pretty_null() { + let mut env = JVM.attach_current_thread().unwrap(); + let null_str = JString::from(JObject::null()); + let result = policies_str_to_pretty_internal(&mut env, null_str, None); + assert!(result.is_ok()); + } + + #[test] + fn policies_str_to_pretty_internal_valid_policy_string() { + let mut env = JVM.attach_current_thread().unwrap(); + + let input = r#"permit(principal, action, resource);"#; + let policies_jstr = env.new_string(input).unwrap(); + + let result = policies_str_to_pretty_internal(&mut env, policies_jstr, None); + assert!( + result.is_ok(), + "Expected valid policy string to format successfully, got: {:?}", + result + ); + + let formatted_jvalue = result.unwrap(); + let jstring_obj: JString = formatted_jvalue.l().unwrap().into(); + let formatted_str: String = env.get_string(&jstring_obj).unwrap().into(); + + assert!( + formatted_str.contains("permit"), + "Expected output to contain 'permit'." + ); + assert!( + formatted_str.contains("(") && formatted_str.contains(")"), + "Expected parentheses in formatted output." + ); + } + #[test] + fn parse_entity_uid_internal_str_failures() { + let invalid_cases = [ + "invalid::syntax", + "::empty", + "no_colon_separator", + "User::missing_quotes", + ]; + + for invalid_input in invalid_cases { + let result = entity_uid_str(invalid_input); + assert!(result.is_err(), "Expected error for: {}", invalid_input); + } + } + + #[test] + fn parse_entity_type_name_str_valid_simple() { + let input = "User"; + let result = parse_entity_type_name_str(input); + assert!(result.is_ok(), "Expected valid parse for 'User'."); + let parsed_etype = result.unwrap(); + assert_eq!(parsed_etype.basename(), "User"); + assert!(parsed_etype.namespace_components().next().is_none()); + } + #[test] + fn parse_entity_type_name_str_failures() { + let invalid_cases = ["", "::", "Invalid::Type!", "123InvalidStart"]; + + for invalid_input in invalid_cases { + let result = parse_entity_type_name_str(invalid_input); + assert!(result.is_err(), "Expected error for: {}", invalid_input); + } + } + + #[test] + fn build_java_policy_set_empty() { + let mut env = JVM.attach_current_thread().unwrap(); + let entries: Vec<(String, String)> = vec![]; + + let result = build_jpolicy_set(&mut env, entries); + assert!(result.is_ok()); + } + } + #[cfg(test)] + mod entity_type_tests { + use super::*; + use crate::{ + jlist::List, + objects::{JEntityId, JEntityTypeName}, + }; + + #[test] + fn get_euid_repr_internal_from_strings() { + let mut env = JVM.attach_current_thread().unwrap(); + + let type_str = env.new_string("User").unwrap(); + let id_str = env.new_string("alice").unwrap(); + + let result = get_euid_repr_internal(&mut env, type_str.into(), id_str.into()); + + assert!(result.is_ok(), "Expected success"); + + let jval = result.unwrap(); + let output_jstr = JString::from(jval.l().unwrap()); + let output: String = env.get_string(&output_jstr).unwrap().into(); + + assert_eq!(output, r#"User::"alice""#); + } + #[test] + fn get_euid_repr_internal_empty_strings() { + let mut env = JVM.attach_current_thread().unwrap(); + + let type_str = env.new_string("").unwrap(); + let id_str = env.new_string("").unwrap(); + + let result = get_euid_repr_internal(&mut env, type_str.into(), id_str.into()); + assert!(result.is_ok()); + + let jval = result.unwrap(); + let output_jstr = JString::from(jval.l().unwrap()); + let output: String = env.get_string(&output_jstr).unwrap().into(); + + assert_eq!(output, r#"::"""#); + } + #[test] + fn get_euid_repr_internal_null() { + let mut env = JVM.attach_current_thread().unwrap(); + + let null_type = JObject::null(); + let null_id = JObject::null(); + let result = get_euid_repr_internal(&mut env, null_type, null_id); + assert!( + result.is_ok(), + "Expected error when both arguments are null" + ); + } + #[test] + fn get_euid_repr_internal_special_chars() { + let mut env = JVM.attach_current_thread().unwrap(); + + let type_str = env.new_string("Some::Type").unwrap(); + let id_str = env.new_string("id-with-specials!@#$%^&*()").unwrap(); + + let result = get_euid_repr_internal(&mut env, type_str.into(), id_str.into()); + assert!(result.is_ok()); + + let jval = result.unwrap(); + let output_jstr = JString::from(jval.l().unwrap()); + let output: String = env.get_string(&output_jstr).unwrap().into(); + + assert_eq!(output, r#"Some::Type::"id-with-specials!@#$%^&*()""#); + } + + #[test] + fn get_entity_type_name_valid() { + let mut env = JVM.attach_current_thread().unwrap(); + let input = env.new_string("User").unwrap(); + + let result = get_entity_type_name_repr_internal(&mut env, input.into()); + + assert!(result.is_ok()); + + let output_jval = result.unwrap(); + let output_jstr = JString::from(output_jval.l().unwrap()); + let output: String = env.get_string(&output_jstr).unwrap().into(); + assert_eq!(output, "User"); + } + + #[test] + fn get_entity_type_name_repr_null() { + let mut env = JVM.attach_current_thread().unwrap(); + let null_obj = JObject::null(); + let result = get_entity_type_name_repr_internal(&mut env, null_obj); + assert!( + result.is_ok(), + "Expected get_entity_type_name to fail for null input" + ); + } + mod policy_set_tests { + use super::*; + use crate::interface::parse_policy_set_to_text_map; + + #[test] + fn parse_policy_set_to_text_map_empty() { + let input = ""; + let result = parse_policy_set_to_text_map(input); + assert!(result.is_ok(), "Failed to parse empty policy set"); + let (policies, templates) = result.unwrap(); + assert!(policies.is_empty()); + assert!(templates.is_empty()); + } + + #[test] + fn parse_policy_set_to_text_map_with_policy() { + let input = r#"permit(principal, action, resource);"#; + let result = parse_policy_set_to_text_map(input); + assert!(result.is_ok(), "Failed to parse policy set with one policy"); + let (policies, templates) = result.unwrap(); + assert_eq!(policies.len(), 1); + assert!(templates.is_empty()); + } + + #[test] + fn parse_policy_set_to_text_map_with_template() { + let input = r#"permit(principal == ?principal, action, resource);"#; + let result = parse_policy_set_to_text_map(input); + assert!(result.is_ok(), "Failed to parse policy set with template"); + let (policies, templates) = result.unwrap(); + assert_eq!(templates.len(), 1); + assert!(policies.is_empty()); + } + + #[test] + fn parse_policy_set_to_text_map_invalid() { + let input = "invalid policy syntax"; + let result = parse_policy_set_to_text_map(input); + assert!(result.is_err(), "Should fail on invalid policy syntax"); + } + + #[test] + fn create_java_policy_set_with_mock_sets() { + let mut env = JVM.attach_current_thread().unwrap(); + let policies_set = env.new_object("java/util/HashSet", "()V", &[]).unwrap(); + let templates_set = env.new_object("java/util/HashSet", "()V", &[]).unwrap(); + let result = create_java_policy_set(&mut env, &policies_set, &templates_set); + assert!( + result.is_err(), + "Expected error due to missing PolicySet class" + ); + } + } } } }