Skip to content

Commit

Permalink
feat: introduces PolicyEngineValidator for pre- or post-validators (#…
Browse files Browse the repository at this point in the history
…4463)

* feat: introduces PolicyEngineValidator for pre- or post-validators

* pr remarks
  • Loading branch information
wolf4ood authored Sep 12, 2024
1 parent f8ef6ae commit 85b5d3b
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.edc.policy.engine.spi.DynamicAtomicConstraintFunction;
import org.eclipse.edc.policy.engine.spi.PolicyContext;
import org.eclipse.edc.policy.engine.spi.PolicyEngine;
import org.eclipse.edc.policy.engine.spi.PolicyValidatorFunction;
import org.eclipse.edc.policy.engine.spi.RuleFunction;
import org.eclipse.edc.policy.engine.spi.plan.PolicyEvaluationPlan;
import org.eclipse.edc.policy.engine.validation.PolicyValidator;
Expand Down Expand Up @@ -57,8 +58,8 @@ public class PolicyEngineImpl implements PolicyEngine {
private final List<DynamicConstraintFunctionEntry<Rule>> dynamicConstraintFunctions = new ArrayList<>();

private final Map<String, List<RuleFunctionEntry<Rule>>> ruleFunctions = new TreeMap<>();
private final Map<String, List<BiFunction<Policy, PolicyContext, Boolean>>> preValidators = new HashMap<>();
private final Map<String, List<BiFunction<Policy, PolicyContext, Boolean>>> postValidators = new HashMap<>();
private final Map<String, List<PolicyValidatorFunction>> preValidators = new HashMap<>();
private final Map<String, List<PolicyValidatorFunction>> postValidators = new HashMap<>();
private final ScopeFilter scopeFilter;
private final RuleValidator ruleValidator;

Expand Down Expand Up @@ -196,16 +197,26 @@ public <R extends Rule> void registerFunction(String scope, Class<R> type, RuleF

@Override
public void registerPreValidator(String scope, BiFunction<Policy, PolicyContext, Boolean> validator) {
registerPreValidator(scope, new PolicyValidatorFunctionWrapper(validator));
}

@Override
public void registerPreValidator(String scope, PolicyValidatorFunction validator) {
preValidators.computeIfAbsent(scope + DELIMITER, k -> new ArrayList<>()).add(validator);
}

@Override
public void registerPostValidator(String scope, BiFunction<Policy, PolicyContext, Boolean> validator) {
registerPostValidator(scope, new PolicyValidatorFunctionWrapper(validator));
}

@Override
public void registerPostValidator(String scope, PolicyValidatorFunction validator) {
postValidators.computeIfAbsent(scope + DELIMITER, k -> new ArrayList<>()).add(validator);
}

@NotNull
private Result<Void> failValidator(String type, BiFunction<Policy, PolicyContext, Boolean> validator, PolicyContext context) {
private Result<Void> failValidator(String type, PolicyValidatorFunction validator, PolicyContext context) {
return failure(context.hasProblems() ? context.getProblems() : List.of(type + " failed: " + validator.getClass().getName()));
}

Expand Down Expand Up @@ -243,4 +254,18 @@ private static class RuleFunctionEntry<R extends Rule> {
}
}

private record PolicyValidatorFunctionWrapper(
BiFunction<Policy, PolicyContext, Boolean> function) implements PolicyValidatorFunction {

@Override
public Boolean apply(Policy policy, PolicyContext policyContext) {
return function.apply(policy, policyContext);
}

@Override
public String name() {
return function.getClass().getSimpleName();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction;
import org.eclipse.edc.policy.engine.spi.DynamicAtomicConstraintFunction;
import org.eclipse.edc.policy.engine.spi.PolicyContext;
import org.eclipse.edc.policy.engine.spi.PolicyValidatorFunction;
import org.eclipse.edc.policy.engine.spi.RuleFunction;
import org.eclipse.edc.policy.engine.spi.plan.PolicyEvaluationPlan;
import org.eclipse.edc.policy.engine.spi.plan.step.AndConstraintStep;
Expand Down Expand Up @@ -51,7 +52,6 @@
import java.util.Objects;
import java.util.Stack;
import java.util.TreeMap;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

import static org.eclipse.edc.policy.engine.PolicyEngineImpl.scopeFilter;
Expand All @@ -60,8 +60,8 @@
public class PolicyEvaluationPlanner implements Policy.Visitor<PolicyEvaluationPlan>, Rule.Visitor<RuleStep<? extends Rule>>, Constraint.Visitor<ConstraintStep> {

private final Stack<Rule> ruleContext = new Stack<>();
private final List<BiFunction<Policy, PolicyContext, Boolean>> preValidators = new ArrayList<>();
private final List<BiFunction<Policy, PolicyContext, Boolean>> postValidators = new ArrayList<>();
private final List<PolicyValidatorFunction> preValidators = new ArrayList<>();
private final List<PolicyValidatorFunction> postValidators = new ArrayList<>();
private final Map<String, List<ConstraintFunctionEntry<Rule>>> constraintFunctions = new TreeMap<>();
private final List<DynamicAtomicConstraintFunctionEntry<Rule>> dynamicConstraintFunctions = new ArrayList<>();
private final List<RuleFunctionFunctionEntry<Rule>> ruleFunctions = new ArrayList<>();
Expand Down Expand Up @@ -270,7 +270,7 @@ public Builder ruleValidator(RuleValidator ruleValidator) {
return this;
}

public Builder preValidator(String scope, BiFunction<Policy, PolicyContext, Boolean> validator) {
public Builder preValidator(String scope, PolicyValidatorFunction validator) {

if (scopeFilter(scope, planner.delimitedScope)) {
planner.preValidators.add(validator);
Expand All @@ -279,19 +279,19 @@ public Builder preValidator(String scope, BiFunction<Policy, PolicyContext, Bool
return this;
}

public Builder preValidators(String scope, List<BiFunction<Policy, PolicyContext, Boolean>> validators) {
public Builder preValidators(String scope, List<PolicyValidatorFunction> validators) {
validators.forEach(validator -> preValidator(scope, validator));
return this;
}

public Builder postValidator(String scope, BiFunction<Policy, PolicyContext, Boolean> validator) {
public Builder postValidator(String scope, PolicyValidatorFunction validator) {
if (scopeFilter(scope, planner.delimitedScope)) {
planner.postValidators.add(validator);
}
return this;
}

public Builder postValidators(String scope, List<BiFunction<Policy, PolicyContext, Boolean>> validators) {
public Builder postValidators(String scope, List<PolicyValidatorFunction> validators) {
validators.forEach(validator -> postValidator(scope, validator));
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
package org.eclipse.edc.policy.engine;

import org.eclipse.edc.policy.engine.spi.DynamicAtomicConstraintFunction;
import org.eclipse.edc.policy.engine.spi.PolicyContext;
import org.eclipse.edc.policy.engine.spi.PolicyEngine;
import org.eclipse.edc.policy.engine.spi.PolicyValidatorFunction;
import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry;
import org.eclipse.edc.policy.engine.spi.RuleFunction;
import org.eclipse.edc.policy.engine.spi.plan.PolicyEvaluationPlan;
import org.eclipse.edc.policy.engine.spi.plan.step.AtomicConstraintStep;
import org.eclipse.edc.policy.engine.spi.plan.step.MultiplicityConstraintStep;
import org.eclipse.edc.policy.engine.spi.plan.step.RuleStep;
import org.eclipse.edc.policy.engine.spi.plan.step.ValidatorStep;
import org.eclipse.edc.policy.engine.validation.RuleValidator;
import org.eclipse.edc.policy.model.Action;
import org.eclipse.edc.policy.model.AndConstraint;
Expand All @@ -44,6 +47,7 @@
import org.junit.jupiter.params.provider.ArgumentsSource;

import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;

Expand Down Expand Up @@ -441,17 +445,54 @@ void shouldEvaluate_withNoValidators() {
assertThat(plan.getPreValidators()).isEmpty();
}

@Test
void shouldEvaluate_withFunctionalValidators() {
var emptyPolicy = Policy.Builder.newInstance().build();

BiFunction<Policy, PolicyContext, Boolean> function = (policy, policyContext) -> true;
policyEngine.registerPreValidator(TEST_SCOPE, function);
policyEngine.registerPostValidator(TEST_SCOPE, function);

var plan = policyEngine.createEvaluationPlan(TEST_SCOPE, emptyPolicy);

assertThat(plan.getPreValidators()).hasSize(1)
.extracting(ValidatorStep::name)
.allMatch(s -> s.contains(PolicyEngineImplPlannerTest.class.getSimpleName()));

assertThat(plan.getPostValidators()).hasSize(1)
.extracting(ValidatorStep::name)
.allMatch(s -> s.contains(PolicyEngineImplPlannerTest.class.getSimpleName()));

}

@Test
void shouldEvaluate_withValidators() {
var emptyPolicy = Policy.Builder.newInstance().build();
policyEngine.registerPreValidator(TEST_SCOPE, (policy, policyContext) -> true);
policyEngine.registerPostValidator(TEST_SCOPE, (policy, policyContext) -> true);
policyEngine.registerPreValidator(TEST_SCOPE, new MyValidatorFunction());
policyEngine.registerPostValidator(TEST_SCOPE, new MyValidatorFunction());

var plan = policyEngine.createEvaluationPlan(TEST_SCOPE, emptyPolicy);

assertThat(plan.getPreValidators()).hasSize(1);
assertThat(plan.getPostValidators()).hasSize(1);
assertThat(plan.getPreValidators()).hasSize(1)
.extracting(ValidatorStep::name)
.contains("MyCustomValidator");
assertThat(plan.getPostValidators()).hasSize(1)
.extracting(ValidatorStep::name)
.contains("MyCustomValidator");

}

static class MyValidatorFunction implements PolicyValidatorFunction {

@Override
public Boolean apply(Policy policy, PolicyContext policyContext) {
return true;
}

@Override
public String name() {
return "MyCustomValidator";
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.eclipse.edc.policy.engine;

import org.eclipse.edc.policy.engine.spi.DynamicAtomicConstraintFunction;
import org.eclipse.edc.policy.engine.spi.PolicyContext;
import org.eclipse.edc.policy.engine.spi.PolicyContextImpl;
import org.eclipse.edc.policy.engine.spi.PolicyEngine;
import org.eclipse.edc.policy.engine.spi.RuleBindingRegistry;
Expand All @@ -38,6 +39,7 @@
import org.junit.jupiter.params.provider.ValueSource;

import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -213,6 +215,37 @@ void validateRuleFunctionOutOfScope() {
assertThat(policyEngine.evaluate("bar", policy, context).succeeded()).isTrue();
}

@Test
void validateAllScopesPreFunctionalValidator() {
bindingRegistry.bind("foo", ALL_SCOPES);

BiFunction<Policy, PolicyContext, Boolean> function = (policy, context) -> false;
policyEngine.registerPreValidator(ALL_SCOPES, function);

var policy = Policy.Builder.newInstance().build();
var context = PolicyContextImpl.Builder.newInstance().build();

var result = policyEngine.evaluate(TEST_SCOPE, policy, context);

assertThat(result).isFailed();
}

@Test
void validateAllScopesPostFunctionalValidator() {
bindingRegistry.bind("foo", ALL_SCOPES);

BiFunction<Policy, PolicyContext, Boolean> function = (policy, context) -> false;
policyEngine.registerPostValidator(ALL_SCOPES, function);

var policy = Policy.Builder.newInstance().build();
var context = PolicyContextImpl.Builder.newInstance().build();

var result = policyEngine.evaluate(TEST_SCOPE, policy, context);

assertThat(result).isFailed();
}


@ParameterizedTest
@ValueSource(booleans = { true, false })
void validateAllScopesPrePostValidator(boolean preValidation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.eclipse.edc.connector.controlplane.transfer.spi.types.TransferProcess;
import org.eclipse.edc.edr.spi.types.EndpointDataReferenceEntry;
import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction;
import org.eclipse.edc.policy.engine.spi.PolicyValidatorFunction;
import org.eclipse.edc.policy.engine.spi.plan.PolicyEvaluationPlan;
import org.eclipse.edc.policy.engine.spi.plan.step.AndConstraintStep;
import org.eclipse.edc.policy.engine.spi.plan.step.AtomicConstraintStep;
Expand Down Expand Up @@ -274,12 +275,15 @@ public static PolicyEvaluationPlan createPolicyEvaluationPlan() {
var duty = DutyStep.Builder.newInstance().constraint(xoneConstraintStep).rule(mock()).build();
var prohibition = ProhibitionStep.Builder.newInstance().constraint(andConstraintStep).rule(mock()).build();

var validatorFunction = mock(PolicyValidatorFunction.class);
when(validatorFunction.name()).thenReturn("FunctionName");

return PolicyEvaluationPlan.Builder.newInstance()
.postValidator(new ValidatorStep(mock()))
.postValidator(new ValidatorStep(validatorFunction))
.prohibition(prohibition)
.permission(permission)
.duty(duty)
.preValidator(new ValidatorStep(mock()))
.preValidator(new ValidatorStep(validatorFunction))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import jakarta.json.Json;
import jakarta.json.JsonObject;
import org.eclipse.edc.policy.engine.spi.AtomicConstraintFunction;
import org.eclipse.edc.policy.engine.spi.PolicyValidatorFunction;
import org.eclipse.edc.policy.engine.spi.RuleFunction;
import org.eclipse.edc.policy.engine.spi.plan.PolicyEvaluationPlan;
import org.eclipse.edc.policy.engine.spi.plan.step.AndConstraintStep;
Expand Down Expand Up @@ -73,7 +74,7 @@
import static org.mockito.Mockito.when;

public class JsonObjectFromPolicyEvaluationPlanTransformerTest {

private final JsonObjectFromPolicyEvaluationPlanTransformer transformer = new JsonObjectFromPolicyEvaluationPlanTransformer(Json.createBuilderFactory(emptyMap()));
private final TransformerContext context = mock(TransformerContext.class);

Expand Down Expand Up @@ -132,7 +133,10 @@ void transform_withPermissionStep() {
@Test
void transform_withValidators() {

var validator = new ValidatorStep(mock());
var validatorFunction = mock(PolicyValidatorFunction.class);
when(validatorFunction.name()).thenReturn("Name");

var validator = new ValidatorStep(validatorFunction);
var plan = PolicyEvaluationPlan.Builder.newInstance()
.preValidator(validator)
.postValidator(validator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,27 @@ public interface PolicyEngine {

/**
* Registers a function that performs pre-validation on the policy for the given scope.
*
* @deprecated Please use {@link PolicyEngine#registerPreValidator(String, PolicyValidatorFunction)}
*/
@Deprecated(since = "0.10.0")
void registerPreValidator(String scope, BiFunction<Policy, PolicyContext, Boolean> validator);

/**
* Registers a function that performs pre-validation on the policy for the given scope.
*/
void registerPreValidator(String scope, PolicyValidatorFunction validator);

/**
* Registers a function that performs post-validation on the policy for the given scope.
*
* @deprecated Please use {@link PolicyEngine#registerPostValidator(String, PolicyValidatorFunction)}
*/
@Deprecated(since = "0.10.0")
void registerPostValidator(String scope, BiFunction<Policy, PolicyContext, Boolean> validator);

/**
* Registers a function that performs post-validation on the policy for the given scope.
*/
void registerPostValidator(String scope, PolicyValidatorFunction validator);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.policy.engine.spi;

import org.eclipse.edc.policy.model.Policy;

import java.util.function.BiFunction;

/**
* A {@link Policy} validator that can be registered in the {@link PolicyEngine} in pre- or post-evaluation phase.
*/
@FunctionalInterface
public interface PolicyValidatorFunction extends BiFunction<Policy, PolicyContext, Boolean> {

/**
* Returns the name of the {@link PolicyValidatorFunction}
*/
default String name() {
return getClass().getSimpleName();
}
}
Loading

0 comments on commit 85b5d3b

Please sign in to comment.