Skip to content

Commit

Permalink
feat: policy, template, and schema parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Victor Moreno committed Dec 20, 2023
1 parent 9de7bd1 commit feb6974
Show file tree
Hide file tree
Showing 11 changed files with 447 additions and 60 deletions.
2 changes: 1 addition & 1 deletion CedarJava/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,4 @@ test {
showStandardStreams false
exceptionFormat 'full'
}
}
}
Empty file modified CedarJava/config.sh
100644 → 100755
Empty file.
16 changes: 16 additions & 0 deletions CedarJava/src/main/java/com/cedarpolicy/model/schema/Schema.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.cedarpolicy.model.schema;

import com.cedarpolicy.model.exception.InternalException;
import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -27,6 +28,10 @@
public final class Schema {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

static {
System.load(System.getenv("CEDAR_JAVA_FFI_LIB"));
}

// The schema after being parsed as a JSON object.
@JsonValue private final JsonNode schemaJson;

Expand Down Expand Up @@ -84,4 +89,15 @@ public int hashCode() {
public String toString() {
return "Schema(schemaJson=" + schemaJson + ")";
}

public static Schema parse(String schemaStr) throws IOException, InternalException {
var success = parseSchema(schemaStr).equals("Success");
if (success) {
return new Schema(schemaStr);
} else {
throw new IOException("Unable to parse schema");
}
}

private static native String parseSchema(String schemaStr) throws InternalException;
}
45 changes: 44 additions & 1 deletion CedarJava/src/main/java/com/cedarpolicy/model/slice/Policy.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,24 @@

package com.cedarpolicy.model.slice;

import com.cedarpolicy.model.exception.InternalException;
import com.cedarpolicy.value.EntityUID;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.util.concurrent.atomic.AtomicInteger;


/** Policies in the Cedar language. */
public class Policy {
private static final Logger LOG = LoggerFactory.getLogger(Policy.class);
private static final AtomicInteger idCounter = new AtomicInteger(0);
static {
System.load(System.getenv("CEDAR_JAVA_FFI_LIB"));
}

/** Policy string. */
public final String policySrc;
/** Policy ID. */
Expand All @@ -41,7 +55,7 @@ public Policy(
throw new NullPointerException("Failed to construct policy from null string");
}
if (policyID == null) {
throw new NullPointerException("Failed to construct policy with null ID");
policyID = "policy" + idCounter.addAndGet(1);
}
this.policySrc = policy;
this.policyID = policyID;
Expand All @@ -51,4 +65,33 @@ public Policy(
public String toString() {
return "// Policy ID: " + policyID + "\n" + policySrc;
}

public static Policy parseStaticPolicy(String policyStr) throws InternalException, NullPointerException {
var policyText = parsePolicyJni(policyStr);
return new Policy(policyText, null);
}

public static Policy parsePolicyTemplate(String templateStr) throws InternalException, NullPointerException {
var templateText = parsePolicyTemplateJni(templateStr);
return new Policy(templateText, null);
}

/**
* This method takes in a Policy and a list of Instantiations and calls Cedar JNI to ensure those slots
* can be used to instantiate the template. If the Template is validated ahead of time by using Policy.parsePolicyTemplate
* and the Instantiations are also ensured to be valid (for example, by validating their parts using EntityTypeName.parse
* and EntityIdentifier.parse), then this should only fail because the slots in the template don't match the instantiations
* (barring JNI failures).
* @param p Policy object constructed from a valid template. Best if built from Policy.parsePolicyTemplate
* @param principal EntityUid to put into the principal slot. Leave null if there's no principal slot
* @param resource EntityUid to put into the resource slot. Leave null if there's no resource slot
* @return
*/
public static boolean validateTemplateLinkedPolicy(Policy p, EntityUID principal, EntityUID resource) throws InternalException, NullPointerException {
return validateTemplateLinkedPolicyJni(p.policySrc, principal, resource);
}

private static native String parsePolicyJni(String policyStr) throws InternalException, NullPointerException;
private static native String parsePolicyTemplateJni(String policyTemplateStr) throws InternalException, NullPointerException;
private static native boolean validateTemplateLinkedPolicyJni(String templateText, EntityUID principal, EntityUID resource) throws InternalException, NullPointerException;
}
13 changes: 4 additions & 9 deletions CedarJava/src/test/java/com/cedarpolicy/AuthTests.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
package com.cedarpolicy;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Optional;
import java.util.HashSet;
import java.util.HashMap;

import org.junit.jupiter.api.Test;
import com.cedarpolicy.BasicAuthorizationEngine;
import com.cedarpolicy.model.AuthorizationRequest;
import com.cedarpolicy.model.exception.AuthException;
import com.cedarpolicy.model.slice.BasicSlice;
import com.cedarpolicy.model.slice.Policy;
import com.cedarpolicy.model.slice.Slice;
import com.cedarpolicy.value.EntityUID;
import com.cedarpolicy.value.EntityTypeName;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class AuthTests {

@Test
public void simple() {
var auth = new BasicAuthorizationEngine();
Expand All @@ -34,5 +30,4 @@ public void simple() {
}, "Should not throw AuthException");

}

}
102 changes: 102 additions & 0 deletions CedarJava/src/test/java/com/cedarpolicy/PolicyTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.cedarpolicy;

import com.cedarpolicy.model.exception.InternalException;
import com.cedarpolicy.model.slice.Policy;
import com.cedarpolicy.value.EntityUID;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class PolicyTests {
@Test
public void parseStaticPolicyTests() {
assertDoesNotThrow(() -> {
var policy1 = Policy.parseStaticPolicy("permit(principal, action, resource);");
var policy2 = Policy.parseStaticPolicy("permit(principal, action, resource) when { principal has x && principal.x == 5};");
assertNotEquals(policy1.policyID.equals(policy2.policyID), true);
});
assertThrows(InternalException.class, () -> {
Policy.parseStaticPolicy("permit();");
});
assertThrows(NullPointerException.class, () -> {
Policy.parseStaticPolicy(null);
});
}

@Test
public void parsePolicyTemplateTests() {
assertDoesNotThrow(() -> {
String tbody = "permit(principal == ?principal, action, resource in ?resource);";
var template = Policy.parsePolicyTemplate(tbody);
assertTrue(template.policySrc.equals(tbody));
});
assertThrows(InternalException.class, () -> {
Policy.parsePolicyTemplate("permit(principal in ?resource, action, resource);");
});
}

@Test
public void validateTemplateLinkedPolicySuccessTest() {
Policy p = new Policy("permit(principal == ?principal, action, resource in ?resource);", null);
EntityUID principal1 = EntityUID.parse("Library::User::\"Victor\"").get();
EntityUID resource1 = EntityUID.parse("Library::Book::\"The black Swan\"").get();

Policy p2 = new Policy("permit(principal, action, resource in ?resource);", null);
EntityUID resource2 = EntityUID.parse("Library::Book::\"Thinking Fast and Slow\"").get();

Policy p3 = new Policy("permit(principal == ?principal, action, resource);", null);

Policy p4 = new Policy("permit(principal, action, resource);", null);

assertDoesNotThrow(() -> {
assertTrue(Policy.validateTemplateLinkedPolicy(p, principal1, resource1));
assertTrue(Policy.validateTemplateLinkedPolicy(p2, null, resource2));
assertTrue(Policy.validateTemplateLinkedPolicy(p3, principal1, null));
assertTrue(Policy.validateTemplateLinkedPolicy(p4, null, null));
});
}
@Test
public void validateTemplateLinkedPolicyFailsWhenExpected() {
Policy p1 = new Policy("permit(principal, action, resource);", null);
EntityUID principal = EntityUID.parse("Library::User::\"Victor\"").get();
EntityUID resource = EntityUID.parse("Library::Book::\"Thinking Fast and Slow\"").get();

Policy p2 = new Policy("permit(principal, action, resource in ?resource);", null);


Policy p3 = new Policy("permit(principal == ?principal, action, resource);", null);

// fails if we fill either slot in a policy with no slots
assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p1, principal, null);
});
assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p1, null, resource);
});
assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p1, null, resource);
});
assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p1, principal, resource);
});


// fails if we fill both slots or the wrong slot in a policy with one slot
assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p2, principal, null);
});
assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p2, principal, resource);
});

assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p3, null, resource);
});
assertThrows(InternalException.class, () -> {
Policy.validateTemplateLinkedPolicy(p3, principal, resource);
});
}
}
20 changes: 20 additions & 0 deletions CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.cedarpolicy;

import com.cedarpolicy.model.schema.Schema;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class SchemaTests {
@Test
public void parseSchema() {
assertDoesNotThrow(() -> {
Schema.parse("{\"ns1\": {\"entityTypes\": {}, \"actions\": {}}}");
Schema.parse("{}");
});
assertThrows(Exception.class, () -> {
Schema.parse("{\"foo\": \"bar\"}");
});
}
}
2 changes: 1 addition & 1 deletion CedarJavaFFI/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version = "3.0.0"
[dependencies]
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
cedar-policy = { version = "3.0", path = "../cedar/cedar-policy" } # Need latest version from github
cedar-policy = { version = "3.0", path = "../../cedar/cedar-policy" } # Need latest version from github

# JNI Support
jni = "0.21.0"
Expand Down
Loading

0 comments on commit feb6974

Please sign in to comment.