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
147 changes: 147 additions & 0 deletions CedarJava/src/test/java/com/cedarpolicy/EntityValidationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@
import com.cedarpolicy.model.schema.Schema;
import com.cedarpolicy.pbt.EntityGen;
import com.cedarpolicy.value.EntityTypeName;
import com.cedarpolicy.value.EntityUID;
import com.cedarpolicy.value.PrimBool;
import com.cedarpolicy.value.PrimString;

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

/**
* Tests for entity validator
*/
Expand Down Expand Up @@ -193,6 +197,147 @@ public void testEntityWithUnknownTagWithCedarSchema() throws AuthException {
"Expected to match regex but was: '%s'".formatted(errMsg));
}

/**
* Test that valid enum entities are accepted.
*/
@Test
public void testValidEnumEntities() throws AuthException {
// Create valid entities using enum types
EntityTypeName userType = EntityTypeName.parse("User").get();
EntityTypeName taskType = EntityTypeName.parse("Task").get();
EntityTypeName colorType = EntityTypeName.parse("Color").get();

Entity user = new Entity(userType.of("alice"), new HashMap<>() {
{
put("name", new PrimString("Alice"));
}
}, new HashSet<>());

Entity task = new Entity(taskType.of("task1"), new HashMap<>() {
{
put("owner", user.getEUID());
put("name", new PrimString("Complete project"));
put("status", new EntityUID(colorType, "Red"));
}
}, new HashSet<>());

EntityValidationRequest request = new EntityValidationRequest(ENUM_SCHEMA, List.of(user, task));
engine.validateEntities(request);
}

/**
* Test that enum entities with invalid enum values are rejected.
*/
@Test
public void testEnumEntitiesWithInvalidValues() throws AuthException {
EntityTypeName userType = EntityTypeName.parse("User").get();
EntityTypeName taskType = EntityTypeName.parse("Task").get();
EntityTypeName colorType = EntityTypeName.parse("Color").get();

Entity user = new Entity(userType.of("alice"), new HashMap<>() {
{
put("name", new PrimString("Alice"));
}
}, new HashSet<>());

// Create task with invalid enum value "Purple" (not in Color enum)
Entity task = new Entity(taskType.of("task1"), new HashMap<>() {
{
put("owner", user.getEUID());
put("name", new PrimString("Complete project"));
put("status", new EntityUID(colorType, "Purple")); // Invalid enum value
}
}, new HashSet<>());

EntityValidationRequest request = new EntityValidationRequest(ENUM_SCHEMA, List.of(user, task));

BadRequestException exception = assertThrows(BadRequestException.class, () -> engine.validateEntities(request));

String errMsg = exception.getErrors().get(0);
assertTrue(errMsg.contains("Purple") || errMsg.contains("Color"),
"Expected error about invalid enum value but was: '%s'".formatted(errMsg));
}

/**
* Test that enum entities cannot have attributes.
*/
@Test
public void testEnumEntitiesCannotHaveAttributes() throws AuthException {
EntityTypeName colorType = EntityTypeName.parse("Color").get();

// Try to create enum entity with attributes (should fail)
Entity enumEntity = new Entity(colorType.of("Red"), new HashMap<>() {
{
put("shade", new PrimString("Dark")); // Enum entities shouldn't have attributes
}
}, new HashSet<>());

EntityValidationRequest request = new EntityValidationRequest(ENUM_SCHEMA, List.of(enumEntity));

BadRequestException exception = assertThrows(BadRequestException.class, () -> engine.validateEntities(request));

String errMsg = exception.getErrors().get(0);
assertTrue(errMsg.contains("attribute") && (errMsg.contains("Color") || errMsg.contains("Red")),
"Expected error about enum entity having attributes but was: '%s'".formatted(errMsg));
}

/**
* Test that enum entities cannot have parents.
*/
@Test
public void testEnumEntitiesCannotHaveParents() throws AuthException {
EntityTypeName colorType = EntityTypeName.parse("Color").get();
EntityTypeName userType = EntityTypeName.parse("User").get();

Entity user = new Entity(userType.of("alice"), new HashMap<>() {
{
put("name", new PrimString("Alice"));
}
}, new HashSet<>());

// Try to create enum entity with parent (should fail)
Entity enumEntity = new Entity(colorType.of("Red"), new HashMap<>(), new HashSet<>() {
{
add(user.getEUID()); // Enum entities shouldn't have parents
}
});

EntityValidationRequest request = new EntityValidationRequest(ENUM_SCHEMA, List.of(user, enumEntity));

BadRequestException exception = assertThrows(BadRequestException.class, () -> engine.validateEntities(request));

String errMsg = exception.getErrors().get(0);
assertTrue(errMsg.contains("parent") || errMsg.contains("ancestor") || errMsg.contains("Color"),
"Expected error about enum entity having parents but was: '%s'".formatted(errMsg));
}

/**
* Test enum entity validation with Cedar schema format.
*/
@Test
public void testEnumEntitiesWithCedarSchema() throws AuthException {
EntityTypeName userType = EntityTypeName.parse("User").get();
EntityTypeName taskType = EntityTypeName.parse("Task").get();
EntityTypeName colorType = EntityTypeName.parse("Color").get();

Entity user = new Entity(userType.of("bob"), new HashMap<>() {
{
put("name", new PrimString("Bob"));
}
}, new HashSet<>());

Entity task = new Entity(taskType.of("task2"), new HashMap<>() {
{
put("owner", user.getEUID());
put("name", new PrimString("Review code"));
put("status", new EntityUID(colorType, "Green"));
}
}, new HashSet<>());

EntityValidationRequest cedarRequest = new EntityValidationRequest(ENUM_SCHEMA_CEDAR, List.of(user, task));
engine.validateEntities(cedarRequest);
}

