Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,46 @@ public static PolicySet parsePolicies(String policiesString) throws InternalExce
return policySet;
}

/**
* Parse multiple policies and templates from a JSON file into a PolicySet.
*
* <p>The file is expected to contain the JSON (EST) representation of a policy set, e.g.
* <pre>{@code
* {
* "staticPolicies": { "policy0": { ... } },
* "templates": { "template0": { ... } },
* "templateLinks": [ ... ]
* }
* }</pre>
*
* @param filePath the path to the JSON file containing the policies
* @return a PolicySet containing the parsed policies
* @throws InternalException
* @throws IOException
* @throws NullPointerException
*/
public static PolicySet parsePoliciesJson(Path filePath) throws InternalException, IOException {
// Read the file contents into a String
String policiesJsonString = Files.readString(filePath);
return parsePoliciesJson(policiesJsonString);
}

/**
* Parse a JSON string containing multiple policies and templates into a PolicySet.
*
* <p>The string is expected to be the JSON (EST) representation of a policy set. See
* {@link #parsePoliciesJson(Path)} for the expected shape.
*
* @param policiesJsonString the JSON string containing the policies
* @return a PolicySet containing the parsed policies
* @throws InternalException
* @throws NullPointerException
*/
public static PolicySet parsePoliciesJson(String policiesJsonString) throws InternalException {
PolicySet policySet = parsePoliciesJsonJni(policiesJsonString);
return policySet;
}

// --- Caching support ---

private volatile String cacheId;
Expand Down Expand Up @@ -219,6 +259,8 @@ public void run() {
}

private static native PolicySet parsePoliciesJni(String policiesStr) throws InternalException, NullPointerException;
private static native PolicySet parsePoliciesJsonJni(String policiesJsonStr)
throws InternalException, NullPointerException;
private static native String policySetToJson(String policySetStr) throws InternalException, NullPointerException;
private static native void preparsePolicySetJni(String id, String policiesJson) throws InternalException;
private static native void removeCachedPolicySetJni(String id);
Expand Down
67 changes: 67 additions & 0 deletions CedarJava/src/test/java/com/cedarpolicy/PolicySetTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@
import com.cedarpolicy.model.exception.InternalException;
import com.cedarpolicy.model.policy.Policy;
import com.cedarpolicy.model.policy.PolicySet;
import com.cedarpolicy.model.policy.TemplateLink;
import com.cedarpolicy.value.EntityUID;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;

import static org.junit.jupiter.api.Assertions.assertThrows;
Expand Down Expand Up @@ -79,6 +82,70 @@ public void parseTemplatesTests() throws InternalException, IOException {
assertEquals(1, policySet.templates.size());
}

@Test
public void parsePoliciesJsonTests() throws InternalException, IOException {
PolicySet policySet = PolicySet.parsePoliciesJson(Path.of(TEST_RESOURCES_DIR + "policies.json"));
for (Policy p: policySet.policies) {
assertNotNull(p.policySrc);
}
// Make sure the policy IDs are unique as Policies are made
assertEquals(2, policySet.policies.stream().map(p -> p.policyID).distinct().count());
assertEquals(2, policySet.policies.size());
assertEquals(0, policySet.templates.size());
}

@Test
public void parsePoliciesJsonStringTests() throws InternalException {
String json = "{\"staticPolicies\":{\"policy0\":{\"effect\":\"permit\","
+ "\"principal\":{\"op\":\"All\"},\"action\":{\"op\":\"All\"},"
+ "\"resource\":{\"op\":\"All\"},\"conditions\":[]}},"
+ "\"templates\":{},\"templateLinks\":[]}";
PolicySet policySet = PolicySet.parsePoliciesJson(json);
for (Policy p: policySet.policies) {
assertNotNull(p.policySrc);
}
assertEquals(1, policySet.policies.size());
assertEquals(0, policySet.templates.size());
}

@Test
public void parseTemplatesJsonTests() throws InternalException, IOException {
PolicySet policySet = PolicySet.parsePoliciesJson(Path.of(TEST_RESOURCES_DIR + "template.json"));
for (Policy p: policySet.policies) {
assertNotNull(p.policySrc);
}
// The two static policies only; the template-linked policy is reported
// via templateLinks rather than as a static policy.
assertEquals(2, policySet.policies.size());

for (Policy p: policySet.templates) {
assertNotNull(p.policySrc);
}
assertEquals(1, policySet.templates.size());

// The template instantiation is preserved as a TemplateLink
assertEquals(1, policySet.templateLinks.size());
TemplateLink link = policySet.templateLinks.get(0);
assertEquals("Template #1", link.getTemplateId());
assertEquals("Link #1", link.getResultPolicyId());

Map<String, EntityUID> linkValues = link.getLinkValues();
assertEquals(2, linkValues.size());
assertEquals("User::\"Matt\"", linkValues.get("?principal").toString());
assertEquals("Album::\"Vacation\"", linkValues.get("?resource").toString());
}

