Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduces PolicyEngineValidator for pre- or post-validators #4463

Merged
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
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
Loading