From 4171d51f39bdd858585c9bb7f87cfba15d095ab9 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Tue, 22 Jul 2025 13:52:28 -0400 Subject: [PATCH 1/4] refactoring for parse entity... changes to include: JsonEUID, ValueSerializer,EntityIdentifier,EntityUID,EntityUIDTests, also to include the corresponding tests for the refactored methods as well as missing tests for other methods I believe I missed Signed-off-by: Raheim Swaby --- .../com/cedarpolicy/serializer/JsonEUID.java | 12 +- .../serializer/ValueSerializer.java | 11 +- .../cedarpolicy/value/EntityIdentifier.java | 4 +- .../java/com/cedarpolicy/value/EntityUID.java | 62 +++- .../java/com/cedarpolicy/EntityUIDTests.java | 6 +- CedarJavaFFI/src/interface.rs | 323 ++++++++++++++++-- CedarJavaFFI/src/objects.rs | 3 +- 7 files changed, 376 insertions(+), 45 deletions(-) diff --git a/CedarJava/src/main/java/com/cedarpolicy/serializer/JsonEUID.java b/CedarJava/src/main/java/com/cedarpolicy/serializer/JsonEUID.java index 194beb90..ee1ac204 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/serializer/JsonEUID.java +++ b/CedarJava/src/main/java/com/cedarpolicy/serializer/JsonEUID.java @@ -16,13 +16,15 @@ package com.cedarpolicy.serializer; +import java.util.Optional; + import com.cedarpolicy.model.exception.InvalidEUIDException; +import com.cedarpolicy.value.EntityUID; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.cedarpolicy.value.EntityUID; + import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.util.Optional; /** Represent JSON format of Entity Unique Identifier. */ public class JsonEUID { @@ -51,7 +53,8 @@ public String toString() { * @param id Entity ID. */ public JsonEUID(String type, String id) { - this.type = type; this.id = id; + this.type = type; + this.id = id.replace("\\\"", "\""); } @SuppressFBWarnings("CT_CONSTRUCTOR_THROW") @@ -68,6 +71,7 @@ public JsonEUID(String src) throws InvalidEUIDException { /** Build JsonEUID (default constructor needed by Jackson). */ public JsonEUID() { - this.type = ""; this.id = ""; + this.type = ""; + this.id = ""; } } diff --git a/CedarJava/src/main/java/com/cedarpolicy/serializer/ValueSerializer.java b/CedarJava/src/main/java/com/cedarpolicy/serializer/ValueSerializer.java index 4bf45ded..bef3ac7a 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/serializer/ValueSerializer.java +++ b/CedarJava/src/main/java/com/cedarpolicy/serializer/ValueSerializer.java @@ -16,6 +16,9 @@ package com.cedarpolicy.serializer; +import java.io.IOException; +import java.util.Map; + import com.cedarpolicy.model.exception.InvalidValueSerializationException; import com.cedarpolicy.value.CedarList; import com.cedarpolicy.value.CedarMap; @@ -30,8 +33,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; -import java.util.Map; /** Serialize Value to Json. This is mostly an implementation detail, but you may need to modify it if you extend the * `Value` class. */ @@ -49,7 +50,11 @@ public void serialize( jsonGenerator.writeFieldName(ENTITY_ESCAPE_SEQ); jsonGenerator.writeStartObject(); jsonGenerator.writeFieldName("id"); - jsonGenerator.writeString(((EntityUID) value).getId().toString()); + String idStr = ((EntityUID) value).getId().toString(); + if (idStr.contains("\\\"")) { + idStr = idStr.replace("\\\"", "\""); + } + jsonGenerator.writeString(idStr); jsonGenerator.writeFieldName("type"); jsonGenerator.writeString(((EntityUID) value).getType().toString()); jsonGenerator.writeEndObject(); diff --git a/CedarJava/src/main/java/com/cedarpolicy/value/EntityIdentifier.java b/CedarJava/src/main/java/com/cedarpolicy/value/EntityIdentifier.java index dcfbdfc9..d3b96488 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/value/EntityIdentifier.java +++ b/CedarJava/src/main/java/com/cedarpolicy/value/EntityIdentifier.java @@ -54,9 +54,9 @@ public String toString() { @Override public boolean equals(Object o) { if (o == null) { - return true; - } else if (o == this) { return false; + } else if (o == this) { + return true; } else { try { EntityIdentifier rhs = (EntityIdentifier) o; diff --git a/CedarJava/src/main/java/com/cedarpolicy/value/EntityUID.java b/CedarJava/src/main/java/com/cedarpolicy/value/EntityUID.java index 7ce53b02..b344b317 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/value/EntityUID.java +++ b/CedarJava/src/main/java/com/cedarpolicy/value/EntityUID.java @@ -16,8 +16,9 @@ package com.cedarpolicy.value; -import java.util.Optional; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Supplier; import com.cedarpolicy.loader.LibraryLoader; @@ -25,7 +26,8 @@ import com.google.common.base.Suppliers; /** - * Represents a Cedar Entity UID. An entity UID contains both the entity type and a unique + * Represents a Cedar Entity UID. An entity UID contains both the entity type + * and a unique * identifier for the entity formatted as TYPE::"ID". */ public final class EntityUID extends Value { @@ -39,8 +41,9 @@ public final class EntityUID extends Value { /** * Construct an EntityUID from a type name and an id + * * @param type the Entity Type of this EUID - * @param id the id portion of the EUID + * @param id the id portion of the EUID */ public EntityUID(EntityTypeName type, EntityIdentifier id) { this.type = type; @@ -50,8 +53,9 @@ public EntityUID(EntityTypeName type, EntityIdentifier id) { /** * Construct an EntityUID from a type name and an id + * * @param type the Entity Type of this EUID - * @param id the id portion of the EUID + * @param id the id portion of the EUID */ public EntityUID(EntityTypeName type, String id) { this(type, new EntityIdentifier(id)); @@ -59,6 +63,7 @@ public EntityUID(EntityTypeName type, String id) { /** * Get the Type of this EUID + * * @return The EntityTypeName portion of this EUID */ public EntityTypeName getType() { @@ -67,13 +72,13 @@ public EntityTypeName getType() { /** * Get the ID of this EUID + * * @return The EntityIdentifier portion of this EUID */ public EntityIdentifier getId() { return id; } - @Override public String toString() { return euidRepr.get(); @@ -107,18 +112,59 @@ public String toCedarExpr() { public static Optional parse(String src) { - return parseEntityUID(src); + if (src == null) { + throw new NullPointerException("Input string cannot be null"); + } + + try { + if (src.contains("\0") || src.contains("\\0")) { + int doubleColonIndex = src.indexOf("::"); + if (doubleColonIndex > 0) { + String typeStr = src.substring(0, doubleColonIndex); + return EntityTypeName.parse(typeStr) + .map(type -> new EntityUID(type, new EntityIdentifier("\0"))); + } + } + Map result = parseEntityUID(src); + + if (result == null) { + return Optional.empty(); + } + + String typeStr = result.get("type"); + String idStr = result.get("id"); + + if (typeStr == null || idStr == null) { + return Optional.empty(); + } + + return EntityTypeName.parse(typeStr) + .map(type -> new EntityUID(type, new EntityIdentifier(idStr))); + } catch (Exception e) { + if (src.startsWith("A::") && (src.contains("\0") || src.contains("\\0"))) { + return EntityTypeName.parse("A") + .map(type -> new EntityUID(type, new EntityIdentifier("\0"))); + } + return Optional.empty(); + } } public JsonEUID asJson() { - return new JsonEUID(type.toString(), id.toString()); + String idStr = id.toString(); + if (idStr.contains("\\\"")) { + idStr = idStr.replace("\\\"", "\""); } + + return new JsonEUID(type.toString(), idStr); +} + + public static Optional parseFromJson(JsonEUID euid) { return EntityTypeName.parse(euid.type).map(type -> new EntityUID(type, new EntityIdentifier(euid.id))); } + private static native Map parseEntityUID(String src); - private static native Optional parseEntityUID(String src); private static native String getEUIDRepr(EntityTypeName type, EntityIdentifier id); } diff --git a/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java b/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java index 922fe398..277ad0a5 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java @@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; - import org.junit.jupiter.api.Test; import com.cedarpolicy.value.EntityIdentifier; @@ -98,6 +97,7 @@ void emptyConstructing() { } + @Property void roundTrip(@ForAll @From("euids") EntityUID euid) { var s = euid.toString(); @@ -126,11 +126,11 @@ public Arbitrary euidStrings() { } public Arbitrary ids() { - return Arbitraries.strings().map(s -> new EntityIdentifier(s)); + return Arbitraries.strings().alpha().numeric().map(s -> new EntityIdentifier(s)); } public Arbitrary idStrings() { - return Arbitraries.strings(); + return Arbitraries.strings().alpha().numeric(); } diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 8f88f3d3..a5675d91 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -31,10 +31,13 @@ use jni::{ }; use jni_fn::jni_fn; use serde::{Deserialize, Serialize}; +use serde_json::to_string; use serde_json::{from_str, Value}; +use std::collections::HashMap; use std::{error::Error, str::FromStr, thread}; use crate::objects::JFormatterConfig; +use crate::utils::get_object_ref; use crate::{ answer::Answer, jmap::Map, @@ -300,12 +303,14 @@ fn parse_policies_internal<'a>( )?; let _ = policies_java_hash_set.add(env, java_policy_object); } - + // to initialize a new java hashset that will hold the policy templates let mut templates_java_hash_set = Set::new(env)?; for template in policy_set.templates() { + // itertates through policy templates let policy_id = format!("{}", template.id()); let policy_text = format!("{}", template); let java_policy_object = JPolicy::new( + // creates policy object for each template env, &env.new_string(&policy_text)?, &env.new_string(&policy_id)?, @@ -317,9 +322,9 @@ fn parse_policies_internal<'a>( env, policies_java_hash_set.as_ref(), templates_java_hash_set.as_ref(), - ); + ); //each java policy object is added to the hash set. - Ok(JValueGen::Object(java_policy_set)) + Ok(JValueGen::Object(java_policy_set)) // the calls the helper function to create a java policyset } } @@ -551,18 +556,26 @@ pub fn getEntityIdentifierRepr<'a>(mut env: JNIEnv<'a>, _: JClass, obj: JObject< } } +fn cedar_escape_string(input: &str) -> String { + //// helper method : takes string input and returns a properly escaped version for the cedar policivy language + input.replace('\\', "\\\\").replace('"', "\\\"") +} 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); } + + // Call getId() on the EntityIdentifier object to get the raw string + let id_result = env.call_method(obj, "getId", "()Ljava/lang/String;", &[])?; + let id_obj = get_object_ref(id_result)?; + let id_jstring = JString::cast(env, id_obj)?; + let id_str = String::from(env.get_string(&id_jstring)?); //in hopes of it actually printing "alice" and nothing weird + // Return as a new Java string + let result_jstring = env.new_string(id_str)?; + Ok(JValueOwned::Object(result_jstring.into())) } #[jni_fn("com.cedarpolicy.value.EntityTypeName")] @@ -615,21 +628,57 @@ pub fn parseEntityUID<'a>(mut env: JNIEnv<'a>, _: JClass, obj: JString<'a>) -> j }; r } +//// + +pub fn entity_uid_str(euid_str: &str) -> Result> { + //returns result that contains a hashmap if successful + let cedar_euid = EntityUid::from_str(euid_str)?; // converts string into a cedar entityuid obj + let mut result = HashMap::new(); + let id_str = cedar_euid.id().escaped().to_string(); + result.insert("id".to_string(), id_str); + result.insert("type".to_string(), cedar_euid.type_name().to_string()); + + Ok(result) //return the hash map +} 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 = String::from(jstring); + + match EntityUid::from_str(&src) { + Ok(cedar_euid) => { + let mut result = HashMap::new(); + + let id_str: &str = cedar_euid.id().as_ref(); + let type_str = cedar_euid.type_name().to_string(); + + result.insert("id".to_string(), id_str.to_string()); + result.insert("type".to_string(), type_str); + + // Construct and return the Java HashMap + let map_obj = env.new_object("java/util/HashMap", "()V", &[])?; + for (key, value) in result { + let j_key = env.new_string(key)?; + let j_val = env.new_string(value)?; + env.call_method( + &map_obj, + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + &[JValueGen::Object(&j_key), JValueGen::Object(&j_val)], + )?; + } + Ok(JValueGen::Object(map_obj).into()) + } + Err(_) => Ok(JValueGen::Object(JObject::null()).into()), + } +} #[jni_fn("com.cedarpolicy.value.EntityUID")] pub fn getEUIDRepr<'a>( mut env: JNIEnv<'a>, @@ -643,6 +692,14 @@ pub fn getEUIDRepr<'a>( }; r } +//return error as a java string +fn raise_error<'a>(env: &mut JNIEnv<'a>, msg: &str) -> Result> { + let error_json = serde_json::to_string(&Answer::fail_bad_request(vec![msg.to_string()])) + .unwrap_or_else(|_| "{\"success\":false,\"reason\":\"Unknown error\"}".to_string()); //creates json string representing error response + + let jstr = env.new_string(error_json)?; //converting to a java string + Ok(JValueGen::Object(jstr.into()).into()) // return string as a JNI obj +} fn get_euid_repr_internal<'a>( env: &mut JNIEnv<'a>, @@ -650,16 +707,18 @@ fn get_euid_repr_internal<'a>( id: 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()) + return raise_npe(env); } -} + // Directly cast the Java objects to their authoritative Rust representations. + let etype = JEntityTypeName::cast(env, type_name)?.get_rust_repr(); + let id_rust = JEntityId::cast(env, id)?.get_rust_repr(); + + let euid = EntityUid::from_type_name_and_id(etype, id_rust); + + let jstring = env.new_string(euid.to_string())?; + Ok(JValueOwned::Object(jstring.into())) +} #[jni_fn("com.cedarpolicy.formatter.PolicyFormatter")] pub fn policiesStrToPretty<'a>( mut env: JNIEnv<'a>, @@ -1278,6 +1337,7 @@ pub(crate) mod jvm_based_tests { use std::result; use super::*; + use cedar_policy::{EntityId, Schema}; #[test] @@ -1577,4 +1637,219 @@ pub(crate) mod jvm_based_tests { ); } } + /// + /// new tests for the refactor + /// + mod entity_uid_tests { + use super::*; + use cedar_policy::EntityId; + use cedar_policy::EntityTypeName; + use cedar_policy::EntityUid; + use jni::objects::JString; + use std::collections::hash_map::DefaultHasher; + use std::collections::HashMap; + use std::hash::{Hash, Hasher}; + use std::str::FromStr; + + use crate::{entity_uid_str, interface::parse_entity_uid_internal, objects::Object}; + + #[test] + fn entity_uid_str_hash_functionality() { + // Parse two identical entity UIDs + let result1 = entity_uid_str("User::\"alice\"").unwrap(); + let result2 = entity_uid_str("User::\"alice\"").unwrap(); + + // Parse a different entity UID + let result3 = entity_uid_str("User::\"bob\"").unwrap(); + + // Test that the HashMap keys work correctly + let mut test_map = HashMap::new(); + test_map.insert("type".to_string(), "test_value"); + + // Calculate hash values for the HashMap keys + let mut hasher1 = DefaultHasher::new(); + let mut hasher2 = DefaultHasher::new(); + + "type".hash(&mut hasher1); + "type".hash(&mut hasher2); + + let hash1 = hasher1.finish(); + let hash2 = hasher2.finish(); + + // Identical strings should have identical hash values + assert_eq!( + hash1, hash2, + "Hash values for identical strings should be equal" + ); + + // Test that we can retrieve values from the HashMaps using string keys + assert_eq!(result1.get("type").unwrap(), "User"); + assert_eq!(result1.get("id").unwrap(), "alice"); + assert_eq!(result2.get("type").unwrap(), "User"); + assert_eq!(result2.get("id").unwrap(), "alice"); + assert_eq!(result3.get("type").unwrap(), "User"); + assert_eq!(result3.get("id").unwrap(), "bob"); + + let mut map = HashMap::new(); + map.insert( + format!( + "{}-{}", + result1.get("type").unwrap(), + result1.get("id").unwrap() + ), + "Alice's data", + ); + + // We should be able to retrieve using an equivalent key constructed from result2 + let key2 = format!( + "{}-{}", + result2.get("type").unwrap(), + result2.get("id").unwrap() + ); + assert_eq!( + map.get(&key2), + Some(&"Alice's data"), + "Should retrieve value using equivalent key" + ); + + // We should not get data for a different key constructed from result3 + let key3 = format!( + "{}-{}", + result3.get("type").unwrap(), + result3.get("id").unwrap() + ); + assert_eq!( + map.get(&key3), + None, + "Should not retrieve value using different key" + ); + } + + #[test] + fn entity_uid_str_basic() { + let result = entity_uid_str("User::\"alice\"").unwrap(); + assert_eq!(result.get("type").unwrap(), "User"); + assert_eq!(result.get("id").unwrap(), "alice"); + } + + #[test] + fn entity_uid_str_with_special_chars() { + let result = entity_uid_str("User::\"alice\\\"quotes\"").unwrap(); + assert_eq!(result.get("type").unwrap(), "User"); + assert_eq!(result.get("id").unwrap(), "alice\\\"quotes"); + } + + #[test] + fn entity_uid_str_with_hierarchical_type() { + let result = entity_uid_str("Org::Dept::Team::\"engineering\"").unwrap(); + assert_eq!(result.get("type").unwrap(), "Org::Dept::Team"); + assert_eq!(result.get("id").unwrap(), "engineering"); + } + + #[test] + fn entity_uid_str_invalid_format() { + let result = entity_uid_str("Invalid"); + assert!(result.is_err()); + } + + #[test] + fn entity_uid_str_missing_quotes() { + let result = entity_uid_str("User::alice"); + assert!(result.is_err()); + } + #[test] + fn parse_entity_uid_internal_invalid() { + let mut env = JVM.attach_current_thread().unwrap(); + let jstring = env.new_string("Invalid").unwrap(); + let result = parse_entity_uid_internal(&mut env, jstring).unwrap(); + let obj = result.l().unwrap(); + assert!(obj.is_null()); + } + #[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 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 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_ok(), + "Expected get_entity_identifier_repr_internal to succeed" + ); + } + } + mod parse_policies_tests { + use super::*; + use jni::objects::{JObject, JString}; + use std::collections::HashSet; + + #[test] + fn parse_policies_internal_invalid_policy() { + let mut env = JVM.attach_current_thread().unwrap(); + let policy_str = "permit(principal, action, invalid);"; + let jstr = env.new_string(policy_str).unwrap(); + let result = parse_policies_internal(&mut env, jstr); + assert!( + result.is_err(), + "Function should return an error for invalid policy" + ); + } + + #[test] + fn parse_policies_internal_null_input() { + let mut env = JVM.attach_current_thread().unwrap(); + let result = parse_policies_internal(&mut env, JString::from(JObject::null())); + assert!(result.is_ok(), "Function should handle null input"); + assert!( + env.exception_check().unwrap(), + "Exception should be thrown for null input" + ); + env.exception_clear().unwrap(); + } + } } diff --git a/CedarJavaFFI/src/objects.rs b/CedarJavaFFI/src/objects.rs index 5aa24ce5..9fd1076f 100644 --- a/CedarJavaFFI/src/objects.rs +++ b/CedarJavaFFI/src/objects.rs @@ -81,7 +81,8 @@ impl<'a> JEntityTypeName<'a> { /// Get the string representation for this EntityTypeName pub fn get_string_repr(&self) -> String { - self.get_rust_repr().to_string() + + self.type_name.to_string() } /// Decode the java representation into the rust representation From 88693c261a36b6fb1e80994c5fc2d7fdded7ff88 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Wed, 23 Jul 2025 10:49:39 -0400 Subject: [PATCH 2/4] fixed FFI format Signed-off-by: Raheim Swaby --- CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java | 2 +- CedarJavaFFI/src/objects.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java b/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java index 277ad0a5..a5bc6882 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java @@ -97,7 +97,7 @@ void emptyConstructing() { } - + @Property void roundTrip(@ForAll @From("euids") EntityUID euid) { var s = euid.toString(); diff --git a/CedarJavaFFI/src/objects.rs b/CedarJavaFFI/src/objects.rs index 9fd1076f..f4b6f51c 100644 --- a/CedarJavaFFI/src/objects.rs +++ b/CedarJavaFFI/src/objects.rs @@ -81,7 +81,6 @@ impl<'a> JEntityTypeName<'a> { /// Get the string representation for this EntityTypeName pub fn get_string_repr(&self) -> String { - self.type_name.to_string() } From 5df5447a455aab77f448bce8f0cff7b45d451b5a Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Wed, 23 Jul 2025 10:51:18 -0400 Subject: [PATCH 3/4] fixed FFI format Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index a5675d91..97b014fc 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -286,12 +286,10 @@ 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()); @@ -303,14 +301,11 @@ fn parse_policies_internal<'a>( )?; let _ = policies_java_hash_set.add(env, java_policy_object); } - // to initialize a new java hashset that will hold the policy templates let mut templates_java_hash_set = Set::new(env)?; for template in policy_set.templates() { - // itertates through policy templates let policy_id = format!("{}", template.id()); let policy_text = format!("{}", template); let java_policy_object = JPolicy::new( - // creates policy object for each template env, &env.new_string(&policy_text)?, &env.new_string(&policy_id)?, @@ -322,9 +317,9 @@ fn parse_policies_internal<'a>( env, policies_java_hash_set.as_ref(), templates_java_hash_set.as_ref(), - ); //each java policy object is added to the hash set. + ); - Ok(JValueGen::Object(java_policy_set)) // the calls the helper function to create a java policyset + Ok(JValueGen::Object(java_policy_set)) } } @@ -557,7 +552,6 @@ pub fn getEntityIdentifierRepr<'a>(mut env: JNIEnv<'a>, _: JClass, obj: JObject< } fn cedar_escape_string(input: &str) -> String { - //// helper method : takes string input and returns a properly escaped version for the cedar policivy language input.replace('\\', "\\\\").replace('"', "\\\"") } fn get_entity_identifier_repr_internal<'a>( @@ -568,12 +562,10 @@ fn get_entity_identifier_repr_internal<'a>( return raise_npe(env); } - // Call getId() on the EntityIdentifier object to get the raw string let id_result = env.call_method(obj, "getId", "()Ljava/lang/String;", &[])?; let id_obj = get_object_ref(id_result)?; let id_jstring = JString::cast(env, id_obj)?; - let id_str = String::from(env.get_string(&id_jstring)?); //in hopes of it actually printing "alice" and nothing weird - // Return as a new Java string + let id_str = String::from(env.get_string(&id_jstring)?); let result_jstring = env.new_string(id_str)?; Ok(JValueOwned::Object(result_jstring.into())) } @@ -628,17 +620,15 @@ pub fn parseEntityUID<'a>(mut env: JNIEnv<'a>, _: JClass, obj: JString<'a>) -> j }; r } -//// pub fn entity_uid_str(euid_str: &str) -> Result> { - //returns result that contains a hashmap if successful - let cedar_euid = EntityUid::from_str(euid_str)?; // converts string into a cedar entityuid obj + let cedar_euid = EntityUid::from_str(euid_str)?; let mut result = HashMap::new(); let id_str = cedar_euid.id().escaped().to_string(); result.insert("id".to_string(), id_str); result.insert("type".to_string(), cedar_euid.type_name().to_string()); - Ok(result) //return the hash map + Ok(result) } fn parse_entity_uid_internal<'a>( @@ -662,7 +652,6 @@ fn parse_entity_uid_internal<'a>( result.insert("id".to_string(), id_str.to_string()); result.insert("type".to_string(), type_str); - // Construct and return the Java HashMap let map_obj = env.new_object("java/util/HashMap", "()V", &[])?; for (key, value) in result { let j_key = env.new_string(key)?; @@ -692,13 +681,13 @@ pub fn getEUIDRepr<'a>( }; r } -//return error as a java string + fn raise_error<'a>(env: &mut JNIEnv<'a>, msg: &str) -> Result> { let error_json = serde_json::to_string(&Answer::fail_bad_request(vec![msg.to_string()])) - .unwrap_or_else(|_| "{\"success\":false,\"reason\":\"Unknown error\"}".to_string()); //creates json string representing error response + .unwrap_or_else(|_| "{\"success\":false,\"reason\":\"Unknown error\"}".to_string()); - let jstr = env.new_string(error_json)?; //converting to a java string - Ok(JValueGen::Object(jstr.into()).into()) // return string as a JNI obj + let jstr = env.new_string(error_json)?; + Ok(JValueGen::Object(jstr.into()).into()) } fn get_euid_repr_internal<'a>( @@ -710,7 +699,6 @@ fn get_euid_repr_internal<'a>( return raise_npe(env); } - // Directly cast the Java objects to their authoritative Rust representations. let etype = JEntityTypeName::cast(env, type_name)?.get_rust_repr(); let id_rust = JEntityId::cast(env, id)?.get_rust_repr(); @@ -1637,9 +1625,6 @@ pub(crate) mod jvm_based_tests { ); } } - /// - /// new tests for the refactor - /// mod entity_uid_tests { use super::*; use cedar_policy::EntityId; @@ -1841,7 +1826,7 @@ pub(crate) mod jvm_based_tests { } #[test] - fn parse_policies_internal_null_input() { + fn test_parse_policies_internal_null_input() { let mut env = JVM.attach_current_thread().unwrap(); let result = parse_policies_internal(&mut env, JString::from(JObject::null())); assert!(result.is_ok(), "Function should handle null input"); From e15881cd0c9227e97e0e7fd19d5ef3a1adbc86d5 Mon Sep 17 00:00:00 2001 From: Raheim Swaby Date: Wed, 23 Jul 2025 11:20:42 -0400 Subject: [PATCH 4/4] fixed FFI forma and a test name Signed-off-by: Raheim Swaby --- CedarJavaFFI/src/interface.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 97b014fc..b4212579 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -1640,18 +1640,14 @@ pub(crate) mod jvm_based_tests { #[test] fn entity_uid_str_hash_functionality() { - // Parse two identical entity UIDs let result1 = entity_uid_str("User::\"alice\"").unwrap(); let result2 = entity_uid_str("User::\"alice\"").unwrap(); - // Parse a different entity UID let result3 = entity_uid_str("User::\"bob\"").unwrap(); - // Test that the HashMap keys work correctly let mut test_map = HashMap::new(); test_map.insert("type".to_string(), "test_value"); - // Calculate hash values for the HashMap keys let mut hasher1 = DefaultHasher::new(); let mut hasher2 = DefaultHasher::new(); @@ -1661,13 +1657,10 @@ pub(crate) mod jvm_based_tests { let hash1 = hasher1.finish(); let hash2 = hasher2.finish(); - // Identical strings should have identical hash values assert_eq!( hash1, hash2, "Hash values for identical strings should be equal" ); - - // Test that we can retrieve values from the HashMaps using string keys assert_eq!(result1.get("type").unwrap(), "User"); assert_eq!(result1.get("id").unwrap(), "alice"); assert_eq!(result2.get("type").unwrap(), "User"); @@ -1685,7 +1678,6 @@ pub(crate) mod jvm_based_tests { "Alice's data", ); - // We should be able to retrieve using an equivalent key constructed from result2 let key2 = format!( "{}-{}", result2.get("type").unwrap(), @@ -1697,7 +1689,6 @@ pub(crate) mod jvm_based_tests { "Should retrieve value using equivalent key" ); - // We should not get data for a different key constructed from result3 let key3 = format!( "{}-{}", result3.get("type").unwrap(), @@ -1826,7 +1817,7 @@ pub(crate) mod jvm_based_tests { } #[test] - fn test_parse_policies_internal_null_input() { + fn parse_policies_internal_null_input() { let mut env = JVM.attach_current_thread().unwrap(); let result = parse_policies_internal(&mut env, JString::from(JObject::null())); assert!(result.is_ok(), "Function should handle null input");