diff options
author | 2019-11-04 16:18:08 -0500 | |
---|---|---|
committer | 2019-11-12 18:48:04 -0500 | |
commit | fe3a8ec467beae89bca9d10b8b5b39f98c81ca01 (patch) | |
tree | 284c92033a8a504c46615ee26be6e7955938488f /applications/common/src/main/java/org | |
parent | 1bedb591cc68c10c7db916fda8a3f02d67c2314d (diff) |
Implement closest match algorithm
Needed to add more obligations, which make it easier to
scan through obligations and be able to pull information
about the policy.
Adding weight as an obligation for Optimization policies in
order to implement "closest match" algorithm.
Moved Obligation to a support class.
Added JUnit code coverage on the translator classes.
Split some methods up to reduce complexity via sonar.
Issue-ID: POLICY-2066
Change-Id: Ibb13d2dc0a63ab2a6d585b0697a0c1d129fa8f7b
Signed-off-by: Pamela Dragosh <pdragosh@research.att.com>
Diffstat (limited to 'applications/common/src/main/java/org')
5 files changed, 533 insertions, 98 deletions
diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapObligation.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapObligation.java new file mode 100644 index 00000000..da2e7f10 --- /dev/null +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/OnapObligation.java @@ -0,0 +1,259 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.pdp.xacml.application.common; + +import com.att.research.xacml.api.AttributeAssignment; +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.Obligation; +import com.google.gson.Gson; +import java.util.Map; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.ToString; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType; + +@Getter +@ToString +public class OnapObligation { + + @Getter(AccessLevel.NONE) + private static final ObjectFactory factory = new ObjectFactory(); + + @Getter(AccessLevel.NONE) + private static final Gson gson = new Gson(); + + private String policyId; + private String policyType; + private String policyContent; + private Integer weight; + + /** + * Constructor from an obligation. + * + * @param obligation Obligation object + */ + public OnapObligation(Obligation obligation) { + // + // Scan through the obligations for them + // + for (AttributeAssignment assignment : obligation.getAttributeAssignments()) { + scanAttribute(assignment); + } + } + + /** + * Constructor for just the policy details. + * + * @param policyId String + * @param policyContent String + */ + public OnapObligation(String policyId, String policyContent) { + this.policyId = policyId; + this.policyContent = policyContent; + } + + + /** + * Constructor for policy details, type and weight. + * + * @param policyId String + * @param policyContent String + * @param policyType String + * @param weight int + */ + public OnapObligation(String policyId, String policyContent, String policyType, Integer weight) { + this.policyId = policyId; + this.policyContent = policyContent; + this.policyType = policyType; + this.weight = weight; + } + + /** + * getPolicyContentAsMap returns the policy as a map for convience. + * + * @return {@code Map<String, Object>} + */ + @SuppressWarnings("unchecked") + public Map<String, Object> getPolicyContentAsMap() { + if (this.policyContent == null) { + return null; + } + return gson.fromJson(this.policyContent, Map.class); + } + + /** + * Generates default obligation using default Permit and Obligation Id. + * + * @return ObligationExpressionType object + */ + public ObligationExpressionType generateObligation() { + return this.generateObligation(EffectType.PERMIT, ToscaDictionary.ID_OBLIGATION_REST_BODY); + } + + /** + * generateObligation - generates an obligation object with the attributes that exist. Note: + * any null values will result in NO attribute assignment for that attribute. + * + * @param effectType EffectType object + * @param obligationId Id for the obligation + * @return ObligationExpressionType object + */ + public ObligationExpressionType generateObligation(EffectType effectType, Identifier obligationId) { + // + // Create an ObligationExpression + // + ObligationExpressionType obligation = new ObligationExpressionType(); + obligation.setFulfillOn(effectType); + obligation.setObligationId(obligationId.stringValue()); + // + // Update the obligation + // + updateObligation(obligation); + // + // Convenience return + // + return obligation; + } + + /** + * Updates an existing Obligation object with the attributes that exist. Note: any null values + * will result in NO attribute assignment for that attribute. + * + * @param obligation ObligationExpressionType object + * @return ObligationExpressionType object + */ + public ObligationExpressionType updateObligation(ObligationExpressionType obligation) { + // + // Add policy-id + // + addOptionalAttributeToObligation(obligation, ToscaDictionary.ID_OBLIGATION_POLICY_ID, + ToscaDictionary.ID_OBLIGATION_POLICY_ID_DATATYPE, + ToscaDictionary.ID_OBLIGATION_POLICY_ID_CATEGORY, + policyId); + // + // Add policy contents + // + addOptionalAttributeToObligation(obligation, ToscaDictionary.ID_OBLIGATION_POLICY_CONTENT, + ToscaDictionary.ID_OBLIGATION_POLICY_CONTENT_DATATYPE, + ToscaDictionary.ID_OBLIGATION_POLICY_CONTENT_CATEGORY, + policyContent); + // + // Add the weight + // + addOptionalAttributeToObligation(obligation, ToscaDictionary.ID_OBLIGATION_POLICY_WEIGHT, + ToscaDictionary.ID_OBLIGATION_POLICY_WEIGHT_DATATYPE, + ToscaDictionary.ID_OBLIGATION_POLICY_WEIGHT_CATEGORY, + weight); + // + // Add the policy type + // + addOptionalAttributeToObligation(obligation, ToscaDictionary.ID_OBLIGATION_POLICY_TYPE, + ToscaDictionary.ID_OBLIGATION_POLICY_TYPE_DATATYPE, + ToscaDictionary.ID_OBLIGATION_POLICY_TYPE_CATEGORY, + policyType); + // + // Return as a convenience + // + return obligation; + } + + /** + * scanAttribute - scans the assignment for a supported obligation assignment. Applications + * can override this class and provide their own custom attribute assignments if desired. + * + * @param assignment AttributeAssignment object + * @return true if found an ONAP supported attribute + */ + protected boolean scanAttribute(AttributeAssignment assignment) { + // + // Check for our supported attributes. Note: Cannot use a switch + // as Identifier isn't a constant. + // + if (ToscaDictionary.ID_OBLIGATION_POLICY_ID.equals(assignment.getAttributeId())) { + policyId = assignment.getAttributeValue().getValue().toString(); + return true; + } else if (ToscaDictionary.ID_OBLIGATION_POLICY_TYPE.equals(assignment.getAttributeId())) { + policyType = assignment.getAttributeValue().getValue().toString(); + return true; + } else if (ToscaDictionary.ID_OBLIGATION_POLICY_CONTENT.equals(assignment.getAttributeId())) { + policyContent = assignment.getAttributeValue().getValue().toString(); + return true; + } else if (ToscaDictionary.ID_OBLIGATION_POLICY_WEIGHT.equals(assignment.getAttributeId())) { + weight = Integer.decode(assignment.getAttributeValue().getValue().toString()); + return true; + } + // + // By returning true, we indicate this isn't an attribute + // supported in the this class. Targeted for applications + // that derive from this class in order to extend it. + // + return false; + } + + /** + * Creates the necessary objects to insert into the obligation, if the value object is not null. + * + * @param obligation Incoming Obligation + * @param id Attribute Id + * @param datatype Attribute's Data type + * @param category Attributes Category + * @param theValue Attribute value + * @return obligation Incoming obligation + */ + protected ObligationExpressionType addOptionalAttributeToObligation(ObligationExpressionType obligation, + Identifier id, Identifier datatype, Identifier category, Object theValue) { + // + // Simple check for null + // + if (theValue == null) { + return obligation; + } + // + // Create an AttributeValue for it + // + AttributeValueType value = new AttributeValueType(); + value.setDataType(datatype.stringValue()); + value.getContent().add(theValue.toString()); + // + // Create our AttributeAssignmentExpression where we will + // store the contents of the policy id. + // + AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType(); + expressionType.setAttributeId(id.stringValue()); + expressionType.setCategory(category.stringValue()); + expressionType.setExpression(factory.createAttributeValue(value)); + // + // Add it to the obligation + // + obligation.getAttributeAssignmentExpression().add(expressionType); + // + // Return as convenience + // + return obligation; + } + +} diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java index 500be2e6..1a899971 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/ToscaDictionary.java @@ -112,20 +112,41 @@ public final class ToscaDictionary { public static final Identifier ID_OBLIGATION_REST_BODY = new IdentifierImpl(ID_URN_ONAP, "rest:body"); - public static final Identifier ID_OBLIGATION_POLICY_MONITORING = - new IdentifierImpl(ID_URN_ONAP, ":obligation:monitoring"); + public static final Identifier ID_OBLIGATION_POLICY_CONTENT = + new IdentifierImpl(ID_URN_ONAP, ":obligation:policycontent"); - public static final Identifier ID_OBLIGATION_POLICY_MONITORING_CONTENTS = - new IdentifierImpl(ID_URN_ONAP, ":obligation:monitoring:contents"); + public static final Identifier ID_OBLIGATION_POLICY_CONTENT_CATEGORY = + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE; + + public static final Identifier ID_OBLIGATION_POLICY_CONTENT_DATATYPE = + XACML3.ID_DATATYPE_STRING; - public static final Identifier ID_OBLIGATION_POLICY_MONITORING_CATEGORY = + public static final Identifier ID_OBLIGATION_POLICY_ID = + new IdentifierImpl(ID_URN_ONAP, ":obligation:policyid"); + + public static final Identifier ID_OBLIGATION_POLICY_ID_CATEGORY = XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE; - public static final Identifier ID_OBLIGATION_POLICY_MONITORING_DATATYPE = + public static final Identifier ID_OBLIGATION_POLICY_ID_DATATYPE = XACML3.ID_DATATYPE_STRING; - public static final Identifier ID_OBLIGATION_MONITORING_ISSUER = - new IdentifierImpl(ID_URN_ONAP, "issuer:monitoring"); + public static final Identifier ID_OBLIGATION_POLICY_WEIGHT = + new IdentifierImpl(ID_URN_ONAP, ":obligation:weight"); + + public static final Identifier ID_OBLIGATION_POLICY_WEIGHT_CATEGORY = + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE; + + public static final Identifier ID_OBLIGATION_POLICY_WEIGHT_DATATYPE = + XACML3.ID_DATATYPE_INTEGER; + + public static final Identifier ID_OBLIGATION_POLICY_TYPE = + new IdentifierImpl(ID_URN_ONAP, ":obligation:policytype"); + + public static final Identifier ID_OBLIGATION_POLICY_TYPE_CATEGORY = + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE; + + public static final Identifier ID_OBLIGATION_POLICY_TYPE_DATATYPE = + XACML3.ID_DATATYPE_STRING; diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java index d2d383b9..508bc245 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdBaseTranslator.java @@ -22,24 +22,20 @@ package org.onap.policy.pdp.xacml.application.common.std; -import com.att.research.xacml.api.AttributeAssignment; import com.att.research.xacml.api.Decision; import com.att.research.xacml.api.Obligation; import com.att.research.xacml.api.Request; import com.att.research.xacml.api.Response; import com.att.research.xacml.api.Result; import com.att.research.xacml.api.XACML3; -import com.google.gson.Gson; import java.util.Collection; import java.util.HashMap; import java.util.Map; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType; -import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType; @@ -50,6 +46,7 @@ import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType; import org.onap.policy.models.decisions.concepts.DecisionRequest; import org.onap.policy.models.decisions.concepts.DecisionResponse; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.pdp.xacml.application.common.OnapObligation; import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator; @@ -58,16 +55,16 @@ import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class StdBaseTranslator implements ToscaPolicyTranslator { +public abstract class StdBaseTranslator implements ToscaPolicyTranslator { private static final Logger LOGGER = LoggerFactory.getLogger(StdBaseTranslator.class); - private static Gson gson = new Gson(); + private static final ObjectFactory factory = new ObjectFactory(); public static final String POLICY_ID = "policy-id"; public static final String POLICY_VERSION = "policy-version"; @Override public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException { - throw new ToscaPolicyConversionException("Please override converPolicy"); + throw new ToscaPolicyConversionException("Please override convertPolicy"); } @Override @@ -95,8 +92,7 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { // Go through obligations // scanObligations(xacmlResult.getObligations(), decisionResponse); - } else if (xacmlResult.getDecision() == Decision.DENY - || xacmlResult.getDecision() == Decision.INDETERMINATE) { + } else { // // TODO we have to return an ErrorResponse object instead // @@ -109,55 +105,13 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { /** * scanObligations - scans the list of obligations and make appropriate method calls to process - * obligations. + * obligations. This method must be overridden and be implemented for the specific application as + * obligations may have different expected attributes per application. * * @param obligations Collection of obligation objects * @param decisionResponse DecisionResponse object used to store any results from obligations. */ - protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) { - for (Obligation obligation : obligations) { - LOGGER.info("Obligation: {}", obligation); - for (AttributeAssignment assignment : obligation.getAttributeAssignments()) { - LOGGER.info("Attribute Assignment: {}", assignment); - processObligationAttribute(assignment, decisionResponse); - } - } - } - - /** - * processObligationAttribute - processes an individual obligation attribute assignment object. - * - * @param assignment AttributeAssignment object - * @param decisionResponse DecisionResponse object used to store any results from attribute assignment. - */ - @SuppressWarnings("unchecked") - protected void processObligationAttribute(AttributeAssignment assignment, DecisionResponse decisionResponse) { - // - // We care about the content attribute - // - if (ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS - .equals(assignment.getAttributeId())) { - // - // The contents are in Json form - // - Object stringContents = assignment.getAttributeValue().getValue(); - LOGGER.info("DCAE contents: {}{}", XacmlPolicyUtils.LINE_SEPARATOR, stringContents); - // - // Let's parse it into a map using Gson - // - Map<String, Object> result; - result = gson.fromJson(stringContents.toString(), Map.class); - // - // Find the metadata section - // - Map<String, Object> metadata = (Map<String, Object>) result.get("metadata"); - if (metadata != null) { - decisionResponse.getPolicies().put(metadata.get(POLICY_ID).toString(), result); - } else { - LOGGER.error("Missing metadata section in policy contained in obligation."); - } - } - } + protected abstract void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse); /** * From the TOSCA metadata section, pull in values that are needed into the XACML policy. @@ -196,36 +150,27 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { * But this is fine for now. * * @param <T> RuleType, PolicyType, PolicySetType object + * @Param policyId The policy-id * @param ruleOrPolicy Incoming RuleType, PolicyType, PolicySetType object * @param jsonPolicy JSON String representation of policy. + * @param weight Weighting for the policy (optional) * @return Return the Incoming RuleType, PolicyType, PolicySetType object for convenience. */ - protected <T> T addObligation(T ruleOrPolicy, String jsonPolicy) { + protected <T> T addObligation(T ruleOrPolicy, String policyId, String jsonPolicy, Integer weight, + String policyType) { // // Creating obligation for returning policy // - LOGGER.info("Obligation Policy {}{}", XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy); - // - // Create an AttributeValue for it - // - AttributeValueType value = new AttributeValueType(); - value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue()); - value.getContent().add(jsonPolicy); + LOGGER.info("Obligation Policy id: {} type: {} weight: {} policy:{}{}", policyId, policyType, weight, + XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy); // - // Create our AttributeAssignmentExpression where we will - // store the contents of the policy in JSON format. + // Create our OnapObligation // - AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType(); - expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue()); - ObjectFactory factory = new ObjectFactory(); - expressionType.setExpression(factory.createAttributeValue(value)); + OnapObligation onapObligation = new OnapObligation(policyId, jsonPolicy, policyType, weight); // - // Create an ObligationExpression for it + // Generate the obligation // - ObligationExpressionType obligation = new ObligationExpressionType(); - obligation.setFulfillOn(EffectType.PERMIT); - obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue()); - obligation.getAttributeAssignmentExpression().add(expressionType); + ObligationExpressionType obligation = onapObligation.generateObligation(); // // Now we can add it into the rule/policy/policyset // @@ -237,6 +182,8 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { ((PolicyType) ruleOrPolicy).setObligationExpressions(obligations); } else if (ruleOrPolicy instanceof PolicySetType) { ((PolicySetType) ruleOrPolicy).setObligationExpressions(obligations); + } else { + LOGGER.error("Unsupported class for adding obligation {}", ruleOrPolicy.getClass()); } // // Return as a convenience @@ -300,7 +247,6 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { valueZero.setDataType(XACML3.ID_DATATYPE_INTEGER.stringValue()); valueZero.getContent().add("0"); // Yes really - represent as a string - ObjectFactory factory = new ObjectFactory(); applyBagSize.getExpression().add(factory.createAttributeDesignator(designator)); ApplyType applyGreaterThan = new ApplyType(); diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdCombinedPolicyResultsTranslator.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdCombinedPolicyResultsTranslator.java index be0a507e..bcd594fb 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdCombinedPolicyResultsTranslator.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdCombinedPolicyResultsTranslator.java @@ -23,9 +23,14 @@ package org.onap.policy.pdp.xacml.application.common.std; import com.att.research.xacml.api.DataTypeException; +import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.Obligation; import com.att.research.xacml.api.Request; import com.att.research.xacml.api.XACML3; import com.att.research.xacml.std.annotations.RequestParser; +import com.google.common.base.Strings; +import java.util.Collection; +import java.util.Map; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; @@ -35,7 +40,9 @@ import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType; import org.onap.policy.common.utils.coder.CoderException; import org.onap.policy.common.utils.coder.StandardCoder; import org.onap.policy.models.decisions.concepts.DecisionRequest; +import org.onap.policy.models.decisions.concepts.DecisionResponse; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.pdp.xacml.application.common.OnapObligation; import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException; import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; @@ -53,10 +60,23 @@ public class StdCombinedPolicyResultsTranslator extends StdBaseTranslator { @Override public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException { // + // Sanity checks + // + if (toscaPolicy == null) { + throw new ToscaPolicyConversionException("Cannot convert a NULL policy"); + } + if (toscaPolicy.getMetadata() == null) { + throw new ToscaPolicyConversionException("Cannot convert a policy with missing metadata section"); + } + // + // Get the policy Id + // + String policyId = toscaPolicy.getMetadata().get(POLICY_ID); + // // Set it as the policy ID // PolicyType newPolicyType = new PolicyType(); - newPolicyType.setPolicyId(toscaPolicy.getMetadata().get(POLICY_ID)); + newPolicyType.setPolicyId(policyId); // // Optional description // @@ -72,8 +92,7 @@ public class StdCombinedPolicyResultsTranslator extends StdBaseTranslator { // // Generate the TargetType // - TargetType target = this.generateTargetType(toscaPolicy.getMetadata().get(POLICY_ID), - toscaPolicy.getType(), toscaPolicy.getVersion()); + TargetType target = this.generateTargetType(policyId, toscaPolicy.getType(), toscaPolicy.getVersion()); newPolicyType.setTarget(target); // // Now create the Permit Rule @@ -82,7 +101,7 @@ public class StdCombinedPolicyResultsTranslator extends StdBaseTranslator { // RuleType rule = new RuleType(); rule.setDescription("Default is to PERMIT if the policy matches."); - rule.setRuleId(toscaPolicy.getMetadata().get(POLICY_ID) + ":rule"); + rule.setRuleId(policyId + ":rule"); rule.setEffect(EffectType.PERMIT); rule.setTarget(new TargetType()); // @@ -95,7 +114,7 @@ public class StdCombinedPolicyResultsTranslator extends StdBaseTranslator { } catch (CoderException e) { throw new ToscaPolicyConversionException(e); } - addObligation(rule, jsonPolicy); + addObligation(rule, policyId, jsonPolicy, null, toscaPolicy.getType()); // // Add the rule to the policy // @@ -120,6 +139,60 @@ public class StdCombinedPolicyResultsTranslator extends StdBaseTranslator { return null; } + /** + * scanObligations - scans the list of obligations and make appropriate method calls to process + * obligations. + * + * @param obligations Collection of obligation objects + * @param decisionResponse DecisionResponse object used to store any results from obligations. + */ + @Override + protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) { + for (Obligation obligation : obligations) { + Identifier obligationId = obligation.getId(); + LOGGER.info("Obligation: {}", obligationId); + if (ToscaDictionary.ID_OBLIGATION_REST_BODY.equals(obligationId)) { + scanContentObligation(obligation, decisionResponse); + } + } + } + + /** + * scanContentObligation - scans the specific obligation for policy-id and policy-content. + * + * @param obligation Obligation incoming obligation object + * @param decisionResponse DecisionResponse object + */ + protected void scanContentObligation(Obligation obligation, DecisionResponse decisionResponse) { + // + // Create our OnapObligation which will scan for attributes + // + OnapObligation onapObligation = new OnapObligation(obligation); + // + // Get the attributes we care about + // + String policyId = onapObligation.getPolicyId(); + Map<String, Object> policyContent = onapObligation.getPolicyContentAsMap(); + // + // Sanity check that we got the attributes we care about. NOTE: This translator + // ensures that these are set when convertPolicy is called. + // + if (! Strings.isNullOrEmpty(policyId) && policyContent != null) { + decisionResponse.getPolicies().put(policyId, policyContent); + } else { + LOGGER.error("Missing obligation policyId {} or policyContent {}", policyId, + policyContent == null ? "null" : policyContent.size()); + } + } + + /** + * generateTargetType - Generates a TargetType object for the policy-id and policy-type. + * + * @param policyId String policy-id + * @param policyType String policy type + * @param policyTypeVersion String policy type version + * @return TargetType object + */ protected TargetType generateTargetType(String policyId, String policyType, String policyTypeVersion) { // // Create all the match's that are possible diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java index 66770e91..addb0df3 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchableTranslator.java @@ -23,6 +23,7 @@ package org.onap.policy.pdp.xacml.application.common.std; import com.att.research.xacml.api.Identifier; +import com.att.research.xacml.api.Obligation; import com.att.research.xacml.api.Request; import com.att.research.xacml.api.XACML3; import com.att.research.xacml.std.IdentifierImpl; @@ -38,6 +39,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -48,17 +51,20 @@ import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType; +import org.apache.commons.lang3.tuple.Pair; import org.onap.policy.common.endpoints.parameters.RestServerParameters; import org.onap.policy.common.utils.coder.CoderException; import org.onap.policy.common.utils.coder.StandardCoder; import org.onap.policy.common.utils.coder.StandardYamlCoder; import org.onap.policy.models.decisions.concepts.DecisionRequest; +import org.onap.policy.models.decisions.concepts.DecisionResponse; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType; import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier; import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty; import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate; import org.onap.policy.models.tosca.simple.concepts.JpaToscaServiceTemplate; +import org.onap.policy.pdp.xacml.application.common.OnapObligation; import org.onap.policy.pdp.xacml.application.common.PolicyApiCaller; import org.onap.policy.pdp.xacml.application.common.PolicyApiException; import org.onap.policy.pdp.xacml.application.common.ToscaDictionary; @@ -104,6 +110,127 @@ public class StdMatchableTranslator extends StdBaseTranslator { return null; } + /** + * scanObligations - scans the list of obligations and make appropriate method calls to process + * obligations. + * + * @param obligations Collection of obligation objects + * @param decisionResponse DecisionResponse object used to store any results from obligations. + */ + @Override + protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) { + // + // Implementing a crude "closest match" on the results, which means we will strip out + // any policies that has the lower weight than any of the others. + // + // Most likely these are "default" policies with a weight of zero, but not always. + // + // It is possible to have multiple policies with an equal weight, that is desired. + // + // So we need to track each policy type separately and the weights for each policy. + // + // policy-type -> weight -> List({policy-id, policy-content}, {policy-id, policy-content}) + // + Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches = new LinkedHashMap<>(); + // + // Now scan the list of obligations + // + for (Obligation obligation : obligations) { + Identifier obligationId = obligation.getId(); + LOGGER.info("Obligation: {}", obligationId); + if (ToscaDictionary.ID_OBLIGATION_REST_BODY.equals(obligationId)) { + scanClosestMatchObligation(closestMatches, obligation); + } else { + LOGGER.warn("Unsupported Obligation Id {}", obligation.getId()); + } + } + // + // Now add all the policies to the DecisionResponse + // + closestMatches.forEach((thePolicyType, weightMap) -> + weightMap.forEach((weight, policies) -> + policies.forEach(policy -> { + LOGGER.info("Policy {}", policy); + decisionResponse.getPolicies().put(policy.getLeft(), policy.getRight()); + }) + ) + ); + } + + /** + * scanClosestMatchObligation - scans for the obligation specifically holding policy + * contents and their details. + * + * @param closestMatches Map holding the current set of highest weight policy types + * @param Obligation Obligation object + */ + protected void scanClosestMatchObligation( + Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches, Obligation obligation) { + // + // Create our OnapObligation object + // + OnapObligation onapObligation = new OnapObligation(obligation); + // + // All 4 *should* be there + // + if (onapObligation.getPolicyId() == null || onapObligation.getPolicyContent() == null + || onapObligation.getPolicyType() == null || onapObligation.getWeight() == null) { + LOGGER.error("Missing an expected attribute in obligation."); + return; + } + // + // Save the values + // + String policyId = onapObligation.getPolicyId(); + String policyType = onapObligation.getPolicyType(); + Map<String, Object> policyContent = onapObligation.getPolicyContentAsMap(); + int policyWeight = onapObligation.getWeight(); + // + // If the Policy Type exists, get the weight map. + // + Map<Integer, List<Pair<String, Map<String, Object>>>> weightMap = closestMatches.get(policyType); + if (weightMap != null) { + // + // Only need to check first one - as we will ensure there is only one weight + // + Entry<Integer, List<Pair<String, Map<String, Object>>>> firstEntry = + weightMap.entrySet().iterator().next(); + if (policyWeight < firstEntry.getKey()) { + // + // Existing policies have a greater weight, so we will not add it + // + LOGGER.info("{} is lesser weight {} than current policies, will not return it", policyWeight, + firstEntry.getKey()); + } else if (firstEntry.getKey().equals(policyWeight)) { + // + // Same weight - we will add it + // + LOGGER.info("Same weight {}, adding policy", policyWeight); + firstEntry.getValue().add(Pair.of(policyId, policyContent)); + } else { + // + // The weight is greater, so we need to remove the other policies + // and point to this one. + // + LOGGER.info("New policy has greater weight {}, replacing {}", policyWeight, firstEntry.getKey()); + List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>(); + listPolicies.add(Pair.of(policyId, policyContent)); + weightMap.clear(); + weightMap.put(policyWeight, listPolicies); + } + } else { + // + // Create a new entry + // + LOGGER.info("New entry {} weight {}", policyType, policyWeight); + List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>(); + listPolicies.add(Pair.of(policyId, policyContent)); + Map<Integer, List<Pair<String, Map<String, Object>>>> newWeightMap = new LinkedHashMap<>(); + newWeightMap.put(policyWeight, listPolicies); + closestMatches.put(policyType, newWeightMap); + } + } + @Override public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException { // @@ -143,7 +270,8 @@ public class StdMatchableTranslator extends StdBaseTranslator { // Generate the TargetType - the policy should not be evaluated // unless all the matchable properties it cares about are matched. // - newPolicyType.setTarget(generateTargetType(toscaPolicy.getProperties(), toscaPolicyTypes)); + Pair<TargetType, Integer> pairGenerated = generateTargetType(toscaPolicy.getProperties(), toscaPolicyTypes); + newPolicyType.setTarget(pairGenerated.getLeft()); // // Now represent the policy as Json // @@ -157,7 +285,7 @@ public class StdMatchableTranslator extends StdBaseTranslator { // // Add it as an obligation // - addObligation(newPolicyType, jsonPolicy); + addObligation(newPolicyType, policyName, jsonPolicy, pairGenerated.getRight(), toscaPolicy.getType()); // // Now create the Permit Rule. // @@ -196,24 +324,28 @@ public class StdMatchableTranslator extends StdBaseTranslator { * * @param properties Properties section of policy * @param policyTypes Collection of policy Type to find matchable metadata - * @return TargetType object + * @return {@code Pair<TargetType, Integer>} Returns a TargetType and a Total Weight of matchables. */ - protected TargetType generateTargetType(Map<String, Object> properties, Collection<ToscaPolicyType> policyTypes) { + protected Pair<TargetType, Integer> generateTargetType(Map<String, Object> properties, + Collection<ToscaPolicyType> policyTypes) { TargetType targetType = new TargetType(); // // Iterate the properties // + int totalWeight = 0; for (Entry<String, Object> entrySet : properties.entrySet()) { // // Find matchable properties // if (isMatchable(entrySet.getKey(), policyTypes)) { LOGGER.info("Found matchable property {}", entrySet.getKey()); - generateMatchable(targetType, entrySet.getKey(), entrySet.getValue()); + int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue()); + LOGGER.info("Weight is {}", weight); + totalWeight += weight; } } - - return targetType; + LOGGER.info("Total weight is {}", totalWeight); + return Pair.of(targetType, totalWeight); } /** @@ -259,29 +391,33 @@ public class StdMatchableTranslator extends StdBaseTranslator { /** * generateMatchable - Given the object, generates list of MatchType objects and add them - * to the TargetType object. + * to the TargetType object. Returns a weight which is the number of AnyOf's generated. The + * weight can be used to further filter the results for "closest match". * * @param targetType TargetType object to add matches to * @param key Property key * @param value Object is the value - which can be a Collection or single Object - * @return TargetType incoming TargetType returned as a convenience + * @return int Weight of the match. */ @SuppressWarnings("unchecked") - protected TargetType generateMatchable(TargetType targetType, String key, Object value) { + protected int generateMatchable(TargetType targetType, String key, Object value) { + int weight = 0; if (value instanceof Collection) { AnyOfType anyOf = generateMatches((Collection<Object>) value, new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); if (! anyOf.getAllOf().isEmpty()) { targetType.getAnyOf().add(anyOf); + weight = 1; } } else { AnyOfType anyOf = generateMatches(Arrays.asList(value), new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); if (! anyOf.getAllOf().isEmpty()) { targetType.getAnyOf().add(anyOf); + weight = 1; } } - return targetType; + return weight; } /** |