@Test
public void parsePoliciesJsonExceptionTests() throws InternalException, IOException {
assertThrows(IOException.class, () -> {
PolicySet.parsePoliciesJson(Path.of("nonExistentFilePath.json"));
});
// Cedar policy text, not JSON, should fail to parse as JSON
assertThrows(InternalException.class, () -> {
PolicySet.parsePoliciesJson("permit(principal, action, resource);");
});
}

@Test
public void parsePoliciesExceptionTests() throws InternalException, IOException {
assertThrows(IOException.class, () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,8 @@ private static class JsonTest {
public String entities;

/**
* File name of the schema file. Path is relative to the integration tests root. Note: This
* field is currently unused by these tests. The tests should be updated to take advantage
* of it once there is a Java interface to the validator.
* File name of the schema file. Path is relative to the integration tests root. Used both
* to validate the policy set and to validate each request.
*/
public String schema;

Expand Down Expand Up @@ -216,6 +215,7 @@ private static class JsonEntity {
"tests/decimal/2.json",
"tests/example_use_cases/1a.json",
"tests/example_use_cases/2a.json",
"tests/example_use_cases/2a_json_policy.json",
"tests/example_use_cases/2a_json_schema.json",
"tests/example_use_cases/2b.json",
"tests/example_use_cases/2c.json",
Expand Down Expand Up @@ -308,17 +308,13 @@ private DynamicContainer loadJsonTests(String jsonFile) throws InternalException
schema)))));
}

/**
* Load the policy set file. Only the Cedar policy format is supported; there is not yet a Java
* interface for parsing a policy set from its JSON (EST) representation.
*/
/** Load the policy set file, in either the Cedar or JSON policy format. */
private PolicySet loadPolicySet(String policiesFile, JsonOrCedarFormat format)
throws InternalException, IOException {
if (format == JsonOrCedarFormat.Json) {
throw new UnsupportedOperationException(
"The JSON policy format is not supported by these tests yet: " + policiesFile);
}
return PolicySet.parsePolicies(resolveIntegrationTestPath(policiesFile));
final Path policiesPath = resolveIntegrationTestPath(policiesFile);
return format == JsonOrCedarFormat.Json
? PolicySet.parsePoliciesJson(policiesPath)
: PolicySet.parsePolicies(policiesPath);
}

/** Load the schema file, in either the Cedar or JSON schema format. */
Expand Down
37 changes: 37 additions & 0 deletions CedarJava/src/test/resources/policies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"staticPolicies": {
"Policy #1": {
"effect": "permit",
"principal": {
"op": "in",
"entity": { "type": "UserGroup", "id": "friends" }
},
"action": {
"op": "==",
"entity": { "type": "Action", "id": "view" }
},
"resource": {
"op": "==",
"entity": { "type": "Photo", "id": "Husky.jpg" }
},
"conditions": []
},
"Policy #2": {
"effect": "forbid",
"principal": {
"op": "==",
"entity": { "type": "User", "id": "Matt" }
},
"action": {
"op": "All"
},
"resource": {
"op": "==",
"entity": { "type": "Photo", "id": "Husky.jpg" }
},
"conditions": []
}
},
"templates": {},
"templateLinks": []
}
62 changes: 62 additions & 0 deletions CedarJava/src/test/resources/template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"staticPolicies": {
"Policy #1": {
"effect": "permit",
"principal": {
"op": "in",
"entity": { "type": "UserGroup", "id": "friends" }
},
"action": {
"op": "==",
"entity": { "type": "Action", "id": "view" }
},
"resource": {
"op": "==",
"entity": { "type": "Photo", "id": "Husky.jpg" }
},
"conditions": []
},
"Policy #2": {
"effect": "forbid",
"principal": {
"op": "==",
"entity": { "type": "User", "id": "Matt" }
},
"action": {
"op": "All"
},
"resource": {
"op": "==",
"entity": { "type": "Photo", "id": "Husky.jpg" }
},
"conditions": []
}
},
"templates": {
"Template #1": {
"effect": "permit",
"principal": {
"op": "==",
"slot": "?principal"
},
"action": {
"op": "All"
},
"resource": {
"op": "in",
"slot": "?resource"
},
"conditions": []
}
},
"templateLinks": [
{
"newId": "Link #1",
"templateId": "Template #1",
"values": {
"?principal": { "type": "User", "id": "Matt" },
"?resource": { "type": "Album", "id": "Vacation" }
}
}
]
}
Loading
Loading