Skip to content

Commit

Permalink
fix: ODRL LogicConstraint validator (#4073)
Browse files Browse the repository at this point in the history
  • Loading branch information
ndr-brt authored Apr 2, 2024
1 parent 957bc4d commit a312620
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,37 @@
package org.eclipse.edc.connector.controlplane.api.management.policy.validation;

import jakarta.json.JsonObject;
import jakarta.json.JsonString;
import jakarta.json.JsonValue;
import org.eclipse.edc.validator.jsonobject.JsonLdPath;
import org.eclipse.edc.validator.jsonobject.JsonObjectValidator;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryObject;
import org.eclipse.edc.validator.jsonobject.validators.MandatoryValue;
import org.eclipse.edc.validator.jsonobject.validators.OptionalIdNotBlank;
import org.eclipse.edc.validator.jsonobject.validators.TypeIs;
import org.eclipse.edc.validator.spi.ValidationResult;
import org.eclipse.edc.validator.spi.Validator;
import org.eclipse.edc.validator.spi.Violation;

import java.util.Collection;
import java.util.Objects;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import static java.lang.String.format;
import static org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition.EDC_POLICY_DEFINITION_POLICY;
import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_ACTION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_AND_CONSTRAINT_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_AND_SEQUENCE_CONSTRAINT_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_CONSEQUENCE_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_CONSTRAINT_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_DUTY_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_LEFT_OPERAND_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_LOGICAL_CONSTRAINT_TYPE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_OBLIGATION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_OPERATOR_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_OR_CONSTRAINT_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_PERMISSION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_POLICY_TYPE_SET;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_PROHIBITION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_REMEDY_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_RIGHT_OPERAND_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_XONE_CONSTRAINT_ATTRIBUTE;
import static org.eclipse.edc.validator.spi.Violation.violation;

public class PolicyDefinitionValidator {
public static Validator<JsonObject> instance() {
Expand All @@ -65,7 +64,6 @@ public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder b
.verifyArrayItem(ODRL_OBLIGATION_ATTRIBUTE, DutyValidator::instance)
.verifyArrayItem(ODRL_PROHIBITION_ATTRIBUTE, ProhibitionValidator::instance);
}

}

private static class PermissionValidator {
Expand All @@ -74,7 +72,8 @@ public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder b
return builder
.verify(ActionValidator::new)
.verifyArrayItem(ODRL_DUTY_ATTRIBUTE, DutyValidator::instance)
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, ConstraintValidatorWrapper::instance);
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, b -> b
.verify(ConstraintValidator::new));
}

}
Expand All @@ -85,7 +84,8 @@ public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder b
return builder
.verify(ActionValidator::new)
.verifyArrayItem(ODRL_CONSEQUENCE_ATTRIBUTE, ConsequenceValidator::instance)
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, ConstraintValidatorWrapper::instance);
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, b -> b
.verify(ConstraintValidator::new));
}

}
Expand All @@ -96,7 +96,8 @@ public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder b
return builder
.verify(ActionValidator::new)
.verifyArrayItem(ODRL_REMEDY_ATTRIBUTE, DutyValidator::instance)
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, ConstraintValidatorWrapper::instance);
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, b -> b
.verify(ConstraintValidator::new));
}

}
Expand All @@ -106,9 +107,9 @@ public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder b

return builder
.verify(ActionValidator::new)
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, ConstraintValidatorWrapper::instance);
.verifyArrayItem(ODRL_CONSTRAINT_ATTRIBUTE, b -> b
.verify(ConstraintValidator::new));
}

}

private record ActionValidator(JsonLdPath path) implements Validator<JsonObject> {
Expand All @@ -117,49 +118,34 @@ public ValidationResult validate(JsonObject input) {
return Optional.of(input.containsKey(ODRL_ACTION_ATTRIBUTE))
.filter(it -> input.get(ODRL_ACTION_ATTRIBUTE) != null)
.map(it -> ValidationResult.success())
.orElse(ValidationResult.failure(Violation.violation(format("%s is mandatory but missing or null", path.append(ODRL_ACTION_ATTRIBUTE)), ODRL_ACTION_ATTRIBUTE)));
.orElse(ValidationResult.failure(violation(format("%s is mandatory but missing or null", path.append(ODRL_ACTION_ATTRIBUTE)), ODRL_ACTION_ATTRIBUTE)));
}

}

