Skip to content

Commit

Permalink
feat: adds REST API for policy evaluation plan (#4452)
Browse files Browse the repository at this point in the history
  • Loading branch information
wolf4ood authored Sep 5, 2024
1 parent f5fcd2e commit 70ba9c2
Show file tree
Hide file tree
Showing 36 changed files with 1,146 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ public Result<Void> validate(Policy policy) {

@Override
public PolicyEvaluationPlan createEvaluationPlan(String scope, Policy policy) {
var delimitedScope = scope + DELIMITER;
var planner = PolicyEvaluationPlanner.Builder.newInstance(delimitedScope).ruleValidator(ruleValidator);
var planner = PolicyEvaluationPlanner.Builder.newInstance(scope).ruleValidator(ruleValidator);

preValidators.forEach(planner::preValidators);
postValidators.forEach(planner::postValidators);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import java.util.stream.Collectors;

import static org.eclipse.edc.policy.engine.PolicyEngineImpl.scopeFilter;
import static org.eclipse.edc.policy.engine.spi.PolicyEngine.DELIMITER;

public class PolicyEvaluationPlanner implements Policy.Visitor<PolicyEvaluationPlan>, Rule.Visitor<RuleStep<? extends Rule>>, Constraint.Visitor<ConstraintStep> {

Expand All @@ -65,11 +66,13 @@ public class PolicyEvaluationPlanner implements Policy.Visitor<PolicyEvaluationP
private final List<DynamicAtomicConstraintFunctionEntry<Rule>> dynamicConstraintFunctions = new ArrayList<>();
private final List<RuleFunctionFunctionEntry<Rule>> ruleFunctions = new ArrayList<>();
private final String delimitedScope;
private final String scope;

private RuleValidator ruleValidator;

private PolicyEvaluationPlanner(String delimitedScope) {
this.delimitedScope = delimitedScope;
private PolicyEvaluationPlanner(String scope) {
this.scope = scope;
this.delimitedScope = scope + DELIMITER;
}

@Override
Expand All @@ -96,9 +99,18 @@ public AtomicConstraintStep visitAtomicConstraint(AtomicConstraint constraint) {
var currentRule = currentRule();
var leftValue = constraint.getLeftExpression().accept(s -> s.getValue().toString());
var function = getFunctions(leftValue, currentRule.getClass());
var isFiltered = !ruleValidator.isInScope(leftValue, delimitedScope) || function == null;

return new AtomicConstraintStep(constraint, isFiltered, currentRule, function);
var filteringReasons = new ArrayList<String>();

if (!ruleValidator.isInScope(leftValue, delimitedScope)) {
filteringReasons.add("leftOperand '%s' is not bound to scope '%s'".formatted(leftValue, scope));
}

if (function == null) {
filteringReasons.add("leftOperand '%s' is not bound to any function within scope '%s'".formatted(leftValue, scope));
}

return new AtomicConstraintStep(constraint, filteringReasons, currentRule, function);
}

@Override
Expand All @@ -115,7 +127,7 @@ public PolicyEvaluationPlan visitPolicy(Policy policy) {

policy.getObligations().stream().map(obligation -> obligation.accept(this))
.map(DutyStep.class::cast)
.forEach(builder::obligation);
.forEach(builder::duty);

policy.getProhibitions().stream().map(permission -> permission.accept(this))
.map(ProhibitionStep.class::cast)
Expand Down Expand Up @@ -169,7 +181,11 @@ private <R extends Rule> void visitRule(R rule, RuleStep.Builder builder) {

try {
ruleContext.push(rule);
builder.filtered(shouldIgnoreRule(rule));

if (rule.getAction() != null && !ruleValidator.isBounded(rule.getAction().getType())) {
builder.filtered(true);
builder.filteringReason("action '%s' is not bound to scope '%s'".formatted(rule.getAction().getType(), scope));
}
builder.rule(rule);

for (var functionEntry : ruleFunctions) {
Expand All @@ -192,10 +208,6 @@ private Rule currentRule() {
return ruleContext.peek();
}

private boolean shouldIgnoreRule(Rule rule) {
return rule.getAction() != null && !ruleValidator.isBounded(rule.getAction().getType());
}

private List<ConstraintStep> validateMultiplicityConstraint(MultiplicityConstraint multiplicityConstraint) {
return multiplicityConstraint.getConstraints()
.stream()
Expand Down Expand Up @@ -235,6 +247,11 @@ public boolean evaluate(Operator operator, Object rightValue, R rule, PolicyCont
public Result<Void> validate(Operator operator, Object rightValue, R rule) {
return inner.validate(leftOperand, operator, rightValue, rule);
}

@Override
public String name() {
return inner.name();
}
}

public static class Builder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
var prohibition = Prohibition.Builder.newInstance().constraint(constraint).action(action).build();

Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> permissionSteps = PolicyEvaluationPlan::getPermissionSteps;
Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> dutySteps = PolicyEvaluationPlan::getDutySteps;
Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> dutySteps = PolicyEvaluationPlan::getObligationSteps;
Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> prohibitionSteps = PolicyEvaluationPlan::getProhibitionSteps;

var permission = Permission.Builder.newInstance().constraint(constraint).action(action).build();
Expand Down Expand Up @@ -275,6 +275,7 @@ void shouldIgnorePermissionStep_whenActionNotBound() {
.first()
.satisfies(permissionStep -> {
assertThat(permissionStep.isFiltered()).isTrue();
assertThat(permissionStep.getFilteringReasons()).hasSize(1);
assertThat(permissionStep.getConstraintSteps()).hasSize(1)
.first()
.isInstanceOfSatisfying(AtomicConstraintStep.class, constraintStep -> {
Expand Down Expand Up @@ -388,7 +389,7 @@ void shouldEvaluate_withMultiplicityConstraint(Policy policy, Class<Rule> ruleCl
assertThat(ruleStep.getConstraintSteps()).hasSize(1)
.first()
.isInstanceOfSatisfying(MultiplicityConstraintStep.class, constraintStep -> {
assertThat(constraintStep.getSteps()).hasSize(2);
assertThat(constraintStep.getConstraintSteps()).hasSize(2);
assertThat(constraintStep.getConstraint()).isNotNull();
});
}));
Expand All @@ -414,7 +415,7 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
var duty = Duty.Builder.newInstance().constraint(xoneConstraint).build();

Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> permissionSteps = PolicyEvaluationPlan::getPermissionSteps;
Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> dutySteps = PolicyEvaluationPlan::getDutySteps;
Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> dutySteps = PolicyEvaluationPlan::getObligationSteps;
Function<PolicyEvaluationPlan, List<? extends RuleStep<? extends Rule>>> prohibitionSteps = PolicyEvaluationPlan::getProhibitionSteps;

return Stream.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.edc.connector.controlplane.services.query.QueryValidator;
import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService;
import org.eclipse.edc.policy.engine.spi.PolicyEngine;
import org.eclipse.edc.policy.engine.spi.plan.PolicyEvaluationPlan;
import org.eclipse.edc.policy.model.AndConstraint;
import org.eclipse.edc.policy.model.AtomicConstraint;
import org.eclipse.edc.policy.model.Constraint;
Expand All @@ -31,7 +32,6 @@
import org.eclipse.edc.policy.model.Policy;
import org.eclipse.edc.policy.model.XoneConstraint;
import org.eclipse.edc.spi.query.QuerySpec;
import org.eclipse.edc.spi.result.Result;
import org.eclipse.edc.spi.result.ServiceResult;
import org.eclipse.edc.transaction.spi.TransactionContext;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -124,8 +124,17 @@ public ServiceResult<PolicyDefinition> update(PolicyDefinition policyDefinition)
}

@Override
public Result<Void> validate(Policy policy) {
return policyEngine.validate(policy);
public ServiceResult<Void> validate(Policy policy) {
var validationResult = policyEngine.validate(policy);
if (validationResult.failed()) {
return ServiceResult.badRequest(validationResult.getFailureMessages());
}
return ServiceResult.success();
}

@Override
public ServiceResult<PolicyEvaluationPlan> createEvaluationPlan(String scope, Policy policy) {
return ServiceResult.success(policyEngine.createEvaluationPlan(scope, policy));
}

private List<PolicyDefinition> queryPolicyDefinitions(QuerySpec query) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"version": "3.1.0-alpha",
"urlPath": "/v3.1alpha",
"lastUpdated": "2024-08-30T10:17:00Z",
"lastUpdated": "2024-09-04T10:17:00Z",
"maturity": "alpha"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public abstract class BasePolicyDefinitionApiController {
protected final Monitor monitor;
protected final PolicyDefinitionService service;
protected final TypeTransformerRegistry transformerRegistry;
private final JsonObjectValidatorRegistry validatorRegistry;
protected final JsonObjectValidatorRegistry validatorRegistry;

public BasePolicyDefinitionApiController(Monitor monitor, TypeTransformerRegistry transformerRegistry,
PolicyDefinitionService service, JsonObjectValidatorRegistry validatorRegistry) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

import jakarta.json.Json;
import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectFromPolicyDefinitionTransformer;
import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectFromPolicyEvaluationPlanTransformer;
import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectFromPolicyValidationResultTransformer;
import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectToPolicyDefinitionTransformer;
import org.eclipse.edc.connector.controlplane.api.management.policy.transform.JsonObjectToPolicyEvaluationPlanRequestTransformer;
import org.eclipse.edc.connector.controlplane.api.management.policy.v2.PolicyDefinitionApiV2Controller;
import org.eclipse.edc.connector.controlplane.api.management.policy.v3.PolicyDefinitionApiV3Controller;
import org.eclipse.edc.connector.controlplane.api.management.policy.v31alpha.PolicyDefinitionApiV31AlphaController;
import org.eclipse.edc.connector.controlplane.api.management.policy.validation.PolicyDefinitionValidator;
import org.eclipse.edc.connector.controlplane.api.management.policy.validation.PolicyEvaluationPlanRequestValidator;
import org.eclipse.edc.connector.controlplane.services.spi.policydefinition.PolicyDefinitionService;
import org.eclipse.edc.runtime.metamodel.annotation.Extension;
import org.eclipse.edc.runtime.metamodel.annotation.Inject;
Expand All @@ -35,6 +38,7 @@

import java.util.Map;

import static org.eclipse.edc.connector.controlplane.api.management.policy.model.PolicyEvaluationPlanRequest.EDC_POLICY_EVALUATION_PLAN_REQUEST_TYPE;
import static org.eclipse.edc.connector.controlplane.policy.spi.PolicyDefinition.EDC_POLICY_DEFINITION_TYPE;
import static org.eclipse.edc.spi.constants.CoreConstants.JSON_LD;

Expand Down Expand Up @@ -69,11 +73,14 @@ public void initialize(ServiceExtensionContext context) {
var jsonBuilderFactory = Json.createBuilderFactory(Map.of());
var managementApiTransformerRegistry = transformerRegistry.forContext("management-api");
var mapper = typeManager.getMapper(JSON_LD);
managementApiTransformerRegistry.register(new JsonObjectToPolicyEvaluationPlanRequestTransformer());
managementApiTransformerRegistry.register(new JsonObjectToPolicyDefinitionTransformer());
managementApiTransformerRegistry.register(new JsonObjectFromPolicyDefinitionTransformer(jsonBuilderFactory, mapper));
managementApiTransformerRegistry.register(new JsonObjectFromPolicyValidationResultTransformer(jsonBuilderFactory));
managementApiTransformerRegistry.register(new JsonObjectFromPolicyEvaluationPlanTransformer(jsonBuilderFactory));

validatorRegistry.register(EDC_POLICY_DEFINITION_TYPE, PolicyDefinitionValidator.instance());
validatorRegistry.register(EDC_POLICY_EVALUATION_PLAN_REQUEST_TYPE, PolicyEvaluationPlanRequestValidator.instance());

var monitor = context.getMonitor();
webService.registerResource(ApiContext.MANAGEMENT, new PolicyDefinitionApiV2Controller(monitor, managementApiTransformerRegistry, service, validatorRegistry));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.connector.controlplane.api.management.policy.model;

import static org.eclipse.edc.spi.constants.CoreConstants.EDC_NAMESPACE;

public record PolicyEvaluationPlanRequest(String policyScope) {
public static final String EDC_POLICY_EVALUATION_PLAN_REQUEST_TYPE = EDC_NAMESPACE + "PolicyEvaluationPlanRequest";
public static final String EDC_POLICY_EVALUATION_PLAN_REQUEST_POLICY_SCOPE = EDC_NAMESPACE + "policyScope";
}
Loading

0 comments on commit 70ba9c2

Please sign in to comment.