Skip to content

Commit

Permalink
use custom serializers for Entity and Schema (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
khieta committed Jul 3, 2024
1 parent 59fb8ef commit de2f87a
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 57 deletions.
6 changes: 6 additions & 0 deletions CedarJava/src/main/java/com/cedarpolicy/CedarJson.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@

package com.cedarpolicy;

import com.cedarpolicy.model.entity.Entity;
import com.cedarpolicy.model.schema.Schema;
import com.cedarpolicy.model.slice.Slice;
import com.cedarpolicy.serializer.EntitySerializer;
import com.cedarpolicy.serializer.SchemaSerializer;
import com.cedarpolicy.serializer.SliceSerializer;
import com.cedarpolicy.serializer.ValueDeserializer;
import com.cedarpolicy.serializer.ValueSerializer;
Expand Down Expand Up @@ -50,6 +54,8 @@ private static ObjectMapper createObjectMapper() {
final ObjectMapper mapper = new ObjectMapper();

final SimpleModule module = new SimpleModule();
module.addSerializer(Entity.class, new EntitySerializer());
module.addSerializer(Schema.class, new SchemaSerializer());
module.addSerializer(Slice.class, new SliceSerializer());
module.addSerializer(Value.class, new ValueSerializer());
module.addDeserializer(Value.class, new ValueDeserializer());
Expand Down
14 changes: 14 additions & 0 deletions CedarJava/src/main/java/com/cedarpolicy/model/policy/Policy.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ public Policy(
this.policyID = policyID;
}

/**
* Get the policy ID.
*/
public String getID() {
return policyID;
}

/**
* Get the policy source.
*/
public String getSource() {
return policySrc;
}

@Override
public String toString() {
return "// Policy ID: " + policyID + "\n" + policySrc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.util.stream.Collectors;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -67,6 +68,24 @@ public PolicySet(Set<Policy> policies, Set<Policy> templates, List<TemplateLink>
this.templateLinks = templateLinks;
}

/**
* Get the static policies in the policy set.
*
* @return A map from policy id to `Policy` object
*/
public Map<String, String> getStaticPolicies() {
return policies.stream().collect(Collectors.toMap(Policy::getID, Policy::getSource));
}

/**
* Get the templates in the policy set.
*
* @return A map from policy id to `Policy` object
*/
public Map<String, String> getTemplates() {
return templates.stream().collect(Collectors.toMap(Policy::getID, Policy::getSource));
}

/**
* Parse multiple policies and templates from a file into a PolicySet.
* @param filePath the path to the file containing the policies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@

import com.cedarpolicy.loader.LibraryLoader;
import com.cedarpolicy.model.exception.InternalException;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.util.Optional;
Expand All @@ -36,13 +34,12 @@ public final class Schema {
}

/** Is this schema in the JSON or human format */
@JsonIgnore
public final JsonOrHuman type;

/** This will be present if and only if `type` is `Json`. */
@JsonProperty("json")
private final Optional<JsonNode> schemaJson;
public final Optional<JsonNode> schemaJson;

/** This will be present if and only if `type` is `Human`. */
@JsonProperty("human")
public final Optional<String> schemaText;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import com.cedarpolicy.model.policy.Policy;
import com.cedarpolicy.model.policy.TemplateLink;
import com.cedarpolicy.value.Value;
import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import java.util.*;
Expand All @@ -37,10 +36,8 @@ public class BasicSlice implements Slice {

private final Set<Entity> entities;

@JsonProperty("templatePolicies")
private final Map<String, String> templatePolicies;

@JsonProperty("templateInstantiations")
private final List<TemplateLink> templateLinks;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.cedarpolicy.serializer;

import com.cedarpolicy.model.entity.Entity;
import com.cedarpolicy.value.EntityUID;
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.stream.Collectors;

/** Serialize an entity. */
public class EntitySerializer extends JsonSerializer<Entity> {

/** Serialize an entity. */
@Override
public void serialize(
Entity entity, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField("uid", entity.getEUID().asJson());
jsonGenerator.writeObjectField("attrs", entity.attrs);
jsonGenerator.writeObjectField("parents",
entity.getParents().stream().map(EntityUID::asJson).collect(Collectors.toSet()));
jsonGenerator.writeEndObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,10 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.cedarpolicy.value.EntityUID;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/** Represent JSON format of Entity Unique Identifier. */
@JsonDeserialize
@JsonSerialize
public class JsonEUID {
/** euid (__entity is used as escape sequence in JSON). */
@JsonProperty("type")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.cedarpolicy.serializer;

import com.cedarpolicy.model.schema.Schema;
import com.cedarpolicy.model.schema.Schema.JsonOrHuman;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;

/** Serialize a schema. */
public class SchemaSerializer extends JsonSerializer<Schema> {

/** Serialize a schema. */
@Override
public void serialize(
Schema schema, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeStartObject();
if (schema.type == JsonOrHuman.Json) {
jsonGenerator.writeObjectField("json", schema.schemaJson.get());
} else {
jsonGenerator.writeStringField("human", schema.schemaText.get());
}
jsonGenerator.writeEndObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,11 @@

package com.cedarpolicy.serializer;

import com.cedarpolicy.model.entity.Entity;
import com.cedarpolicy.model.slice.Slice;
import com.cedarpolicy.value.Value;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/** Serialize a slice. Only used internally by CedarJson */
public class SliceSerializer extends JsonSerializer<Slice> {
Expand All @@ -39,36 +32,10 @@ public void serialize(
throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField("policies", slice.getPolicies());
jsonGenerator.writeObjectField(
"entities", convertEntitiesToJsonEntities(slice.getEntities()));
jsonGenerator.writeObjectField("entities", slice.getEntities());
jsonGenerator.writeObjectField("templates", slice.getTemplates());
jsonGenerator.writeObjectField(
"templateInstantiations", slice.getTemplateLinks());
jsonGenerator.writeEndObject();
}

private static class JsonEntity {
/** Entity uid for the entity. */
@SuppressFBWarnings public final JsonEUID uid;

/** Entity attributes, where the value string is a Cedar literal value. */
@SuppressFBWarnings public final Map<String, Value> attrs;

/** Set of direct parent entities of this entity. */
public final Set<JsonEUID> parents;

JsonEntity(Entity e) {
this.uid = e.getEUID().asJson();
this.attrs = e.attrs;
this.parents = e.getParents().stream().map(euid -> euid.asJson()).collect(Collectors.toSet());
}
}

Set<JsonEntity> convertEntitiesToJsonEntities(Set<Entity> entities) {
Set<JsonEntity> ret = new HashSet<JsonEntity>();
for (Entity entity : entities) {
ret.add(new JsonEntity(entity));
}
return ret;
}
}
10 changes: 5 additions & 5 deletions CedarJava/src/test/java/com/cedarpolicy/JSONTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public void testEntityUID() {
ObjectNode inner = JsonNodeFactory.instance.objectNode();
inner.put("id", "jakob");
inner.put("type", "silver");
n.put(ENTITY_ESCAPE_SEQ, inner);
n.replace(ENTITY_ESCAPE_SEQ, inner);
assertJSONEqual(n, uid);

String invalidNamespace = "Us,er::\"alice\"";
Expand All @@ -181,7 +181,7 @@ public void testEntityUID() {
inner = JsonNodeFactory.instance.objectNode();
inner.put("id", "ali\"ce");
inner.put("type", "User");
n.put(ENTITY_ESCAPE_SEQ, inner);
n.replace(ENTITY_ESCAPE_SEQ, inner);
assertJSONEqual(n, uid);

String weirdType = "a";
Expand All @@ -191,7 +191,7 @@ public void testEntityUID() {
inner = JsonNodeFactory.instance.objectNode();
inner.put("id", weirdId);
inner.put("type", weirdType);
n.put(ENTITY_ESCAPE_SEQ, inner);
n.replace(ENTITY_ESCAPE_SEQ, inner);
assertJSONEqual(n, uid);
}

Expand All @@ -200,7 +200,7 @@ private ObjectNode buildEuidObject(String type, String id) {
var inner = JsonNodeFactory.instance.objectNode();
inner.put("id", id);
inner.put("type", type);
n.put(ENTITY_ESCAPE_SEQ, inner);
n.replace(ENTITY_ESCAPE_SEQ, inner);
return n;
}

Expand All @@ -213,7 +213,7 @@ public void testLongEntityUID() {
ObjectNode inner = JsonNodeFactory.instance.objectNode();
inner.put("id", "donut");
inner.put("type", "long::john::silver");
n.put(ENTITY_ESCAPE_SEQ, inner);
n.replace(ENTITY_ESCAPE_SEQ, inner);
assertJSONEqual(n, uid);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import com.cedarpolicy.serializer.JsonEUID;
import com.cedarpolicy.value.Value;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import java.io.FileInputStream;
Expand Down Expand Up @@ -91,7 +90,6 @@ private Path resolveIntegrationTestPath(String path) {
* populated by Jackson when the test files are deserialized.
*/
@SuppressWarnings("visibilitymodifier")
@JsonDeserialize
private static class JsonTest {
/**
* File name of the file containing policies. Path is relative to the integration tests
Expand Down Expand Up @@ -123,7 +121,6 @@ private static class JsonTest {

/** Directly corresponds to the structure of a request in the JSON formatted tests files. */
@SuppressWarnings("visibilitymodifier")
@JsonDeserialize
private static class JsonRequest {
/** Textual description of the request. */
public String description;
Expand Down Expand Up @@ -159,7 +156,6 @@ private static class JsonRequest {
* String, rather than String to Values.
*/
@SuppressWarnings("visibilitymodifier")
@JsonDeserialize
private static class JsonEntity {
/** Entity uid for the entity. */
@SuppressFBWarnings(
Expand Down

0 comments on commit de2f87a

Please sign in to comment.