diff --git a/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/PolicyEngineImpl.java b/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/PolicyEngineImpl.java index 25b66e685a4..ef84f6fa4de 100644 --- a/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/PolicyEngineImpl.java +++ b/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/PolicyEngineImpl.java @@ -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; @@ -57,8 +58,8 @@ public class PolicyEngineImpl implements PolicyEngine { private final List> dynamicConstraintFunctions = new ArrayList<>(); private final Map>> ruleFunctions = new TreeMap<>(); - private final Map>> preValidators = new HashMap<>(); - private final Map>> postValidators = new HashMap<>(); + private final Map> preValidators = new HashMap<>(); + private final Map> postValidators = new HashMap<>(); private final ScopeFilter scopeFilter; private final RuleValidator ruleValidator; @@ -196,16 +197,26 @@ public void registerFunction(String scope, Class type, RuleF @Override public void registerPreValidator(String scope, BiFunction 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 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 failValidator(String type, BiFunction validator, PolicyContext context) { + private Result failValidator(String type, PolicyValidatorFunction validator, PolicyContext context) { return failure(context.hasProblems() ? context.getProblems() : List.of(type + " failed: " + validator.getClass().getName())); } @@ -243,4 +254,18 @@ private static class RuleFunctionEntry { } } + private record PolicyValidatorFunctionWrapper( + BiFunction function) implements PolicyValidatorFunction { + + @Override + public Boolean apply(Policy policy, PolicyContext policyContext) { + return function.apply(policy, policyContext); + } + + @Override + public String name() { + return function.getClass().getSimpleName(); + } + } + } diff --git a/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/plan/PolicyEvaluationPlanner.java b/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/plan/PolicyEvaluationPlanner.java index dcab638363a..c0f9c3626c6 100644 --- a/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/plan/PolicyEvaluationPlanner.java +++ b/core/common/lib/policy-engine-lib/src/main/java/org/eclipse/edc/policy/engine/plan/PolicyEvaluationPlanner.java @@ -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; @@ -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; @@ -60,8 +60,8 @@ public class PolicyEvaluationPlanner implements Policy.Visitor, Rule.Visitor>, Constraint.Visitor { private final Stack ruleContext = new Stack<>(); - private final List> preValidators = new ArrayList<>(); - private final List> postValidators = new ArrayList<>(); + private final List preValidators = new ArrayList<>(); + private final List postValidators = new ArrayList<>(); private final Map>> constraintFunctions = new TreeMap<>(); private final List> dynamicConstraintFunctions = new ArrayList<>(); private final List> ruleFunctions = new ArrayList<>(); @@ -270,7 +270,7 @@ public Builder ruleValidator(RuleValidator ruleValidator) { return this; } - public Builder preValidator(String scope, BiFunction validator) { + public Builder preValidator(String scope, PolicyValidatorFunction validator) { if (scopeFilter(scope, planner.delimitedScope)) { planner.preValidators.add(validator); @@ -279,19 +279,19 @@ public Builder preValidator(String scope, BiFunction> validators) { + public Builder preValidators(String scope, List validators) { validators.forEach(validator -> preValidator(scope, validator)); return this; } - public Builder postValidator(String scope, BiFunction 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> validators) { + public Builder postValidators(String scope, List validators) { validators.forEach(validator -> postValidator(scope, validator)); return this; } diff --git a/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplPlannerTest.java b/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplPlannerTest.java index 61b650a05f1..2ba0f92fd4f 100644 --- a/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplPlannerTest.java +++ b/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplPlannerTest.java @@ -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; @@ -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; @@ -441,17 +445,54 @@ void shouldEvaluate_withNoValidators() { assertThat(plan.getPreValidators()).isEmpty(); } + @Test + void shouldEvaluate_withFunctionalValidators() { + var emptyPolicy = Policy.Builder.newInstance().build(); + + BiFunction 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"; + } } } diff --git a/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplTest.java b/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplTest.java index a4ba66348f0..cd1c56fcffa 100644 --- a/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplTest.java +++ b/core/common/lib/policy-engine-lib/src/test/java/org/eclipse/edc/policy/engine/PolicyEngineImplTest.java @@ -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; @@ -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; @@ -213,6 +215,37 @@ void validateRuleFunctionOutOfScope() { assertThat(policyEngine.evaluate("bar", policy, context).succeeded()).isTrue(); } + @Test + void validateAllScopesPreFunctionalValidator() { + bindingRegistry.bind("foo", ALL_SCOPES); + + BiFunction 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 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) { diff --git a/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java index 68db53abdc8..651a5ce5aa0 100644 --- a/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java +++ b/extensions/common/api/management-api-json-ld-context/src/test/java/org/eclipse/edc/connector/api/management/jsonld/serde/TestFunctions.java @@ -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; @@ -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(); } diff --git a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyEvaluationPlanTransformerTest.java b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyEvaluationPlanTransformerTest.java index d3c93af46ba..a78d590eb2b 100644 --- a/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyEvaluationPlanTransformerTest.java +++ b/extensions/control-plane/api/management-api/policy-definition-api/src/test/java/org/eclipse/edc/connector/controlplane/api/management/policy/transform/JsonObjectFromPolicyEvaluationPlanTransformerTest.java @@ -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; @@ -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); @@ -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) diff --git a/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/PolicyEngine.java b/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/PolicyEngine.java index 6864c3b8baf..94da840a5b3 100644 --- a/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/PolicyEngine.java +++ b/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/PolicyEngine.java @@ -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 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 validator); + + /** + * Registers a function that performs post-validation on the policy for the given scope. + */ + void registerPostValidator(String scope, PolicyValidatorFunction validator); } diff --git a/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/PolicyValidatorFunction.java b/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/PolicyValidatorFunction.java new file mode 100644 index 00000000000..865ee2797e0 --- /dev/null +++ b/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/PolicyValidatorFunction.java @@ -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 { + + /** + * Returns the name of the {@link PolicyValidatorFunction} + */ + default String name() { + return getClass().getSimpleName(); + } +} diff --git a/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/plan/step/ValidatorStep.java b/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/plan/step/ValidatorStep.java index 1f1210bfd04..229faadf018 100644 --- a/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/plan/step/ValidatorStep.java +++ b/spi/common/policy-engine-spi/src/main/java/org/eclipse/edc/policy/engine/spi/plan/step/ValidatorStep.java @@ -14,20 +14,17 @@ package org.eclipse.edc.policy.engine.spi.plan.step; -import org.eclipse.edc.policy.engine.spi.PolicyContext; -import org.eclipse.edc.policy.model.Policy; - -import java.util.function.BiFunction; +import org.eclipse.edc.policy.engine.spi.PolicyValidatorFunction; /** * An evaluation step for pre- and post-validators invoked during the evaluation process. */ -public record ValidatorStep(BiFunction validator) { +public record ValidatorStep(PolicyValidatorFunction validator) { /** * Returns the name of the validator */ public String name() { - return validator.getClass().getSimpleName(); + return validator.name(); } }