From 80f8077685d4c4158dc2259b8fc358b1108f63a1 Mon Sep 17 00:00:00 2001 From: Felix Zheng <37223155+felixzheng98@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:39:24 -0400 Subject: [PATCH] Add support for specifying formatter config (#235) Signed-off-by: Felix Zheng --- .../formatter/PolicyFormatter.java | 4 +++ .../cedarpolicy/model/formatter/Config.java | 22 ++++++++++++ .../com/cedarpolicy/PolicyFormatterTests.java | 27 +++++++++++++++ .../formatted_policy_custom_config.cedar | 6 ++++ .../test/resources/malformed_policy_set.cedar | 2 +- .../test/resources/unformatted_policy.cedar | 2 +- CedarJavaFFI/src/interface.rs | 25 ++++++++++++-- CedarJavaFFI/src/objects.rs | 34 +++++++++++++++++++ 8 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 CedarJava/src/main/java/com/cedarpolicy/model/formatter/Config.java create mode 100644 CedarJava/src/test/resources/formatted_policy_custom_config.cedar diff --git a/CedarJava/src/main/java/com/cedarpolicy/formatter/PolicyFormatter.java b/CedarJava/src/main/java/com/cedarpolicy/formatter/PolicyFormatter.java index afd3ae4..2c4f1d1 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/formatter/PolicyFormatter.java +++ b/CedarJava/src/main/java/com/cedarpolicy/formatter/PolicyFormatter.java @@ -2,6 +2,7 @@ import com.cedarpolicy.loader.LibraryLoader; import com.cedarpolicy.model.exception.InternalException; +import com.cedarpolicy.model.formatter.Config; public final class PolicyFormatter { @@ -14,4 +15,7 @@ private PolicyFormatter() { public static native String policiesStrToPretty(String policies) throws InternalException, NullPointerException; + + public static native String policiesStrToPrettyWithConfig(String policies, Config config) + throws InternalException, NullPointerException; } diff --git a/CedarJava/src/main/java/com/cedarpolicy/model/formatter/Config.java b/CedarJava/src/main/java/com/cedarpolicy/model/formatter/Config.java new file mode 100644 index 0000000..3fc20e7 --- /dev/null +++ b/CedarJava/src/main/java/com/cedarpolicy/model/formatter/Config.java @@ -0,0 +1,22 @@ +package com.cedarpolicy.model.formatter; + +public class Config { + + private final int lineWidth; + private final int indentWidth; + + public Config(int lineWidth, int indentWidth) { + this.lineWidth = lineWidth; + this.indentWidth = indentWidth; + } + + @SuppressWarnings("unused") + public int getLineWidth() { + return lineWidth; + } + + @SuppressWarnings("unused") + public int getIndentWidth() { + return indentWidth; + } +} diff --git a/CedarJava/src/test/java/com/cedarpolicy/PolicyFormatterTests.java b/CedarJava/src/test/java/com/cedarpolicy/PolicyFormatterTests.java index abac4d1..ec6ef0d 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/PolicyFormatterTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/PolicyFormatterTests.java @@ -2,6 +2,7 @@ import com.cedarpolicy.formatter.PolicyFormatter; import com.cedarpolicy.model.exception.InternalException; +import com.cedarpolicy.model.formatter.Config; import java.nio.file.Files; import java.nio.file.Path; import org.junit.jupiter.api.Test; @@ -37,4 +38,30 @@ public void testPoliciesStrToPrettyMalformedCedarPolicy() throws Exception { public void testPoliciesStrToPrettyNullSafety() { assertThrows(NullPointerException.class, () -> PolicyFormatter.policiesStrToPretty(null)); } + + @Test + public void testPoliciesStrToPrettyWithConfigNullSafety() throws Exception { + String cedarPolicy = Files.readString(Path.of(TEST_RESOURCES_DIR + "formatted_policy.cedar")); + + assertThrows(NullPointerException.class, + () -> PolicyFormatter.policiesStrToPrettyWithConfig(null, null)); + + assertThrows(NullPointerException.class, + () -> PolicyFormatter.policiesStrToPrettyWithConfig(cedarPolicy, null)); + + assertThrows(NullPointerException.class, + () -> PolicyFormatter.policiesStrToPrettyWithConfig(null, new Config(120, 4))); + } + + @Test + public void testPoliciesStrToPrettyWithConfig() throws Exception { + String unformattedCedarPolicy = Files.readString( + Path.of(TEST_RESOURCES_DIR + "unformatted_policy.cedar")); + + String formattedCedarPolicyWithCustomConfig = Files.readString( + Path.of(TEST_RESOURCES_DIR + "formatted_policy_custom_config.cedar")); + + assertEquals(formattedCedarPolicyWithCustomConfig, + PolicyFormatter.policiesStrToPrettyWithConfig(unformattedCedarPolicy, new Config(120, 4))); + } } diff --git a/CedarJava/src/test/resources/formatted_policy_custom_config.cedar b/CedarJava/src/test/resources/formatted_policy_custom_config.cedar new file mode 100644 index 0000000..1b940bc --- /dev/null +++ b/CedarJava/src/test/resources/formatted_policy_custom_config.cedar @@ -0,0 +1,6 @@ +permit ( + principal, + action == Action::"update", + resource +) +when { resource.owner == principal }; diff --git a/CedarJava/src/test/resources/malformed_policy_set.cedar b/CedarJava/src/test/resources/malformed_policy_set.cedar index 8097238..7325888 100644 --- a/CedarJava/src/test/resources/malformed_policy_set.cedar +++ b/CedarJava/src/test/resources/malformed_policy_set.cedar @@ -10,4 +10,4 @@ forbid ( principal == User::"Liam", action, resource = Photo::"Husky.jpg" -); \ No newline at end of file +); diff --git a/CedarJava/src/test/resources/unformatted_policy.cedar b/CedarJava/src/test/resources/unformatted_policy.cedar index 35121eb..7897777 100644 --- a/CedarJava/src/test/resources/unformatted_policy.cedar +++ b/CedarJava/src/test/resources/unformatted_policy.cedar @@ -3,4 +3,4 @@ permit( action == Action::"update", resource -) when {resource.owner == principal}; \ No newline at end of file +) when {resource.owner == principal}; diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 6a6fff4..419d9af 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -32,6 +32,7 @@ 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, jset::Set, @@ -537,7 +538,20 @@ pub fn policiesStrToPretty<'a>( _: JClass, policies_jstr: JString<'a>, ) -> jvalue { - match policies_str_to_pretty_internal(&mut env, policies_jstr) { + match policies_str_to_pretty_internal(&mut env, policies_jstr, None) { + Ok(v) => v.as_jni(), + Err(e) => jni_failed(&mut env, e.as_ref()), + } +} + +#[jni_fn("com.cedarpolicy.formatter.PolicyFormatter")] +pub fn policiesStrToPrettyWithConfig<'a>( + mut env: JNIEnv<'a>, + _: JClass, + policies_jstr: JString<'a>, + config_obj: JObject<'a>, +) -> jvalue { + match policies_str_to_pretty_internal(&mut env, policies_jstr, Some(config_obj)) { Ok(v) => v.as_jni(), Err(e) => jni_failed(&mut env, e.as_ref()), } @@ -546,11 +560,16 @@ pub fn policiesStrToPretty<'a>( fn policies_str_to_pretty_internal<'a>( env: &mut JNIEnv<'a>, policies_jstr: JString<'a>, + config_obj: Option>, ) -> Result> { - if policies_jstr.is_null() { + if policies_jstr.is_null() || config_obj.as_ref().is_some_and(|obj| obj.is_null()) { raise_npe(env) } else { - let config = Config::default(); + let config = if let Some(obj) = config_obj { + JFormatterConfig::cast(env, obj)?.get_rust_repr() + } else { + 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()), diff --git a/CedarJavaFFI/src/objects.rs b/CedarJavaFFI/src/objects.rs index 8d20a74..5aa24ce 100644 --- a/CedarJavaFFI/src/objects.rs +++ b/CedarJavaFFI/src/objects.rs @@ -21,6 +21,7 @@ use crate::{ use std::{marker::PhantomData, str::FromStr}; use cedar_policy::{EntityId, EntityTypeName, EntityUid}; +use cedar_policy_formatter::Config; use jni::{ objects::{JObject, JString, JValueGen, JValueOwned}, sys::jvalue, @@ -368,3 +369,36 @@ impl<'a> AsRef> for JPolicy<'a> { &self.obj } } + +pub struct JFormatterConfig<'a> { + obj: JObject<'a>, + formatter_config: Config, +} + +impl<'a> JFormatterConfig<'a> { + pub fn get_rust_repr(&self) -> Config { + self.formatter_config.clone() + } +} + +impl<'a> AsRef> for JFormatterConfig<'a> { + fn as_ref(&self) -> &JObject<'a> { + &self.obj + } +} + +impl<'a> Object<'a> for JFormatterConfig<'a> { + fn cast(env: &mut JNIEnv<'a>, obj: JObject<'a>) -> Result { + assert_is_class(env, &obj, "com/cedarpolicy/model/formatter/Config")?; + let line_width_jint = env.call_method(&obj, "getLineWidth", "()I", &[])?.i()?; + let indent_width_jint = env.call_method(&obj, "getIndentWidth", "()I", &[])?.i()?; + let formatter_config = Config { + line_width: usize::try_from(line_width_jint)?, + indent_width: isize::try_from(indent_width_jint)?, + }; + Ok(Self { + obj, + formatter_config, + }) + } +}