From aee2702bc2867870dbcf11de2e1d63d9e1b6fafc Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Thu, 17 Jul 2025 15:56:31 -0400 Subject: [PATCH 01/12] adds schema conversion method along with corresponding test cases rust/java Signed-off-by: Raheim Swaby --- .../com/cedarpolicy/model/schema/Schema.java | 48 +++- .../java/com/cedarpolicy/SchemaTests.java | 75 +++++- CedarJavaFFI/src/interface.rs | 229 +++++++++++++++++- 3 files changed, 340 insertions(+), 12 deletions(-) diff --git a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java index d11bb16e..5db68f49 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java +++ b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java @@ -16,13 +16,15 @@ package com.cedarpolicy.model.schema; +import java.util.Optional; + import com.cedarpolicy.loader.LibraryLoader; import com.cedarpolicy.model.exception.InternalException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Optional; - /** Represents a schema. */ public final class Schema { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @@ -122,16 +124,46 @@ public static Schema parse(JsonOrCedar type, String str) throws InternalExceptio } + public String toCedarFormat() throws InternalException { + if (type == JsonOrCedar.Cedar && schemaText.isPresent()) { + return schemaText.get(); + } else if (type == JsonOrCedar.Json && schemaJson.isPresent()) { + return jsonToCedarJni(schemaJson.get().toString()); + } else { + throw new InternalException("Schema content is missing"); + } + } + + /** + * Converts a Cedar format schema to JSON format + * + * @return JsonNode representing the schema in JSON format + * @throws InternalException If schema is not in Cedar format + * @throws JsonMappingException If JSON mapping fails + * @throws JsonProcessingException If JSON processing fails + * @throws NullPointerException If schema text is null + */ + public JsonNode toJsonFormat() + throws InternalException, JsonMappingException, JsonProcessingException, NullPointerException { + if (type != JsonOrCedar.Cedar || schemaText.isEmpty()) { + throw new InternalException("Schema is not in cedar format"); + } + return OBJECT_MAPPER.readTree(cedarToJsonJni(schemaText.get())); + + } + /** Specifies the schema format used. */ public enum JsonOrCedar { /** - * Cedar JSON schema format. See - * https://docs.cedarpolicy.com/schema/json-schema.html + * Cedar JSON schema format. See + * + * https://docs.cedarpolicy.com/schema/json-schema.html */ Json, /** - * Cedar schema format. See - * https://docs.cedarpolicy.com/schema/human-readable-schema.html + * Cedar schema format. See + * + * https://docs.cedarpolicy.com/schema/human-readable-schema.html */ Cedar } @@ -139,4 +171,8 @@ public enum JsonOrCedar { private static native String parseJsonSchemaJni(String schemaJson) throws InternalException, NullPointerException; private static native String parseCedarSchemaJni(String schemaText) throws InternalException, NullPointerException; + + private static native String jsonToCedarJni(String json) throws InternalException, NullPointerException; + + private static native String cedarToJsonJni(String cedar) throws InternalException, NullPointerException; } diff --git a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java index 5657c793..0435d1f7 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java @@ -16,13 +16,17 @@ package com.cedarpolicy; -import com.cedarpolicy.model.schema.Schema; -import com.cedarpolicy.model.schema.Schema.JsonOrCedar; - -import org.junit.jupiter.api.Test; - import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; + +import com.cedarpolicy.model.exception.InternalException; +import com.cedarpolicy.model.schema.Schema; +import com.cedarpolicy.model.schema.Schema.JsonOrCedar; +import com.fasterxml.jackson.databind.JsonNode; public class SchemaTests { @Test @@ -107,4 +111,65 @@ public void parseCedarSchema() { Schema.parse(JsonOrCedar.Cedar, "namspace Foo::Bar;"); }); } + + @Test + public void testToCedarFormat() throws InternalException { + String cedarSchema = "entity User;"; + Schema cedarSchemaObj = new Schema(cedarSchema); + String result = cedarSchemaObj.toCedarFormat(); + assertNotNull(result); + assertEquals(cedarSchema, result); + + String jsonSchema = """ + { + "": { + "entityTypes": { + "User": {} + }, + "actions": {} + } + } + """; + Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); + String convertedCedar = jsonSchemaObj.toCedarFormat(); + assertNotNull(convertedCedar); + assertTrue(convertedCedar.contains("entity User")); + + Schema invalidSchema = new Schema(JsonOrCedar.Cedar, java.util.Optional.empty(), java.util.Optional.empty()); + assertThrows(InternalException.class, () -> { + invalidSchema.toCedarFormat(); + }); + } + + @Test + public void testToJsonFormat() throws Exception { + String cedarSchema = "entity User;"; + Schema cedarSchemaObj = new Schema(cedarSchema); + JsonNode jsonResult = cedarSchemaObj.toJsonFormat(); + assertNotNull(jsonResult); + assertTrue(jsonResult.has("")); + assertTrue(jsonResult.get("").has("entityTypes")); + assertTrue(jsonResult.get("").get("entityTypes").has("User")); + + String jsonSchema = """ + { + "": { + "entityTypes": { + "User": {} + }, + "actions": {} + } + } + """; + Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); + assertThrows(InternalException.class, () -> { + jsonSchemaObj.toJsonFormat(); + }); + + Schema invalidSchema = new Schema(JsonOrCedar.Cedar, java.util.Optional.empty(), java.util.Optional.empty()); + assertThrows(InternalException.class, () -> { + invalidSchema.toJsonFormat(); + }); + } + } diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index d0534fa4..9b48e659 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -15,7 +15,10 @@ */ use cedar_policy::entities_errors::EntitiesError; #[cfg(feature = "partial-eval")] -use cedar_policy::ffi::is_authorized_partial_json_str; +use cedar_policy::ffi::{ + is_authorized_partial_json_str, schema_to_json, schema_to_text, Schema as FFISchema, + SchemaToJsonAnswer, SchemaToTextAnswer, +}; use cedar_policy::{ ffi::{is_authorized_json_str, validate_json_str}, Entities, EntityUid, Policy, PolicySet, Schema, Template, @@ -702,6 +705,72 @@ fn policies_str_to_pretty_internal<'a>( } } } +#[jni_fn("com.cedarpolicy.model.schema.Schema")] +pub fn jsonToCedarJni<'a>(mut env: JNIEnv<'a>, _: JClass, json_schema: JString<'a>) -> jvalue { + match get_cedar_schema_internal(&mut env, json_schema) { + Ok(val) => val.as_jni(), + Err(e) => jni_failed(&mut env, e.as_ref()), + } +} +pub fn get_cedar_schema_internal<'a>( + env: &mut JNIEnv<'a>, + schema_json_jstr: JString<'a>, +) -> Result> { + let rust_str = env.get_string(&schema_json_jstr)?; + let schema_str = rust_str.to_str()?; + + let schema: FFISchema = serde_json::from_str(schema_str)?; + let cedar_format = schema_to_text(schema); + + match cedar_format { + SchemaToTextAnswer::Success { text, warnings } => { + let jstr = env.new_string(&text)?; + Ok(JValueGen::Object(JObject::from(jstr)).into()) + } + SchemaToTextAnswer::Failure { errors } => { + let joined_errors = errors + .iter() + .map(|e| e.message.clone()) + .collect::>() + .join("; "); + Err(joined_errors.into()) + } + } +} + +#[jni_fn("com.cedarpolicy.model.schema.Schema")] +pub fn cedarToJsonJni<'a>(mut env: JNIEnv<'a>, _: JClass, cedar_schema: JString<'a>) -> jvalue { + match get_json_schema_internal(&mut env, cedar_schema) { + Ok(val) => val.as_jni(), + Err(e) => jni_failed(&mut env, e.as_ref()), + } +} + +pub fn get_json_schema_internal<'a>( + env: &mut JNIEnv<'a>, + cedar_schema_jstr: JString<'a>, +) -> Result> { + let schema_jstr = env.get_string(&cedar_schema_jstr)?; + let schema_str = schema_jstr.to_str()?; + let cedar_schema_str = FFISchema::Cedar(schema_str.into()); + let json_format = schema_to_json(cedar_schema_str); + + match json_format { + SchemaToJsonAnswer::Success { json, warnings: _ } => { + let json_pretty = serde_json::to_string_pretty(&json)?; + let jstr = env.new_string(&json_pretty)?; + Ok(JValueGen::Object(JObject::from(jstr)).into()) + } + SchemaToJsonAnswer::Failure { errors } => { + let joined_errors = errors + .iter() + .map(|e| e.message.clone()) + .collect::>() + .join("; "); + Err(joined_errors.into()) + } + } +} #[cfg(test)] pub(crate) mod jvm_based_tests { @@ -1351,4 +1420,162 @@ pub(crate) mod jvm_based_tests { ); } } + mod conversion_tests { + use super::*; + + #[test] + fn get_cedar_schema_internal_valid() { + let mut env = JVM.attach_current_thread().unwrap(); + let json_input = r#"{ + "schema": { + "entityTypes": { + "User": { + "memberOfTypes": ["Group"] + }, + "Group": {}, + "File": {} + }, + "actions": { + "read": { + "appliesTo": { + "principalTypes": ["User"], + "resourceTypes": ["File"] + } + } + } + } +}"#; + + let jstr = env.new_string(json_input).unwrap(); + let result = get_cedar_schema_internal(&mut env, jstr); + assert!(result.is_ok(), "Expected Cedar conversion to succeed"); + + let cedar_jval = result.unwrap(); + let cedar_jstr = JString::cast(&mut env, cedar_jval.l().unwrap()).unwrap(); + let cedar_str = String::from(env.get_string(&cedar_jstr).unwrap()); + + assert!( + cedar_str.contains("User"), + "Expected output to contain 'User'" + ); + assert!( + cedar_str.contains("Group"), + "Expected output to contain 'Group'" + ); + assert!( + cedar_str.contains("File"), + "Expected output to contain 'File'" + ); + assert!( + cedar_str.contains("read"), + "Expected output to contain 'read'" + ); + } + + #[test] + fn get_cedar_schema_internal_invalid() { + let mut env = JVM.attach_current_thread().unwrap(); + let json_input = r#" + + entity User = { + name: String, + age?: Long, + }; + entity Photo in Album; + entity Album; + action view appliesTo { + principal : [User], + resource: [Album,Photo] + }; + "#; + + let jstr = env.new_string(json_input).unwrap(); + let result = get_cedar_schema_internal(&mut env, jstr); + assert!( + result.is_err(), + "Expected get_cedar_schema_internal to fail {:?}", + result + ); + } + + #[test] + fn get_cedar_schema_internal_null() { + let mut env = JVM.attach_current_thread().unwrap(); + let null_str = JString::from(JObject::null()); + let result = get_cedar_schema_internal(&mut env, null_str); + assert!(result.is_err(), "Expected error on null input"); + } + + #[test] + fn get_json_schema_internal_valid() { + let mut env = JVM.attach_current_thread().unwrap(); + let cedar_input = r#" + entity User = { + name: String, + age?: Long, + }; + entity Photo in Album; + entity Album; + action view appliesTo { + principal : [User], + resource: [Album,Photo] + }; + "#; + + let jstr = env.new_string(cedar_input).unwrap(); + let result = get_json_schema_internal(&mut env, jstr); + assert!(result.is_ok(), "Expected JSON conversion to succeed"); + + let json_jval = result.unwrap(); + let json_jstr = JString::cast(&mut env, json_jval.l().unwrap()).unwrap(); + let json_str = String::from(env.get_string(&json_jstr).unwrap()); + + assert!( + json_str.contains("\"entityTypes\""), + "Expected output to contain 'entityTypes'" + ); + assert!( + json_str.contains("\"User\""), + "Expected output to contain 'User'" + ); + assert!( + json_str.contains("\"Album\""), + "Expected output to contain 'Album'" + ); + assert!( + json_str.contains("\"Photo\""), + "Expected output to contain 'Photo'" + ); + assert!( + json_str.contains("\"actions\""), + "Expected output to contain 'actions'" + ); + assert!( + json_str.contains("\"view\""), + "Expected output to contain 'view'" + ); + } + + #[test] + fn get_json_schema_internal_invalid_input() { + let mut env = JVM.attach_current_thread().unwrap(); + let invalid_cedar = "this is not cedar schema"; + let jstr = env.new_string(invalid_cedar).unwrap(); + + let result = get_json_schema_internal(&mut env, jstr); + assert!( + result.is_err(), + "Expected get_json_schema_internal to fail: {:?}", + result + ); + } + + #[test] + fn get_json_schema_internal_null() { + let mut env = JVM.attach_current_thread().unwrap(); + let null_str = JString::from(JObject::null()); + let result = get_json_schema_internal(&mut env, null_str); + assert!(result.is_err(), "Expected error on null input"); + } + } } From 666f07f5dd3e8de128d2cf83c8df88fb8407c446 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 00:09:42 -0400 Subject: [PATCH 02/12] adds schema conversion method along with corresponding test cases rust/java Signed-off-by: Raheim Swaby --- .../com/cedarpolicy/model/schema/Schema.java | 14 +- .../java/com/cedarpolicy/SchemaTests.java | 164 ++++++++++++------ CedarJavaFFI/src/interface.rs | 155 ++++++++++------- 3 files changed, 209 insertions(+), 124 deletions(-) diff --git a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java index 5db68f49..fa0efbb5 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java +++ b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java @@ -130,7 +130,7 @@ public String toCedarFormat() throws InternalException { } else if (type == JsonOrCedar.Json && schemaJson.isPresent()) { return jsonToCedarJni(schemaJson.get().toString()); } else { - throw new InternalException("Schema content is missing"); + throw new IllegalStateException("Schema content is missing"); } } @@ -145,13 +145,15 @@ public String toCedarFormat() throws InternalException { */ public JsonNode toJsonFormat() throws InternalException, JsonMappingException, JsonProcessingException, NullPointerException { - if (type != JsonOrCedar.Cedar || schemaText.isEmpty()) { - throw new InternalException("Schema is not in cedar format"); + if (type == JsonOrCedar.Json && schemaJson.isPresent()) { + return schemaJson.get(); + } else if (type == JsonOrCedar.Cedar && schemaText.isPresent()) { + return OBJECT_MAPPER.readTree(cedarToJsonJni(schemaText.get())); + } else { + throw new IllegalStateException("Schema content is missing"); } - return OBJECT_MAPPER.readTree(cedarToJsonJni(schemaText.get())); - } - + /** Specifies the schema format used. */ public enum JsonOrCedar { /** diff --git a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java index 0435d1f7..263c670e 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java @@ -16,17 +16,22 @@ package com.cedarpolicy; +import java.util.Optional; + import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import com.cedarpolicy.model.exception.InternalException; import com.cedarpolicy.model.schema.Schema; import com.cedarpolicy.model.schema.Schema.JsonOrCedar; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; public class SchemaTests { @Test @@ -112,64 +117,121 @@ public void parseCedarSchema() { }); } - @Test - public void testToCedarFormat() throws InternalException { - String cedarSchema = "entity User;"; - Schema cedarSchemaObj = new Schema(cedarSchema); - String result = cedarSchemaObj.toCedarFormat(); - assertNotNull(result); - assertEquals(cedarSchema, result); + @Nested + @DisplayName("toCedarFormat Tests") + class ToCedarFormatTests { - String jsonSchema = """ - { - "": { - "entityTypes": { - "User": {} - }, - "actions": {} + @Test + @DisplayName("Should return the same Cedar schema text") + void testFromCedar() throws InternalException { + String cedarSchema = "entity User;"; + Schema cedarSchemaObj = new Schema(cedarSchema); + String result = cedarSchemaObj.toCedarFormat(); + assertNotNull(result, "Result should not be null"); + assertEquals(cedarSchema, result, "Should return the original Cedar schema"); + } + + @Test + @DisplayName("Should convert JSON schema to Cedar format") + void testFromJson() throws InternalException { + String jsonSchema = """ + { + "": { + "entityTypes": { + "User": {} + }, + "actions": {} + } } - } - """; - Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); - String convertedCedar = jsonSchemaObj.toCedarFormat(); - assertNotNull(convertedCedar); - assertTrue(convertedCedar.contains("entity User")); + """; + Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); + String result = jsonSchemaObj.toCedarFormat(); + + assertNotNull(result, "Result should not be null"); + assertTrue(result.contains("entity User"), "Converted Cedar should contain the User entity"); + } - Schema invalidSchema = new Schema(JsonOrCedar.Cedar, java.util.Optional.empty(), java.util.Optional.empty()); - assertThrows(InternalException.class, () -> { - invalidSchema.toCedarFormat(); - }); + @Test + @DisplayName("Should throw IllegalStateException for empty schema") + void testEmptySchema() { + Schema emptySchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.empty()); + Exception exception = assertThrows(IllegalStateException.class, emptySchema::toCedarFormat); + assertEquals("Schema content is missing", exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception for malformed JSON schema") + void testMalformedSchema() { + // Missing closing brace in the JSON structure + String malformedJson = """ + { + "": { + "entityTypes": { + "User": {} + }, + "actions": { + } + """; + Schema malformedSchema = new Schema(JsonOrCedar.Json, Optional.of(malformedJson), Optional.empty()); + assertThrows(IllegalStateException.class, malformedSchema::toCedarFormat); + } } - @Test - public void testToJsonFormat() throws Exception { - String cedarSchema = "entity User;"; - Schema cedarSchemaObj = new Schema(cedarSchema); - JsonNode jsonResult = cedarSchemaObj.toJsonFormat(); - assertNotNull(jsonResult); - assertTrue(jsonResult.has("")); - assertTrue(jsonResult.get("").has("entityTypes")); - assertTrue(jsonResult.get("").get("entityTypes").has("User")); + @Nested + @DisplayName("toJsonFormat Tests") + class ToJsonFormatTests { + + @Test + @DisplayName("Should convert Cedar schema to JSON format") + void testFromCedar() throws Exception { + String cedarSchema = "entity User;"; + Schema cedarSchemaObj = new Schema(cedarSchema); + JsonNode result = cedarSchemaObj.toJsonFormat(); + + String expectedJson = "{\"\":{\"entityTypes\":{\"User\":{}},\"actions\":{}}}"; + JsonNode expectedNode = new ObjectMapper().readTree(expectedJson); + + assertNotNull(result, "Result should not be null"); + assertEquals(expectedNode, result, "JSON should match expected structure"); + } - String jsonSchema = """ - { - "": { - "entityTypes": { - "User": {} - }, - "actions": {} + @Test + @DisplayName("Should return the same JSON schema object") + void testFromJson() throws Exception { + String jsonSchema = """ + { + "": { + "entityTypes": { + "User": {} + }, + "actions": {} + } } - } - """; - Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); - assertThrows(InternalException.class, () -> { - jsonSchemaObj.toJsonFormat(); - }); + """; + Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); + JsonNode result = jsonSchemaObj.toJsonFormat(); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode expectedNode = mapper.readTree(jsonSchema); + + assertNotNull(result, "Result should not be null"); + assertEquals(expectedNode, result, "JSON should match the original schema"); + } - Schema invalidSchema = new Schema(JsonOrCedar.Cedar, java.util.Optional.empty(), java.util.Optional.empty()); - assertThrows(InternalException.class, () -> { - invalidSchema.toJsonFormat(); - }); + @Test + @DisplayName("Should throw IllegalStateException for empty schema") + void testEmptySchema() { + Schema emptySchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.empty()); + Exception exception = assertThrows(IllegalStateException.class, emptySchema::toJsonFormat); + assertEquals("Schema content is missing", exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception for malformed Cedar schema") + void testMalformedSchema() { + String malformedCedar = "entty User"; + Schema malformedSchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.of(malformedCedar)); + assertThrows(InternalException.class, malformedSchema::toJsonFormat); + } } - } diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 9b48e659..299dea44 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -1446,29 +1446,33 @@ pub(crate) mod jvm_based_tests { } }"#; + let expected_cedar = r#"namespace schema { + entity File; + + entity Group; + + entity User in [Group]; + + action "read" appliesTo { + principal: [User], + resource: [File], + context: {} + }; +}"#; + let jstr = env.new_string(json_input).unwrap(); let result = get_cedar_schema_internal(&mut env, jstr); assert!(result.is_ok(), "Expected Cedar conversion to succeed"); let cedar_jval = result.unwrap(); let cedar_jstr = JString::cast(&mut env, cedar_jval.l().unwrap()).unwrap(); - let cedar_str = String::from(env.get_string(&cedar_jstr).unwrap()); + let cedar_str = String::from(env.get_string(&cedar_jstr).unwrap()) + .trim() + .to_string(); - assert!( - cedar_str.contains("User"), - "Expected output to contain 'User'" - ); - assert!( - cedar_str.contains("Group"), - "Expected output to contain 'Group'" - ); - assert!( - cedar_str.contains("File"), - "Expected output to contain 'File'" - ); - assert!( - cedar_str.contains("read"), - "Expected output to contain 'read'" + assert_eq!( + cedar_str, expected_cedar, + "Cedar schema output did not match expected" ); } @@ -1498,68 +1502,93 @@ pub(crate) mod jvm_based_tests { ); } - #[test] - fn get_cedar_schema_internal_null() { - let mut env = JVM.attach_current_thread().unwrap(); - let null_str = JString::from(JObject::null()); - let result = get_cedar_schema_internal(&mut env, null_str); - assert!(result.is_err(), "Expected error on null input"); - } - #[test] fn get_json_schema_internal_valid() { let mut env = JVM.attach_current_thread().unwrap(); let cedar_input = r#" - entity User = { - name: String, - age?: Long, - }; - entity Photo in Album; - entity Album; - action view appliesTo { - principal : [User], - resource: [Album,Photo] - }; + namespace schema { + entity File; + + entity Group; + + entity User in [Group]; + + action "read" appliesTo { + principal: [User], + resource: [File], + context: {} + }; + } "#; + let expected_json = r#"{ + "schema": { + "entityTypes": { + "File": {}, + "Group": {}, + "User": { + "memberOfTypes": [ + "Group" + ] + } + }, + "actions": { + "read": { + "appliesTo": { + "resourceTypes": [ + "File" + ], + "principalTypes": [ + "User" + ] + } + } + } + } +}"#; + let jstr = env.new_string(cedar_input).unwrap(); let result = get_json_schema_internal(&mut env, jstr); assert!(result.is_ok(), "Expected JSON conversion to succeed"); let json_jval = result.unwrap(); let json_jstr = JString::cast(&mut env, json_jval.l().unwrap()).unwrap(); - let json_str = String::from(env.get_string(&json_jstr).unwrap()); + let json_str = String::from(env.get_string(&json_jstr).unwrap()) + .trim() + .to_string(); - assert!( - json_str.contains("\"entityTypes\""), - "Expected output to contain 'entityTypes'" - ); - assert!( - json_str.contains("\"User\""), - "Expected output to contain 'User'" - ); - assert!( - json_str.contains("\"Album\""), - "Expected output to contain 'Album'" - ); - assert!( - json_str.contains("\"Photo\""), - "Expected output to contain 'Photo'" - ); - assert!( - json_str.contains("\"actions\""), - "Expected output to contain 'actions'" - ); - assert!( - json_str.contains("\"view\""), - "Expected output to contain 'view'" + assert_eq!( + json_str, expected_json, + "JSON schema output did not match expected" ); } + #[test] + fn get_json_schema_internal_null() { + let mut env = JVM.attach_current_thread().unwrap(); + let null_str = JString::from(JObject::null()); + let result = get_json_schema_internal(&mut env, null_str); + assert!(result.is_err(), "Expected error on null input"); + } + #[test] fn get_json_schema_internal_invalid_input() { let mut env = JVM.attach_current_thread().unwrap(); - let invalid_cedar = "this is not cedar schema"; + let invalid_cedar = r#" + namespace schema { + entity File + + entity Group with no semicolon + + entity User in [NonExistentGroup]; + + action "read" appliesTo { + principal: [MissingEntity], + resource: [File], + context: {} + }; + } + "#; let jstr = env.new_string(invalid_cedar).unwrap(); let result = get_json_schema_internal(&mut env, jstr); @@ -1569,13 +1598,5 @@ pub(crate) mod jvm_based_tests { result ); } - - #[test] - fn get_json_schema_internal_null() { - let mut env = JVM.attach_current_thread().unwrap(); - let null_str = JString::from(JObject::null()); - let result = get_json_schema_internal(&mut env, null_str); - assert!(result.is_err(), "Expected error on null input"); - } } } From 6e3a612e842624d85469464af3148004754ed2be Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 10:12:15 -0400 Subject: [PATCH 03/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 299dea44..446c1267 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -15,8 +15,9 @@ */ use cedar_policy::entities_errors::EntitiesError; #[cfg(feature = "partial-eval")] + use is_authorized_partial_json_str; use cedar_policy::ffi::{ - is_authorized_partial_json_str, schema_to_json, schema_to_text, Schema as FFISchema, + schema_to_json, schema_to_text, Schema as FFISchema, SchemaToJsonAnswer, SchemaToTextAnswer, }; use cedar_policy::{ From 458e0ec34bca640b4d38e18a90f4c7606bb7e79f Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 10:33:17 -0400 Subject: [PATCH 04/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 446c1267..36a2c67e 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -15,7 +15,7 @@ */ use cedar_policy::entities_errors::EntitiesError; #[cfg(feature = "partial-eval")] - use is_authorized_partial_json_str; +use cedar_policy::ffi::is_authorized_partial_json_str; use cedar_policy::ffi::{ schema_to_json, schema_to_text, Schema as FFISchema, SchemaToJsonAnswer, SchemaToTextAnswer, From dc5a7e6794cd94560ca0ab6f1e494706d17827bf Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 10:35:05 -0400 Subject: [PATCH 05/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, ficed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 36a2c67e..e39b2e65 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -17,8 +17,7 @@ use cedar_policy::entities_errors::EntitiesError; #[cfg(feature = "partial-eval")] use cedar_policy::ffi::is_authorized_partial_json_str; use cedar_policy::ffi::{ - schema_to_json, schema_to_text, Schema as FFISchema, - SchemaToJsonAnswer, SchemaToTextAnswer, + schema_to_json, schema_to_text, Schema as FFISchema, SchemaToJsonAnswer, SchemaToTextAnswer, }; use cedar_policy::{ ffi::{is_authorized_json_str, validate_json_str}, From 6dced3a1afe99f4ebfdd65b22bde9bbfacca98f0 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 10:49:09 -0400 Subject: [PATCH 06/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, ficed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- .../java/com/cedarpolicy/SchemaTests.java | 27 +++--- CedarJavaFFI/src/interface.rs | 88 +++++++++---------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java index 263c670e..d3ed720f 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java @@ -146,9 +146,9 @@ void testFromJson() throws InternalException { """; Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); String result = jsonSchemaObj.toCedarFormat(); - + assertNotNull(result, "Result should not be null"); - assertTrue(result.contains("entity User"), "Converted Cedar should contain the User entity"); + assertTrue(result.contains("entity User;"), "Converted Cedar should contain the User entity"); } @Test @@ -158,22 +158,23 @@ void testEmptySchema() { Exception exception = assertThrows(IllegalStateException.class, emptySchema::toCedarFormat); assertEquals("Schema content is missing", exception.getMessage()); } - + @Test @DisplayName("Should throw exception for malformed JSON schema") void testMalformedSchema() { - // Missing closing brace in the JSON structure String malformedJson = """ { "": { - "entityTypes": { + "entityMalformedTypes": { "User": {} }, - "actions": { + "actions": {} } - """; + } + """; Schema malformedSchema = new Schema(JsonOrCedar.Json, Optional.of(malformedJson), Optional.empty()); - assertThrows(IllegalStateException.class, malformedSchema::toCedarFormat); + assertNotNull(malformedSchema.schemaJson); + assertThrows(InternalException.class, malformedSchema::toCedarFormat); } } @@ -187,10 +188,10 @@ void testFromCedar() throws Exception { String cedarSchema = "entity User;"; Schema cedarSchemaObj = new Schema(cedarSchema); JsonNode result = cedarSchemaObj.toJsonFormat(); - + String expectedJson = "{\"\":{\"entityTypes\":{\"User\":{}},\"actions\":{}}}"; JsonNode expectedNode = new ObjectMapper().readTree(expectedJson); - + assertNotNull(result, "Result should not be null"); assertEquals(expectedNode, result, "JSON should match expected structure"); } @@ -210,10 +211,10 @@ void testFromJson() throws Exception { """; Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonSchema); JsonNode result = jsonSchemaObj.toJsonFormat(); - + ObjectMapper mapper = new ObjectMapper(); JsonNode expectedNode = mapper.readTree(jsonSchema); - + assertNotNull(result, "Result should not be null"); assertEquals(expectedNode, result, "JSON should match the original schema"); } @@ -225,7 +226,7 @@ void testEmptySchema() { Exception exception = assertThrows(IllegalStateException.class, emptySchema::toJsonFormat); assertEquals("Schema content is missing", exception.getMessage()); } - + @Test @DisplayName("Should throw exception for malformed Cedar schema") void testMalformedSchema() { diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index e39b2e65..23d2659d 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -1447,18 +1447,18 @@ pub(crate) mod jvm_based_tests { }"#; let expected_cedar = r#"namespace schema { - entity File; + entity File; - entity Group; + entity Group; - entity User in [Group]; + entity User in [Group]; - action "read" appliesTo { - principal: [User], - resource: [File], - context: {} - }; -}"#; + action "read" appliesTo { + principal: [User], + resource: [File], + context: {} + }; + }"#; let jstr = env.new_string(json_input).unwrap(); let result = get_cedar_schema_internal(&mut env, jstr); @@ -1506,46 +1506,46 @@ pub(crate) mod jvm_based_tests { fn get_json_schema_internal_valid() { let mut env = JVM.attach_current_thread().unwrap(); let cedar_input = r#" - namespace schema { - entity File; + namespace schema { + entity File; - entity Group; + entity Group; - entity User in [Group]; + entity User in [Group]; - action "read" appliesTo { - principal: [User], - resource: [File], - context: {} - }; - } - "#; + action "read" appliesTo { + principal: [User], + resource: [File], + context: {} + }; + } + "#; let expected_json = r#"{ - "schema": { - "entityTypes": { - "File": {}, - "Group": {}, - "User": { - "memberOfTypes": [ - "Group" - ] - } - }, - "actions": { - "read": { - "appliesTo": { - "resourceTypes": [ - "File" - ], - "principalTypes": [ - "User" - ] - } - } - } - } -}"#; + "schema": { + "entityTypes": { + "File": {}, + "Group": {}, + "User": { + "memberOfTypes": [ + "Group" + ] + } + }, + "actions": { + "read": { + "appliesTo": { + "resourceTypes": [ + "File" + ], + "principalTypes": [ + "User" + ] + } + } + } + } + }"#; let jstr = env.new_string(cedar_input).unwrap(); let result = get_json_schema_internal(&mut env, jstr); From 254c3ff50110a59806058cf6c3b1867314b3c46f Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 10:50:30 -0400 Subject: [PATCH 07/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, ficed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 164 +++++++++++++++++----------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 23d2659d..b4f6ae6e 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -1000,39 +1000,39 @@ pub(crate) mod jvm_based_tests { 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" + 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" } + } } - }, - "right": { "Value": "1.3" } - } + } + ] } - } - ] - } - "#; + "#; let java_str = env.new_string(policy_json).unwrap(); let result = from_json_internal(&mut env, java_str); @@ -1284,24 +1284,24 @@ pub(crate) mod jvm_based_tests { 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"] + "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(), "Expected schema to parse successfully"); @@ -1317,24 +1317,24 @@ pub(crate) mod jvm_based_tests { 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"] + "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); @@ -1427,24 +1427,24 @@ pub(crate) mod jvm_based_tests { fn get_cedar_schema_internal_valid() { let mut env = JVM.attach_current_thread().unwrap(); let json_input = r#"{ - "schema": { - "entityTypes": { - "User": { - "memberOfTypes": ["Group"] - }, - "Group": {}, - "File": {} - }, - "actions": { - "read": { - "appliesTo": { - "principalTypes": ["User"], - "resourceTypes": ["File"] + "schema": { + "entityTypes": { + "User": { + "memberOfTypes": ["Group"] + }, + "Group": {}, + "File": {} + }, + "actions": { + "read": { + "appliesTo": { + "principalTypes": ["User"], + "resourceTypes": ["File"] + } + } + } } - } - } - } -}"#; + }"#; let expected_cedar = r#"namespace schema { entity File; From c227d31297ac7891568ae20d55d70fe886d22261 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 10:54:45 -0400 Subject: [PATCH 08/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, ficed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java index d3ed720f..c8399057 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -148,7 +147,8 @@ void testFromJson() throws InternalException { String result = jsonSchemaObj.toCedarFormat(); assertNotNull(result, "Result should not be null"); - assertTrue(result.contains("entity User;"), "Converted Cedar should contain the User entity"); + String expectedCedar = "entity User;"; + assertEquals(expectedCedar, result.trim(), "Converted Cedar should match expected format"); } @Test From 24253714efb843e08444a04e091971232f1ea3d3 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 10:57:50 -0400 Subject: [PATCH 09/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, ficed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index b4f6ae6e..fccb031c 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -1000,7 +1000,7 @@ pub(crate) mod jvm_based_tests { fn from_json_test_valid() { let mut env = JVM.attach_current_thread().unwrap(); - let policy_json = r#" + let policy_json = r#" { "effect": "permit", "principal": { From b6def9cdf7ccbc6d1912e00e056294598d649c93 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 11:11:39 -0400 Subject: [PATCH 10/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, ficed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 112 ++++++++++++++-------------------- 1 file changed, 47 insertions(+), 65 deletions(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index fccb031c..b56d1da0 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -1427,38 +1427,24 @@ pub(crate) mod jvm_based_tests { fn get_cedar_schema_internal_valid() { let mut env = JVM.attach_current_thread().unwrap(); let json_input = r#"{ - "schema": { - "entityTypes": { - "User": { - "memberOfTypes": ["Group"] - }, - "Group": {}, - "File": {} - }, - "actions": { - "read": { - "appliesTo": { - "principalTypes": ["User"], - "resourceTypes": ["File"] - } - } + "schema": { + "entityTypes": { + "User": { + "memberOfTypes": ["Group"] + }, + "Group": {}, + "File": {} + }, + "actions": { + "read": { + "appliesTo": { + "principalTypes": ["User"], + "resourceTypes": ["File"] } } - }"#; - - let expected_cedar = r#"namespace schema { - entity File; - - entity Group; - - entity User in [Group]; - - action "read" appliesTo { - principal: [User], - resource: [File], - context: {} - }; - }"#; + } + } + }"#; let jstr = env.new_string(json_input).unwrap(); let result = get_cedar_schema_internal(&mut env, jstr); @@ -1466,12 +1452,12 @@ pub(crate) mod jvm_based_tests { let cedar_jval = result.unwrap(); let cedar_jstr = JString::cast(&mut env, cedar_jval.l().unwrap()).unwrap(); - let cedar_str = String::from(env.get_string(&cedar_jstr).unwrap()) - .trim() - .to_string(); + let cedar_str = String::from(env.get_string(&cedar_jstr).unwrap()); + let expected_cedar = "namespace schema {\n entity File;\n\n entity Group;\n\n entity User in [Group];\n\n action \"read\" appliesTo {\n principal: [User],\n resource: [File],\n context: {}\n };\n}"; assert_eq!( - cedar_str, expected_cedar, + cedar_str.trim(), + expected_cedar, "Cedar schema output did not match expected" ); } @@ -1521,45 +1507,41 @@ pub(crate) mod jvm_based_tests { } "#; - let expected_json = r#"{ - "schema": { - "entityTypes": { - "File": {}, - "Group": {}, - "User": { - "memberOfTypes": [ - "Group" - ] - } - }, - "actions": { - "read": { - "appliesTo": { - "resourceTypes": [ - "File" - ], - "principalTypes": [ - "User" - ] - } - } - } - } - }"#; - let jstr = env.new_string(cedar_input).unwrap(); let result = get_json_schema_internal(&mut env, jstr); assert!(result.is_ok(), "Expected JSON conversion to succeed"); let json_jval = result.unwrap(); let json_jstr = JString::cast(&mut env, json_jval.l().unwrap()).unwrap(); - let json_str = String::from(env.get_string(&json_jstr).unwrap()) - .trim() - .to_string(); + let json_str = String::from(env.get_string(&json_jstr).unwrap()); + + // Parse the JSON to normalize it for comparison + let actual_json: serde_json::Value = serde_json::from_str(&json_str).unwrap(); + + // Expected JSON structure + let expected_json = serde_json::json!({ + "schema": { + "entityTypes": { + "File": {}, + "Group": {}, + "User": { + "memberOfTypes": ["Group"] + } + }, + "actions": { + "read": { + "appliesTo": { + "principalTypes": ["User"], + "resourceTypes": ["File"] + } + } + } + } + }); assert_eq!( - json_str, expected_json, - "JSON schema output did not match expected" + actual_json, expected_json, + "JSON schema doesn't match expected structure" ); } From e4239a06668bf4ebf8b5caa20b571efc3cf9a36f Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 13:55:42 -0400 Subject: [PATCH 11/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, fixed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- .../java/com/cedarpolicy/model/schema/Schema.java | 13 +++++++++++-- CedarJavaFFI/src/interface.rs | 4 ---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java index fa0efbb5..377b6edb 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java +++ b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java @@ -124,6 +124,14 @@ public static Schema parse(JsonOrCedar type, String str) throws InternalExceptio } + /** + * Converts a schema to Cedar format + * + * @return String representing the schema in Cedar format + * @throws InternalException If conversion from JSON to Cedar format fails + * @throws IllegalStateException If schema content is missing + * @throws NullPointerException If schema text is null + */ public String toCedarFormat() throws InternalException { if (type == JsonOrCedar.Cedar && schemaText.isPresent()) { return schemaText.get(); @@ -138,9 +146,10 @@ public String toCedarFormat() throws InternalException { * Converts a Cedar format schema to JSON format * * @return JsonNode representing the schema in JSON format - * @throws InternalException If schema is not in Cedar format + * @throws InternalException If conversion from Cedar to JSON format fails * @throws JsonMappingException If JSON mapping fails * @throws JsonProcessingException If JSON processing fails + * @throws IllegalStateException If schema content is missing * @throws NullPointerException If schema text is null */ public JsonNode toJsonFormat() @@ -153,7 +162,7 @@ public JsonNode toJsonFormat() throw new IllegalStateException("Schema content is missing"); } } - + /** Specifies the schema format used. */ public enum JsonOrCedar { /** diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index b56d1da0..8f88f3d3 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -1514,11 +1514,7 @@ pub(crate) mod jvm_based_tests { let json_jval = result.unwrap(); let json_jstr = JString::cast(&mut env, json_jval.l().unwrap()).unwrap(); let json_str = String::from(env.get_string(&json_jstr).unwrap()); - - // Parse the JSON to normalize it for comparison let actual_json: serde_json::Value = serde_json::from_str(&json_str).unwrap(); - - // Expected JSON structure let expected_json = serde_json::json!({ "schema": { "entityTypes": { From b61e23e42a9341c6d75e40cf0ddcaf3929a08139 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Fri, 18 Jul 2025 14:47:20 -0400 Subject: [PATCH 12/12] adds schema conversion method along with corresponding test cases rust/java and fixed the import error, fixed the is authorized_json_partial import Signed-off-by: Raheim Swaby --- .../com/cedarpolicy/model/schema/Schema.java | 17 +++++++++-------- .../test/java/com/cedarpolicy/SchemaTests.java | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java index 377b6edb..7a35e951 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java +++ b/CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java @@ -130,15 +130,15 @@ public static Schema parse(JsonOrCedar type, String str) throws InternalExceptio * @return String representing the schema in Cedar format * @throws InternalException If conversion from JSON to Cedar format fails * @throws IllegalStateException If schema content is missing - * @throws NullPointerException If schema text is null + * @throws NullPointerException */ - public String toCedarFormat() throws InternalException { + public String toCedarFormat() throws InternalException, IllegalStateException, NullPointerException { if (type == JsonOrCedar.Cedar && schemaText.isPresent()) { return schemaText.get(); } else if (type == JsonOrCedar.Json && schemaJson.isPresent()) { return jsonToCedarJni(schemaJson.get().toString()); } else { - throw new IllegalStateException("Schema content is missing"); + throw new IllegalStateException("No schema found"); } } @@ -147,19 +147,20 @@ public String toCedarFormat() throws InternalException { * * @return JsonNode representing the schema in JSON format * @throws InternalException If conversion from Cedar to JSON format fails - * @throws JsonMappingException If JSON mapping fails - * @throws JsonProcessingException If JSON processing fails * @throws IllegalStateException If schema content is missing - * @throws NullPointerException If schema text is null + * @throws JsonMappingException If invalid JSON + * @throws JsonProcessingException If invalid JSON + * @throws NullPointerException */ public JsonNode toJsonFormat() - throws InternalException, JsonMappingException, JsonProcessingException, NullPointerException { + throws InternalException, JsonMappingException, JsonProcessingException, NullPointerException, + IllegalStateException { if (type == JsonOrCedar.Json && schemaJson.isPresent()) { return schemaJson.get(); } else if (type == JsonOrCedar.Cedar && schemaText.isPresent()) { return OBJECT_MAPPER.readTree(cedarToJsonJni(schemaText.get())); } else { - throw new IllegalStateException("Schema content is missing"); + throw new IllegalStateException("No schema found"); } } diff --git a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java index c8399057..1883e9a2 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java @@ -156,7 +156,7 @@ void testFromJson() throws InternalException { void testEmptySchema() { Schema emptySchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.empty()); Exception exception = assertThrows(IllegalStateException.class, emptySchema::toCedarFormat); - assertEquals("Schema content is missing", exception.getMessage()); + assertEquals("No schema found", exception.getMessage()); } @Test @@ -224,7 +224,7 @@ void testFromJson() throws Exception { void testEmptySchema() { Schema emptySchema = new Schema(JsonOrCedar.Cedar, Optional.empty(), Optional.empty()); Exception exception = assertThrows(IllegalStateException.class, emptySchema::toJsonFormat); - assertEquals("Schema content is missing", exception.getMessage()); + assertEquals("No schema found", exception.getMessage()); } @Test