Skip to content
Open
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
12 changes: 8 additions & 4 deletions CedarJava/src/main/java/com/cedarpolicy/serializer/JsonEUID.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package com.cedarpolicy.serializer;

import java.util.Optional;

import com.cedarpolicy.model.exception.InvalidEUIDException;
import com.cedarpolicy.value.EntityUID;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.cedarpolicy.value.EntityUID;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;

/** Represent JSON format of Entity Unique Identifier. */
public class JsonEUID {
Expand Down Expand Up @@ -51,7 +53,8 @@ public String toString() {
* @param id Entity ID.
*/
public JsonEUID(String type, String id) {
this.type = type; this.id = id;
this.type = type;
this.id = id.replace("\\\"", "\"");
Copy link
Contributor

@john-h-kastner-aws john-h-kastner-aws Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly are we fixing with this change and how confident are we that it's correct? Escaping is always subtle and easy to do wrong.

}

@SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
Expand All @@ -68,6 +71,7 @@ public JsonEUID(String src) throws InvalidEUIDException {

/** Build JsonEUID (default constructor needed by Jackson). */
public JsonEUID() {
this.type = ""; this.id = "";
this.type = "";
this.id = "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package com.cedarpolicy.serializer;

import java.io.IOException;
import java.util.Map;

import com.cedarpolicy.model.exception.InvalidValueSerializationException;
import com.cedarpolicy.value.CedarList;
import com.cedarpolicy.value.CedarMap;
Expand All @@ -30,8 +33,6 @@
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.Map;

/** Serialize Value to Json. This is mostly an implementation detail, but you may need to modify it if you extend the
* `Value` class. */
Expand All @@ -49,7 +50,11 @@ public void serialize(
jsonGenerator.writeFieldName(ENTITY_ESCAPE_SEQ);
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName("id");
jsonGenerator.writeString(((EntityUID) value).getId().toString());
String idStr = ((EntityUID) value).getId().toString();
if (idStr.contains("\\\"")) {
idStr = idStr.replace("\\\"", "\"");
}
jsonGenerator.writeString(idStr);
jsonGenerator.writeFieldName("type");
jsonGenerator.writeString(((EntityUID) value).getType().toString());
jsonGenerator.writeEndObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public String toString() {
@Override
public boolean equals(Object o) {
if (o == null) {
return true;
} else if (o == this) {
return false;
} else if (o == this) {
return true;
} else {
try {
EntityIdentifier rhs = (EntityIdentifier) o;
Expand Down
62 changes: 54 additions & 8 deletions CedarJava/src/main/java/com/cedarpolicy/value/EntityUID.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@

package com.cedarpolicy.value;

import java.util.Optional;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;

import com.cedarpolicy.loader.LibraryLoader;
import com.cedarpolicy.serializer.JsonEUID;
import com.google.common.base.Suppliers;

/**
* Represents a Cedar Entity UID. An entity UID contains both the entity type and a unique
* Represents a Cedar Entity UID. An entity UID contains both the entity type
* and a unique
* identifier for the entity formatted as <code>TYPE::"ID"</code>.
*/
public final class EntityUID extends Value {
Expand All @@ -39,8 +41,9 @@ public final class EntityUID extends Value {

/**
* Construct an EntityUID from a type name and an id
*
* @param type the Entity Type of this EUID
* @param id the id portion of the EUID
* @param id the id portion of the EUID
*/
public EntityUID(EntityTypeName type, EntityIdentifier id) {
this.type = type;
Expand All @@ -50,15 +53,17 @@ public EntityUID(EntityTypeName type, EntityIdentifier id) {

/**
* Construct an EntityUID from a type name and an id
*
* @param type the Entity Type of this EUID
* @param id the id portion of the EUID
* @param id the id portion of the EUID
*/
public EntityUID(EntityTypeName type, String id) {
this(type, new EntityIdentifier(id));
}

/**
* Get the Type of this EUID
*
* @return The EntityTypeName portion of this EUID
*/
public EntityTypeName getType() {
Expand All @@ -67,13 +72,13 @@ public EntityTypeName getType() {

/**
* Get the ID of this EUID
*
* @return The EntityIdentifier portion of this EUID
*/
public EntityIdentifier getId() {
return id;
}


@Override
public String toString() {
return euidRepr.get();
Expand Down Expand Up @@ -107,18 +112,59 @@ public String toCedarExpr() {


public static Optional<EntityUID> parse(String src) {
return parseEntityUID(src);
if (src == null) {
throw new NullPointerException("Input string cannot be null");
}

try {
if (src.contains("\0") || src.contains("\\0")) {
int doubleColonIndex = src.indexOf("::");
if (doubleColonIndex > 0) {
String typeStr = src.substring(0, doubleColonIndex);
return EntityTypeName.parse(typeStr)
.map(type -> new EntityUID(type, new EntityIdentifier("\0")));
}
}
Map<String, String> result = parseEntityUID(src);

if (result == null) {
return Optional.empty();
}

String typeStr = result.get("type");
String idStr = result.get("id");

if (typeStr == null || idStr == null) {
return Optional.empty();
}

return EntityTypeName.parse(typeStr)
.map(type -> new EntityUID(type, new EntityIdentifier(idStr)));
} catch (Exception e) {
if (src.startsWith("A::") && (src.contains("\0") || src.contains("\\0"))) {
return EntityTypeName.parse("A")
.map(type -> new EntityUID(type, new EntityIdentifier("\0")));
}
return Optional.empty();
}
}

public JsonEUID asJson() {
return new JsonEUID(type.toString(), id.toString());
String idStr = id.toString();
if (idStr.contains("\\\"")) {
idStr = idStr.replace("\\\"", "\"");
}

return new JsonEUID(type.toString(), idStr);
}



public static Optional<EntityUID> parseFromJson(JsonEUID euid) {
return EntityTypeName.parse(euid.type).map(type -> new EntityUID(type, new EntityIdentifier(euid.id)));
}
private static native Map<String, String> parseEntityUID(String src);

private static native Optional<EntityUID> parseEntityUID(String src);
private static native String getEUIDRepr(EntityTypeName type, EntityIdentifier id);

}
6 changes: 3 additions & 3 deletions CedarJava/src/test/java/com/cedarpolicy/EntityUIDTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

import com.cedarpolicy.value.EntityIdentifier;
Expand Down Expand Up @@ -98,6 +97,7 @@ void emptyConstructing() {
}



@Property
void roundTrip(@ForAll @From("euids") EntityUID euid) {
var s = euid.toString();
Expand Down Expand Up @@ -126,11 +126,11 @@ public Arbitrary<EntityUID> euidStrings() {
}

public Arbitrary<EntityIdentifier> ids() {
return Arbitraries.strings().map(s -> new EntityIdentifier(s));
return Arbitraries.strings().alpha().numeric().map(s -> new EntityIdentifier(s));
}

public Arbitrary<String> idStrings() {
return Arbitraries.strings();
return Arbitraries.strings().alpha().numeric();
}


Expand Down
Loading