private static class ConstraintValidatorWrapper {
public static JsonObjectValidator.Builder instance(JsonObjectValidator.Builder builder) {
return builder
.verify(ConstraintValidator::new);
}
private record ConstraintValidator(JsonLdPath path) implements Validator<JsonObject> {

}
private static final List<String> LOGICAL_CONSTRAINTS = List.of(
ODRL_AND_CONSTRAINT_ATTRIBUTE,
ODRL_XONE_CONSTRAINT_ATTRIBUTE,
ODRL_OR_CONSTRAINT_ATTRIBUTE,
ODRL_AND_SEQUENCE_CONSTRAINT_ATTRIBUTE
);

private record ConstraintValidator(JsonLdPath path) implements Validator<JsonObject> {
@Override
public ValidationResult validate(JsonObject input) {
var types = Optional.of(input)
.map(it -> it.getJsonArray(TYPE))
.stream().flatMap(Collection::stream)
.filter(it -> it.getValueType() == JsonValue.ValueType.STRING)
.map(JsonString.class::cast)
.map(JsonString::getString)
.toList();

if (types.contains(ODRL_LOGICAL_CONSTRAINT_TYPE)) {
if (LOGICAL_CONSTRAINTS.stream().anyMatch(input::containsKey)) {
return ValidationResult.success();
}

var violations = Stream.of(ODRL_LEFT_OPERAND_ATTRIBUTE, ODRL_OPERATOR_ATTRIBUTE, ODRL_RIGHT_OPERAND_ATTRIBUTE)
.map(it -> {
var jsonValue = input.get(it);
if (jsonValue == null) {
return Violation.violation(format("%s is mandatory but missing or null", path.append(it)), it);
}
return null;
})
.filter(Objects::nonNull)
.toList();

if (violations.isEmpty()) {
return ValidationResult.success();
}
return ValidationResult.failure(violations);
return JsonObjectValidator.newValidator()
.verify(ODRL_LEFT_OPERAND_ATTRIBUTE, MandatoryObject::new)
.verifyObject(ODRL_LEFT_OPERAND_ATTRIBUTE, b -> b.verifyId(OptionalIdNotBlank::new))
.verify(ODRL_OPERATOR_ATTRIBUTE, MandatoryObject::new)
.verifyObject(ODRL_OPERATOR_ATTRIBUTE, b -> b.verifyId(OptionalIdNotBlank::new))
.verify(ODRL_RIGHT_OPERAND_ATTRIBUTE, MandatoryValue::new)
.build()
.validate(input);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_AND_CONSTRAINT_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_CONSTRAINT_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_LEFT_OPERAND_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_LOGICAL_CONSTRAINT_TYPE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_OPERATOR_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_PERMISSION_ATTRIBUTE;
import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.ODRL_POLICY_TYPE_SET;
Expand Down Expand Up @@ -111,7 +110,13 @@ void shouldFail_whenTypeIsInvalid() {

@Test
void shouldSucceed_whenPolicyWithPermissionIsValid() {
var policyDefinition = createValidDefinition();
var policy = createObjectBuilder()
.add(TYPE, policySet())
.add(ODRL_ACTION_ATTRIBUTE, createValidAction())
.add(ODRL_PERMISSION_ATTRIBUTE, createValidPermission());
var policyDefinition = createObjectBuilder()
.add(EDC_POLICY_DEFINITION_POLICY, createArrayBuilder().add(policy))
.build();

var result = validator.validate(policyDefinition);

Expand All @@ -122,11 +127,11 @@ void shouldSucceed_whenPolicyWithPermissionIsValid() {
void shouldFail_whenPermissionActionIsMissing() {
var permission = createArrayBuilder().add(createObjectBuilder()
.add(ODRL_CONSTRAINT_ATTRIBUTE, createValidConstraint("GroupNumber", "isPartOf", "allowedGroups")));
var policy = createArrayBuilder()
.add(createObjectBuilder().add(ODRL_PERMISSION_ATTRIBUTE, permission))
.add(createObjectBuilder().add(TYPE, createValidType()));
var policyDefinition = createObjectBuilder()
.add(EDC_POLICY_DEFINITION_POLICY, policy)
.add(EDC_POLICY_DEFINITION_POLICY, createArrayBuilder().add(createObjectBuilder()
.add(TYPE, policySet())
.add(ODRL_PERMISSION_ATTRIBUTE, permission))
)
.build();

var result = validator.validate(policyDefinition);
Expand All @@ -143,11 +148,11 @@ void shouldSucceed_whenPermissionConstraintIsMissing() {
var permission = createArrayBuilder().add(createObjectBuilder()
.add(ODRL_ACTION_ATTRIBUTE, createValidAction())
.add(ODRL_CONSTRAINT_ATTRIBUTE, constraint));
var policy = createArrayBuilder()
.add(createObjectBuilder().add(TYPE, createValidType()))
.add(createObjectBuilder().add(ODRL_PERMISSION_ATTRIBUTE, permission));
var policy = createObjectBuilder()
.add(TYPE, policySet())
.add(ODRL_PERMISSION_ATTRIBUTE, permission);
var policyDefinition = createObjectBuilder()
.add(EDC_POLICY_DEFINITION_POLICY, policy)
.add(EDC_POLICY_DEFINITION_POLICY, createArrayBuilder().add(policy))
.build();

var result = validator.validate(policyDefinition);
Expand All @@ -161,11 +166,11 @@ void shouldFail_whenConstraintOperatorIsMissing() {
var permission = createArrayBuilder().add(createObjectBuilder()
.add(ODRL_ACTION_ATTRIBUTE, createValidAction())
.add(ODRL_CONSTRAINT_ATTRIBUTE, constraint));
var policy = createArrayBuilder()
.add(createObjectBuilder().add(ODRL_PERMISSION_ATTRIBUTE, permission))
.add(createObjectBuilder().add(TYPE, createValidType()));
var policy = createObjectBuilder()
.add(TYPE, policySet())
.add(ODRL_PERMISSION_ATTRIBUTE, permission);
var policyDefinition = createObjectBuilder()
.add(EDC_POLICY_DEFINITION_POLICY, policy)
.add(EDC_POLICY_DEFINITION_POLICY, createArrayBuilder().add(policy))
.build();

var result = validator.validate(policyDefinition);
Expand All @@ -180,32 +185,23 @@ void shouldFail_whenConstraintOperatorIsMissing() {
void shouldSucceed_whenLogicalConstraintIsPresent() {
var permission = createArrayBuilder().add(createObjectBuilder()
.add(ODRL_ACTION_ATTRIBUTE, createValidAction())
.add(ODRL_CONSTRAINT_ATTRIBUTE, createValidLogicalConstraint()));
var policy = createArrayBuilder()
.add(createObjectBuilder().add(TYPE, createValidType()))
.add(createObjectBuilder().add(ODRL_PERMISSION_ATTRIBUTE, permission));
.add(ODRL_CONSTRAINT_ATTRIBUTE, createArrayBuilder().add(createObjectBuilder()
.add(ODRL_AND_CONSTRAINT_ATTRIBUTE, createArrayBuilder()
.add(createValidConstraint("inForceDate", "gteq", "2024-01-01T00:00:00Z"))
.add(createValidConstraint("inForceDate", "lteq", "2024-04-01T00:00:00Z"))))));
var policy = createObjectBuilder()
.add(TYPE, policySet())
.add(ODRL_PERMISSION_ATTRIBUTE, permission);
var policyDefinition = createObjectBuilder()
.add(EDC_POLICY_DEFINITION_POLICY, policy)
.add(EDC_POLICY_DEFINITION_POLICY, createArrayBuilder().add(policy))
.build();

var result = validator.validate(policyDefinition);

assertThat(result).isSucceeded();
}

private JsonObject createValidDefinition() {
return createObjectBuilder()
.add(EDC_POLICY_DEFINITION_POLICY, createValidPolicy()).build();
}

private JsonArrayBuilder createValidPolicy() {
return createArrayBuilder().add(createObjectBuilder()
.add(ODRL_ACTION_ATTRIBUTE, createValidAction())
.add(TYPE, createValidType())
.add(ODRL_PERMISSION_ATTRIBUTE, createValidPermission()));
}

private JsonArrayBuilder createValidType() {
private JsonArrayBuilder policySet() {
return createArrayBuilder().add(ODRL_POLICY_TYPE_SET);
}

Expand All @@ -229,12 +225,4 @@ private JsonArrayBuilder createValidConstraint(String left, String operator, Str
.add(ODRL_RIGHT_OPERAND_ATTRIBUTE, rightOperand));
}

private JsonArrayBuilder createValidLogicalConstraint() {
return createArrayBuilder().add(createObjectBuilder()
.add(TYPE, createArrayBuilder().add(ODRL_LOGICAL_CONSTRAINT_TYPE))
.add(ODRL_AND_CONSTRAINT_ATTRIBUTE, createArrayBuilder()
.add(createValidConstraint("inForceDate", "gteq", "2024-01-01T00:00:00Z"))
.add(createValidConstraint("inForceDate", "lteq", "2024-04-01T00:00:00Z"))));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public interface PropertyAndTypeNames {
String ODRL_RIGHT_OPERAND_ATTRIBUTE = ODRL_SCHEMA + "rightOperand";
String ODRL_DUTY_ATTRIBUTE = ODRL_SCHEMA + "duty";
String ODRL_AND_CONSTRAINT_ATTRIBUTE = ODRL_SCHEMA + "and";
String ODRL_AND_SEQUENCE_CONSTRAINT_ATTRIBUTE = ODRL_SCHEMA + "andSequence";
String ODRL_OR_CONSTRAINT_ATTRIBUTE = ODRL_SCHEMA + "or";
String ODRL_XONE_CONSTRAINT_ATTRIBUTE = ODRL_SCHEMA + "xone";
String ODRL_USE_ACTION_ATTRIBUTE = ODRL_SCHEMA + "use";
Expand Down

0 comments on commit a312620

Please sign in to comment.