diff --git a/CedarJava/src/main/java/com/cedarpolicy/model/Effect.java b/CedarJava/src/main/java/com/cedarpolicy/model/Effect.java new file mode 100644 index 00000000..a8ee4995 --- /dev/null +++ b/CedarJava/src/main/java/com/cedarpolicy/model/Effect.java @@ -0,0 +1,34 @@ +package com.cedarpolicy.model; + +/** + * Represents the effect of a Cedar policy. + */ +public enum Effect { + PERMIT, + FORBID; + + /** + * Converts a string to an Effect enum value, case-insensitive. + * + * @param effectString the string value to convert + * @return the corresponding Effect enum value + * @throws NullPointerException if the effectString is null + * @throws IllegalArgumentException if the effectString doesn't match any Effect in {permit, forbid} + */ + public static Effect fromString(String effectString) { + + if (effectString == null) { + throw new NullPointerException(); + } + + switch (effectString.toLowerCase()) { + case "permit": + return PERMIT; + case "forbid": + return FORBID; + default: + throw new IllegalArgumentException("Invalid Effect: " + effectString + ". Expected 'permit' or 'forbid'"); + } + } +} + diff --git a/CedarJava/src/main/java/com/cedarpolicy/model/policy/Policy.java b/CedarJava/src/main/java/com/cedarpolicy/model/policy/Policy.java index 2f58c762..5f1a7a93 100644 --- a/CedarJava/src/main/java/com/cedarpolicy/model/policy/Policy.java +++ b/CedarJava/src/main/java/com/cedarpolicy/model/policy/Policy.java @@ -20,6 +20,7 @@ import com.cedarpolicy.model.exception.InternalException; import com.fasterxml.jackson.annotation.JsonProperty; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import com.cedarpolicy.model.Effect; import java.util.concurrent.atomic.AtomicInteger; @@ -78,6 +79,24 @@ public String toString() { return "// Policy ID: " + policyID + "\n" + policySrc; } + /** + * Returns the effect of a policy. + * + * Determines the policy effect by attempting static policy first, then template. + * In future, it will only support static policies once new class is introduced for Template. + * + * @return The effect of the policy, either "permit" or "forbid" + * @throws InternalException + * @throws NullPointerException + */ + public Effect effect() throws InternalException, NullPointerException { + try { + return Effect.fromString(policyEffectJni(policySrc)); // Get effect for static policy + } catch (InternalException e) { + return Effect.fromString(templateEffectJni(policySrc)); // Get effect for template + } + } + /** * Get the JSON representation of the policy. Currently only supports static policies. */ @@ -106,4 +125,6 @@ private static native String parsePolicyTemplateJni(String policyTemplateStr) private native String toJsonJni(String policyStr) throws InternalException, NullPointerException; private static native String fromJsonJni(String policyJsonStr) throws InternalException, NullPointerException; + private native String policyEffectJni(String policyStr) throws InternalException, NullPointerException; + private native String templateEffectJni(String policyStr) throws InternalException, NullPointerException; } diff --git a/CedarJava/src/test/java/com/cedarpolicy/PolicyTests.java b/CedarJava/src/test/java/com/cedarpolicy/PolicyTests.java index ab293957..a4a5de5b 100644 --- a/CedarJava/src/test/java/com/cedarpolicy/PolicyTests.java +++ b/CedarJava/src/test/java/com/cedarpolicy/PolicyTests.java @@ -18,6 +18,7 @@ import com.cedarpolicy.model.exception.InternalException; import com.cedarpolicy.model.policy.Policy; +import com.cedarpolicy.model.Effect; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -109,4 +110,33 @@ public void policyFromJsonTest() throws InternalException { String actualJson = p.toJson(); assertEquals(validJson, actualJson); } + + @Test void policyEffectTest() throws InternalException { + + assertThrows(NullPointerException.class, () -> { + Policy p = new Policy(null, null); + p.effect(); + }); + + // For effects not in {permit, forbid} + assertThrows(InternalException.class, () -> { + Policy p = new Policy("perm(principal == ?principal, action, resource in ?resource);", null); + p.effect(); + }); + + // Tests for static policies + Policy permitPolicy = new Policy("permit(principal, action, resource);", null); + assertEquals(permitPolicy.effect(), Effect.PERMIT); + + Policy forbidPolicy = new Policy("forbid(principal, action, resource);", null); + assertEquals(forbidPolicy.effect(), Effect.FORBID); + + // Tests for templates + Policy permitTemplate = new Policy("permit(principal == ?principal, action, resource == ?resource);", null); + assertEquals(permitTemplate.effect(), Effect.PERMIT); + + Policy forbidTemplate = new Policy("forbid(principal == ?principal, action, resource == ?resource);", null); + assertEquals(forbidTemplate.effect(), Effect.FORBID); + + } } diff --git a/CedarJavaFFI/src/interface.rs b/CedarJavaFFI/src/interface.rs index 419d9af0..94e30491 100644 --- a/CedarJavaFFI/src/interface.rs +++ b/CedarJavaFFI/src/interface.rs @@ -387,6 +387,52 @@ fn to_json_internal<'a>(env: &mut JNIEnv<'a>, policy_jstr: JString<'a>) -> Resul } } +#[jni_fn("com.cedarpolicy.model.policy.Policy")] +pub fn policyEffectJni<'a>(mut env: JNIEnv<'a>, _: JClass, policy_jstr: JString<'a>) -> jvalue { + match policy_effect_jni_internal(&mut env, policy_jstr) { + Err(e) => jni_failed(&mut env, e.as_ref()), + Ok(effect) => effect.as_jni(), + } +} + +fn policy_effect_jni_internal<'a>( + env: &mut JNIEnv<'a>, + policy_jstr: JString<'a>, +) -> Result> { + if policy_jstr.is_null() { + raise_npe(env) + } else { + let policy_jstring = env.get_string(&policy_jstr)?; + let policy_string = String::from(policy_jstring); + let policy = Policy::from_str(&policy_string)?; + let policy_effect = policy.effect().to_string(); + Ok(JValueGen::Object(env.new_string(&policy_effect)?.into())) + } +} + +#[jni_fn("com.cedarpolicy.model.policy.Policy")] +pub fn templateEffectJni<'a>(mut env: JNIEnv<'a>, _: JClass, policy_jstr: JString<'a>) -> jvalue { + match template_effect_jni_internal(&mut env, policy_jstr) { + Err(e) => jni_failed(&mut env, e.as_ref()), + Ok(effect) => effect.as_jni(), + } +} + +fn template_effect_jni_internal<'a>( + env: &mut JNIEnv<'a>, + policy_jstr: JString<'a>, +) -> Result> { + if policy_jstr.is_null() { + raise_npe(env) + } else { + let policy_jstring = env.get_string(&policy_jstr)?; + let policy_string = String::from(policy_jstring); + let policy = Template::from_str(&policy_string)?; + let policy_effect = policy.effect().to_string(); + Ok(JValueGen::Object(env.new_string(&policy_effect)?.into())) + } +} + #[jni_fn("com.cedarpolicy.model.policy.Policy")] pub fn fromJsonJni<'a>(mut env: JNIEnv<'a>, _: JClass, policy_json_jstr: JString<'a>) -> jvalue { match from_json_internal(&mut env, policy_json_jstr) {