@BeforeAll
public static void setUp() {

Expand All @@ -204,4 +349,6 @@ public static void setUp() {

private static final Schema ROLE_SCHEMA = loadSchemaResource("/role_schema.json");
private static final Schema ROLE_SCHEMA_CEDAR = loadCedarSchemaResource("/role_schema.cedarschema");
private static final Schema ENUM_SCHEMA = loadSchemaResource("/enum_schema.json");
private static final Schema ENUM_SCHEMA_CEDAR = loadCedarSchemaResource("/enum_schema.cedarschema");
}
86 changes: 86 additions & 0 deletions CedarJava/src/test/java/com/cedarpolicy/SchemaTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import java.util.Optional;

import static com.cedarpolicy.TestUtil.loadSchemaResource;
import static com.cedarpolicy.TestUtil.loadCedarSchemaResource;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -235,4 +237,88 @@ void testMalformedSchema() {
assertThrows(InternalException.class, malformedSchema::toJsonFormat);
}
}

@Nested
@DisplayName("Enum Schema Tests")
class EnumSchemaTests {

@Test
void testParseJsonEnumSchema() {
assertDoesNotThrow(() -> {
Schema enumSchema = loadSchemaResource("/enum_schema.json");
assertNotNull(enumSchema, "Enum schema should not be null");
});
}

@Test
@DisplayName("Should parse Cedar schema with enum entities")
void testParseCedarEnumSchema() {
assertDoesNotThrow(() -> {
Schema enumSchema = loadCedarSchemaResource("/enum_schema.cedarschema");
assertNotNull(enumSchema, "Enum schema should not be null");
});
}

@Test
void testRejectEmptyEnums() {
// Test Cedar format empty enum
assertThrows(Exception.class, () -> {
Schema.parse(JsonOrCedar.Cedar, "entity Color enum [];");
});

// Test JSON format empty enum
assertThrows(Exception.class, () -> {
Schema.parse(JsonOrCedar.Json, """
{
"": {
"entityTypes": {
"Color": {
"enum": []
}
},
"actions": {}
}
}
""");
});
}

@Test
void testEnumSchemaFormatConversion() throws Exception {
// Test Cedar to JSON conversion
Schema cedarEnumSchema = Schema.parse(JsonOrCedar.Cedar, """
entity Color enum ["Red", "Blue", "Green"];
entity User;
action view appliesTo { principal: [User], resource: [User] };
""");

JsonNode jsonResult = cedarEnumSchema.toJsonFormat();
assertNotNull(jsonResult, "JSON conversion result should not be null");

// Test JSON to Cedar conversion
String jsonEnumSchema = """
{
"": {
"entityTypes": {
"Color": {
"enum": ["Red", "Blue", "Green"]
},
"User": {}
},
"actions": {
"view": {
"appliesTo": {
"principalTypes": ["User"],
"resourceTypes": ["User"]
}
}
}
}
}
""";
Schema jsonSchemaObj = Schema.parse(JsonOrCedar.Json, jsonEnumSchema);
String cedarResult = jsonSchemaObj.toCedarFormat();
assertNotNull(cedarResult, "Cedar conversion result should not be null");
}
}
}
22 changes: 22 additions & 0 deletions CedarJava/src/test/java/com/cedarpolicy/ValidationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,26 @@ public void validateLevelPolicyFailsWhenExpected() {
thenIsNotValid(levelResponse);
}

/** Test enum entity validation with valid enum values. */
@Test
public void givenEnumSchemaAndValidEnumUsageReturnsValid() {
givenSchema(ENUM_SCHEMA);
givenPolicy("policy0", "permit(" + " principal == User::\"alice\"," + " action == Action::\"UpdateTask\","
+ " resource == Task::\"task1\"" + ") when {" + " resource.status == Color::\"Red\"" + "};");
ValidationResponse response = whenValidated();
thenIsValid(response);
}

/** Test enum entity validation with invalid enum values. */
@Test
public void givenEnumSchemaAndInvalidEnumValueReturnsInvalid() {
givenSchema(ENUM_SCHEMA);
givenPolicy("policy0", "permit(" + " principal == User::\"alice\"," + " action == Action::\"UpdateTask\","
+ " resource == Task::\"task1\"" + ") when {" + " resource.status != Color::\"Purple\"" + "};");
ValidationResponse response = whenValidated();
thenIsNotValid(response);
}

private void givenSchema(Schema testSchema) {
this.schema = testSchema;
}
Expand Down Expand Up @@ -285,4 +305,6 @@ private void reset() {
private static final Schema PHOTOFLASH_SCHEMA = loadSchemaResource("/photoflash_schema.json");
private static final Schema LIBRARY_SCHEMA = loadSchemaResource("/library_schema.json");
private static final Schema LEVEL_SCHEMA = loadSchemaResource("/level_schema.json");
private static final Schema ENUM_SCHEMA = loadSchemaResource("/enum_schema.json");

}
Loading